Fix text insertion at the end of the buffer

This commit is contained in:
Mike Cifelli 2017-03-20 16:20:28 -04:00
parent 38710e21bf
commit fc96894d14
3 changed files with 158 additions and 113 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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() {