Fix text insertion at the end of the buffer
This commit is contained in:
parent
38710e21bf
commit
fc96894d14
|
@ -13,7 +13,6 @@ class ControlSequenceHandler {
|
|||
private Command command;
|
||||
|
||||
public ControlSequenceHandler() {
|
||||
// TODO Auto-generated constructor stub
|
||||
this.inControlSequence = false;
|
||||
this.code = 0;
|
||||
this.command = SGR;
|
||||
|
|
|
@ -20,7 +20,7 @@ public class LispTerminal {
|
|||
private SafePipedOutputStream inputWriter;
|
||||
private SafePipedInputStream outputReader;
|
||||
private ExecutorService executorService;
|
||||
private ControlSequenceHandler controlSequenceHandler;
|
||||
// private ControlSequenceHandler controlSequenceHandler;
|
||||
private TerminalSize terminalSize;
|
||||
private int originColumn;
|
||||
private int originRow;
|
||||
|
@ -29,12 +29,11 @@ public class LispTerminal {
|
|||
private boolean isFinished;
|
||||
|
||||
public LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||
// FIXME - add resize handler
|
||||
this.terminal = terminal;
|
||||
this.inputWriter = new SafePipedOutputStream(inputWriter);
|
||||
this.outputReader = new SafePipedInputStream(outputReader);
|
||||
this.executorService = Executors.newFixedThreadPool(2);
|
||||
this.controlSequenceHandler = new ControlSequenceHandler();
|
||||
// this.controlSequenceHandler = new ControlSequenceHandler();
|
||||
this.terminalSize = terminal.getTerminalSize();
|
||||
this.originColumn = terminal.getCursorPosition().getColumn();
|
||||
this.originRow = terminal.getCursorPosition().getRow();
|
||||
|
@ -100,22 +99,10 @@ public class LispTerminal {
|
|||
}
|
||||
|
||||
private synchronized void doKey(KeyStroke keyStroke) {
|
||||
KeyType keyType = keyStroke.getKeyType();
|
||||
|
||||
if (keyStroke.isCtrlDown())
|
||||
doControlKey(keyStroke);
|
||||
else if (keyType == KeyType.ArrowLeft)
|
||||
moveCursorLeft();
|
||||
else if (keyType == KeyType.ArrowRight)
|
||||
moveCursorRight();
|
||||
else if (keyType == KeyType.Enter)
|
||||
doEnter();
|
||||
else if (keyType == KeyType.Backspace)
|
||||
doBackspace();
|
||||
else if (keyType == KeyType.Delete)
|
||||
doDelete();
|
||||
else if (keyType == KeyType.Character)
|
||||
doCharacter(keyStroke);
|
||||
else
|
||||
doNormalKey(keyStroke);
|
||||
}
|
||||
|
||||
private synchronized void doControlKey(KeyStroke keyStroke) {
|
||||
|
@ -135,6 +122,23 @@ public class LispTerminal {
|
|||
finish();
|
||||
}
|
||||
|
||||
private void doNormalKey(KeyStroke keyStroke) {
|
||||
KeyType keyType = keyStroke.getKeyType();
|
||||
|
||||
if (keyType == KeyType.ArrowLeft)
|
||||
moveCursorLeft();
|
||||
else if (keyType == KeyType.ArrowRight)
|
||||
moveCursorRight();
|
||||
else if (keyType == KeyType.Enter)
|
||||
doEnter();
|
||||
else if (keyType == KeyType.Backspace)
|
||||
doBackspace();
|
||||
else if (keyType == KeyType.Delete)
|
||||
doDelete();
|
||||
else if (keyType == KeyType.Character)
|
||||
doCharacter(keyStroke.getCharacter());
|
||||
}
|
||||
|
||||
private synchronized void moveCursorLeft() {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
|
||||
|
@ -154,15 +158,6 @@ public class LispTerminal {
|
|||
return columnDifference + (totalColumns * rowDifference);
|
||||
}
|
||||
|
||||
private synchronized TerminalPosition getLeadingEdge() {
|
||||
int inputLength = inputLine.length();
|
||||
int totalColumns = terminalSize.getColumns();
|
||||
int rowDifference = inputLength / totalColumns;
|
||||
int columnDifference = inputLength % totalColumns;
|
||||
|
||||
return new TerminalPosition(originColumn + columnDifference, originRow + rowDifference);
|
||||
}
|
||||
|
||||
private synchronized void retractCursor(TerminalPosition cursorPosition) {
|
||||
TerminalPosition newPosition = cursorPosition.withRelativeColumn(-1);
|
||||
|
||||
|
@ -189,31 +184,31 @@ public class LispTerminal {
|
|||
}
|
||||
|
||||
private synchronized void advanceCursor(TerminalPosition cursorPosition) {
|
||||
if (isAtEndOfRow(cursorPosition))
|
||||
if (isEndOfRow(cursorPosition))
|
||||
moveCursorToNextRow(cursorPosition);
|
||||
else
|
||||
terminal.setCursorPosition(cursorPosition.withRelativeColumn(1));
|
||||
}
|
||||
|
||||
private synchronized boolean isAtEndOfRow(TerminalPosition cursorPosition) {
|
||||
private synchronized boolean isEndOfRow(TerminalPosition cursorPosition) {
|
||||
return cursorPosition.getColumn() >= terminalSize.getColumns() - 1;
|
||||
}
|
||||
|
||||
private void moveCursorToNextRow(TerminalPosition cursorPosition) {
|
||||
if (isEndOfBuffer())
|
||||
createNewRowForCursor();
|
||||
private synchronized void moveCursorToNextRow(TerminalPosition cursorPosition) {
|
||||
if (isEndOfBuffer(cursorPosition))
|
||||
createNewRowForCursor(cursorPosition);
|
||||
else
|
||||
terminal.setCursorPosition(cursorPosition.withColumn(0).withRelativeRow(1));
|
||||
}
|
||||
|
||||
private boolean isEndOfBuffer() {
|
||||
return terminal.getCursorPosition().getRow() == terminalSize.getRows() - 1;
|
||||
private synchronized boolean isEndOfBuffer(TerminalPosition cursorPosition) {
|
||||
return cursorPosition.getRow() == terminalSize.getRows() - 1;
|
||||
}
|
||||
|
||||
private void createNewRowForCursor() {
|
||||
TerminalPosition originalPosition = terminal.getCursorPosition();
|
||||
private synchronized void createNewRowForCursor(TerminalPosition cursorPosition) {
|
||||
terminal.setCursorPosition(cursorPosition);
|
||||
terminal.putCharacter('\n');
|
||||
terminal.setCursorPosition(originalPosition.withColumn(0));
|
||||
terminal.setCursorPosition(cursorPosition.withColumn(0));
|
||||
--originRow;
|
||||
}
|
||||
|
||||
|
@ -231,7 +226,16 @@ public class LispTerminal {
|
|||
terminal.setCursorPosition(getLeadingEdge());
|
||||
}
|
||||
|
||||
private void updateOrigin() {
|
||||
private synchronized TerminalPosition getLeadingEdge() {
|
||||
int inputLength = inputLine.length();
|
||||
int totalColumns = terminalSize.getColumns();
|
||||
int rowDifference = inputLength / totalColumns;
|
||||
int columnDifference = inputLength % totalColumns;
|
||||
|
||||
return new TerminalPosition(originColumn + columnDifference, originRow + rowDifference);
|
||||
}
|
||||
|
||||
private synchronized void updateOrigin() {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
originColumn = cursorPosition.getColumn();
|
||||
originRow = cursorPosition.getRow();
|
||||
|
@ -270,28 +274,53 @@ public class LispTerminal {
|
|||
}
|
||||
}
|
||||
|
||||
private synchronized void doCharacter(KeyStroke keyStroke) {
|
||||
private synchronized void doCharacter(Character character) {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
|
||||
if (isPossibleToMoveRight(cursorPosition)) {
|
||||
String remaining = keyStroke.getCharacter()
|
||||
+ inputLine.substring(getDistanceFromOrigin(cursorPosition), inputLine.length());
|
||||
inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition)) + remaining;
|
||||
if (isPossibleToMoveRight(cursorPosition))
|
||||
insertCharacter(character, cursorPosition);
|
||||
else
|
||||
appendCharacter(character, cursorPosition);
|
||||
}
|
||||
|
||||
// FIXME - must have a way to push remainder on to a new line at the end of the buffer
|
||||
private synchronized void insertCharacter(Character character, TerminalPosition cursorPosition) {
|
||||
if (!isBufferFilled()) {
|
||||
int oldOriginRow = originRow;
|
||||
advanceCursor(getLeadingEdge());
|
||||
|
||||
if (originRow != oldOriginRow) {
|
||||
terminal.setCursorPosition(new TerminalPosition(originColumn, originRow));
|
||||
for (char c : inputLine.toCharArray())
|
||||
terminal.putCharacter(c);
|
||||
cursorPosition = cursorPosition.withRelativeRow(-1);
|
||||
}
|
||||
|
||||
terminal.setCursorPosition(cursorPosition);
|
||||
|
||||
int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
|
||||
String remaining = character + inputLine.substring(distanceFromOrigin, inputLine.length());
|
||||
inputLine = inputLine.substring(0, distanceFromOrigin) + remaining;
|
||||
|
||||
for (char c : remaining.toCharArray())
|
||||
terminal.putCharacter(c);
|
||||
|
||||
advanceCursor(cursorPosition);
|
||||
} else {
|
||||
terminal.putCharacter(keyStroke.getCharacter());
|
||||
inputLine += keyStroke.getCharacter();
|
||||
|
||||
advanceCursor(cursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean isBufferFilled() {
|
||||
int row = getLeadingEdge().getRow();
|
||||
int column = getLeadingEdge().getColumn();
|
||||
|
||||
return (row == terminalSize.getRows() - 1) && (column >= terminalSize.getColumns() - 1) && (originRow <= 0);
|
||||
}
|
||||
|
||||
private synchronized void appendCharacter(Character character, TerminalPosition cursorPosition) {
|
||||
terminal.putCharacter(character);
|
||||
inputLine += character;
|
||||
advanceCursor(cursorPosition);
|
||||
}
|
||||
|
||||
private void takeNap() {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
|
@ -326,6 +355,13 @@ public class LispTerminal {
|
|||
|
||||
private synchronized void printSegment() {
|
||||
terminal.setCursorVisible(false);
|
||||
printSegmentCharacters();
|
||||
terminal.setCursorVisible(true);
|
||||
outputSegment = "";
|
||||
updateOrigin();
|
||||
}
|
||||
|
||||
private synchronized void printSegmentCharacters() {
|
||||
moveCursorToEndOfInput();
|
||||
|
||||
for (char c : outputSegment.toCharArray())
|
||||
|
@ -333,15 +369,12 @@ public class LispTerminal {
|
|||
|
||||
moveCursorToNextRowIfNecessary();
|
||||
terminal.flush();
|
||||
terminal.setCursorVisible(true);
|
||||
outputSegment = "";
|
||||
updateOrigin();
|
||||
}
|
||||
|
||||
private synchronized void moveCursorToNextRowIfNecessary() {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
|
||||
if (isAtEndOfRow(cursorPosition))
|
||||
if (isEndOfRow(cursorPosition))
|
||||
moveCursorToNextRow(cursorPosition);
|
||||
}
|
||||
|
||||
|
|
|
@ -78,11 +78,13 @@ public class LispTerminalTest {
|
|||
}
|
||||
|
||||
private void setColumns(int columns) {
|
||||
virtualTerminal.setTerminalSize(new TerminalSize(columns, virtualTerminal.getTerminalSize().getRows()));
|
||||
int rows = virtualTerminal.getTerminalSize().getRows();
|
||||
virtualTerminal.setTerminalSize(new TerminalSize(columns, rows));
|
||||
}
|
||||
|
||||
private void setRows(int rows) {
|
||||
virtualTerminal.setTerminalSize(new TerminalSize(virtualTerminal.getTerminalSize().getColumns(), rows));
|
||||
int columns = virtualTerminal.getTerminalSize().getColumns();
|
||||
virtualTerminal.setTerminalSize(new TerminalSize(columns, rows));
|
||||
}
|
||||
|
||||
private void assertCursorPosition(int column, int row) {
|
||||
|
@ -92,7 +94,15 @@ public class LispTerminalTest {
|
|||
|
||||
private void assertCharacterAtPosition(char character, int column, int row) {
|
||||
TerminalPosition position = new TerminalPosition(column, row);
|
||||
assertEquals(String.valueOf(character), String.valueOf(virtualTerminal.getCharacter(position).getCharacter()));
|
||||
String expected = String.valueOf(character);
|
||||
String actual = String.valueOf(virtualTerminal.getCharacter(position).getCharacter());
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private void assertCharacterPositions(char[][] positions) {
|
||||
for (int row = 0; row < positions.length; row++)
|
||||
for (int column = 0; column < positions[row].length; column++)
|
||||
assertCharacterAtPosition(positions[row][column], column, row);
|
||||
}
|
||||
|
||||
private void assertInputWritten(String expected) {
|
||||
|
@ -101,9 +111,8 @@ public class LispTerminalTest {
|
|||
try {
|
||||
inputWriter.close();
|
||||
|
||||
for (int c = inputReader.read(); c != -1; c = inputReader.read()) {
|
||||
for (int c = inputReader.read(); c != -1; c = inputReader.read())
|
||||
actual += (char) c;
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
|
||||
assertEquals(expected, actual);
|
||||
|
@ -116,12 +125,6 @@ public class LispTerminalTest {
|
|||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
private void assertCharacterPositions(char[][] positions) {
|
||||
for (int row = 0; row < positions.length; row++)
|
||||
for (int column = 0; column < positions[row].length; column++)
|
||||
assertCharacterAtPosition(positions[row][column], column, row);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
inputReader = new PipedInputStream();
|
||||
|
@ -211,11 +214,7 @@ public class LispTerminalTest {
|
|||
enterCharacters("abcd");
|
||||
pressKeyTimes(KeyType.ArrowLeft, 2);
|
||||
enterCharacter('x');
|
||||
assertCharacterAtPosition('a', 0, 0);
|
||||
assertCharacterAtPosition('b', 1, 0);
|
||||
assertCharacterAtPosition('x', 2, 0);
|
||||
assertCharacterAtPosition('c', 3, 0);
|
||||
assertCharacterAtPosition('d', 4, 0);
|
||||
assertCharacterPositions(new char[][] { { 'a', 'b', 'x', 'c', 'd' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -224,11 +223,7 @@ public class LispTerminalTest {
|
|||
enterCharacters("abcd");
|
||||
pressKeyTimes(KeyType.ArrowLeft, 2);
|
||||
enterCharacter('x');
|
||||
assertCharacterAtPosition('a', 0, 0);
|
||||
assertCharacterAtPosition('b', 1, 0);
|
||||
assertCharacterAtPosition('x', 2, 0);
|
||||
assertCharacterAtPosition('c', 3, 0);
|
||||
assertCharacterAtPosition('d', 0, 1);
|
||||
assertCharacterPositions(new char[][] { { 'a', 'b', 'x', 'c' }, { 'd', ' ', ' ', ' ' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -242,12 +237,7 @@ public class LispTerminalTest {
|
|||
enterCharacters("12345");
|
||||
pressKeyTimes(KeyType.Backspace, 2);
|
||||
assertCursorPosition(3, 0);
|
||||
assertCharacterAtPosition('1', 0, 0);
|
||||
assertCharacterAtPosition('2', 1, 0);
|
||||
assertCharacterAtPosition('3', 2, 0);
|
||||
assertCharacterAtPosition(' ', 3, 0);
|
||||
assertCharacterAtPosition(' ', 4, 0);
|
||||
assertCharacterAtPosition(' ', 5, 0);
|
||||
assertCharacterPositions(new char[][] { { '1', '2', '3', ' ', ' ', ' ' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -256,13 +246,7 @@ public class LispTerminalTest {
|
|||
enterCharacters("1234567");
|
||||
pressKeyTimes(KeyType.Backspace, 5);
|
||||
assertCursorPosition(2, 0);
|
||||
assertCharacterAtPosition('1', 0, 0);
|
||||
assertCharacterAtPosition('2', 1, 0);
|
||||
assertCharacterAtPosition(' ', 2, 0);
|
||||
assertCharacterAtPosition(' ', 3, 0);
|
||||
assertCharacterAtPosition(' ', 0, 1);
|
||||
assertCharacterAtPosition(' ', 1, 1);
|
||||
assertCharacterAtPosition(' ', 2, 1);
|
||||
assertCharacterPositions(new char[][] { { '1', '2', ' ', ' ' }, { ' ', ' ', ' ', ' ' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -271,10 +255,7 @@ public class LispTerminalTest {
|
|||
pressKeyTimes(KeyType.ArrowLeft, 2);
|
||||
pressKey(KeyType.Backspace);
|
||||
assertCursorPosition(2, 0);
|
||||
assertCharacterAtPosition('1', 0, 0);
|
||||
assertCharacterAtPosition('2', 1, 0);
|
||||
assertCharacterAtPosition('4', 2, 0);
|
||||
assertCharacterAtPosition('5', 3, 0);
|
||||
assertCharacterPositions(new char[][] { { '1', '2', '4', '5' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -288,9 +269,7 @@ public class LispTerminalTest {
|
|||
enterCharacters("del");
|
||||
pressKey(KeyType.Delete);
|
||||
assertCursorPosition(3, 0);
|
||||
assertCharacterAtPosition('d', 0, 0);
|
||||
assertCharacterAtPosition('e', 1, 0);
|
||||
assertCharacterAtPosition('l', 2, 0);
|
||||
assertCharacterPositions(new char[][] { { 'd', 'e', 'l' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -299,9 +278,7 @@ public class LispTerminalTest {
|
|||
pressKeyTimes(KeyType.ArrowLeft, 3);
|
||||
pressKeyTimes(KeyType.Delete, 3);
|
||||
assertCursorPosition(0, 0);
|
||||
assertCharacterAtPosition(' ', 0, 0);
|
||||
assertCharacterAtPosition(' ', 1, 0);
|
||||
assertCharacterAtPosition(' ', 2, 0);
|
||||
assertCharacterPositions(new char[][] { { ' ', ' ', ' ' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -311,12 +288,7 @@ public class LispTerminalTest {
|
|||
pressKeyTimes(KeyType.ArrowLeft, 5);
|
||||
pressKey(KeyType.Delete);
|
||||
assertCursorPosition(1, 0);
|
||||
assertCharacterAtPosition('d', 0, 0);
|
||||
assertCharacterAtPosition('l', 1, 0);
|
||||
assertCharacterAtPosition('e', 2, 0);
|
||||
assertCharacterAtPosition('t', 3, 0);
|
||||
assertCharacterAtPosition('e', 0, 1);
|
||||
assertCharacterAtPosition(' ', 1, 1);
|
||||
assertCharacterPositions(new char[][] { { 'd', 'l', 'e', 't' }, { 'e', ' ' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -340,6 +312,32 @@ public class LispTerminalTest {
|
|||
assertInputWritten("enter\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterAfterInsertedText_WritesLineToPipedStream() {
|
||||
enterCharacters("enter");
|
||||
pressKeyTimes(KeyType.ArrowLeft, 2);
|
||||
enterCharacters("||");
|
||||
pressKey(KeyType.Enter);
|
||||
assertInputWritten("ent||er\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterAfterBackspace_WritesLineToPipedStream() {
|
||||
enterCharacters("enter");
|
||||
pressKeyTimes(KeyType.Backspace, 2);
|
||||
pressKey(KeyType.Enter);
|
||||
assertInputWritten("ent\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterAfterDelete_WritesLineToPipedStream() {
|
||||
enterCharacters("enter");
|
||||
pressKeyTimes(KeyType.ArrowLeft, 2);
|
||||
pressKeyTimes(KeyType.Delete, 2);
|
||||
pressKey(KeyType.Enter);
|
||||
assertInputWritten("ent\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controlDWorks() {
|
||||
enterCharacters("control-d");
|
||||
|
@ -401,16 +399,31 @@ public class LispTerminalTest {
|
|||
assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', ' ' } });
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void insertingTextPushesInputPastEndOfBuffer() {
|
||||
// setColumns(3);
|
||||
// setRows(3);
|
||||
// enterCharacters("00011122");
|
||||
// pressKeyTimes(KeyType.ArrowLeft, 4);
|
||||
// enterCharacters("zz");
|
||||
// assertCursorPosition(0, 1);
|
||||
// assertCharacterPositions(new char[][] { { '1', 'z', 'z' }, { '1', '1', '2' }, { '2', ' ', ' ' } });
|
||||
// }
|
||||
@Test
|
||||
public void insertingTextPushesInputPastEndOfBuffer() {
|
||||
setColumns(3);
|
||||
setRows(4);
|
||||
pressKey(KeyType.Enter);
|
||||
enterCharacters("00011122");
|
||||
pressKeyTimes(KeyType.ArrowLeft, 4);
|
||||
assertCursorPosition(1, 2);
|
||||
enterCharacters("zz");
|
||||
assertCursorPosition(0, 2);
|
||||
assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', 'z', 'z' }, { '1', '1', '2' },
|
||||
{ '2', ' ', ' ' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertingTextDoesNothingWhenBufferFilled() {
|
||||
setColumns(3);
|
||||
setRows(3);
|
||||
enterCharacters("00011122");
|
||||
pressKeyTimes(KeyType.ArrowLeft, 4);
|
||||
assertCursorPosition(1, 1);
|
||||
enterCharacters("zz");
|
||||
assertCursorPosition(1, 1);
|
||||
assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', '1', '1' }, { '2', '2', ' ' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printedOutputToEndOfRow_MovesCursorToNextRow() {
|
||||
|
|
Loading…
Reference in New Issue