Improve terminal performance

This commit is contained in:
Mike Cifelli 2017-03-20 11:18:36 -04:00
parent cef7be6c25
commit 38710e21bf
4 changed files with 210 additions and 188 deletions

View File

@ -3,11 +3,11 @@ package terminal;
import static terminal.ControlSequenceHandler.Command.SGR; import static terminal.ControlSequenceHandler.Command.SGR;
class ControlSequenceHandler { class ControlSequenceHandler {
public static final boolean isEscape(char c) { public static final boolean isEscape(char c) {
return c == '\u001B'; return c == '\u001B';
} }
private boolean inControlSequence; private boolean inControlSequence;
private int code; private int code;
private Command command; private Command command;
@ -18,7 +18,7 @@ class ControlSequenceHandler {
this.code = 0; this.code = 0;
this.command = SGR; this.command = SGR;
} }
public static enum Command { public static enum Command {
SGR SGR
} }

View File

@ -6,21 +6,24 @@ import static util.Characters.EOF;
import java.io.*; import java.io.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import com.googlecode.lanterna.TerminalPosition; import com.googlecode.lanterna.*;
import com.googlecode.lanterna.input.*; import com.googlecode.lanterna.input.*;
import com.googlecode.lanterna.terminal.IOSafeTerminal; import com.googlecode.lanterna.terminal.*;
import terminal.SafePipedStream.*;
public class LispTerminal { public class LispTerminal {
public static final char END_OF_SEGMENT = 'x'; public static final char END_OF_SEGMENT = 'x';
private IOSafeTerminal terminal; private IOSafeTerminal terminal;
private PipedOutputStream inputWriter; private SafePipedOutputStream inputWriter;
private PipedInputStream outputReader; private SafePipedInputStream outputReader;
private ExecutorService executorService; private ExecutorService executorService;
private ControlSequenceHandler controlSequenceHandler; private ControlSequenceHandler controlSequenceHandler;
private TerminalPosition origin; private TerminalSize terminalSize;
private TerminalPosition leadingEdge; private int originColumn;
private int originRow;
private String inputLine; private String inputLine;
private String outputSegment; private String outputSegment;
private boolean isFinished; private boolean isFinished;
@ -28,15 +31,32 @@ public class LispTerminal {
public LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) { public LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) {
// FIXME - add resize handler // FIXME - add resize handler
this.terminal = terminal; this.terminal = terminal;
this.inputWriter = inputWriter; this.inputWriter = new SafePipedOutputStream(inputWriter);
this.outputReader = outputReader; this.outputReader = new SafePipedInputStream(outputReader);
this.executorService = Executors.newFixedThreadPool(2); this.executorService = Executors.newFixedThreadPool(2);
this.controlSequenceHandler = new ControlSequenceHandler(); this.controlSequenceHandler = new ControlSequenceHandler();
this.origin = terminal.getCursorPosition(); this.terminalSize = terminal.getTerminalSize();
this.leadingEdge = origin; this.originColumn = terminal.getCursorPosition().getColumn();
this.originRow = terminal.getCursorPosition().getRow();
this.inputLine = ""; this.inputLine = "";
this.outputSegment = ""; this.outputSegment = "";
this.isFinished = false; this.isFinished = false;
addResizeListener();
}
private void addResizeListener() {
terminal.addResizeListener(new TerminalResizeListener() {
@Override
public void onResized(Terminal terminal, TerminalSize newSize) {
resize();
}
});
}
private void resize() {
terminalSize = terminal.getTerminalSize();
} }
public void run() { public void run() {
@ -51,8 +71,12 @@ public class LispTerminal {
} }
private void processNextKey() { private void processNextKey() {
handleKey(getKeyStroke()); KeyStroke keyStroke = getKeyStroke();
takeNap();
if (keyStroke != null)
handleKey(keyStroke);
else
takeNap();
} }
private KeyStroke getKeyStroke() { private KeyStroke getKeyStroke() {
@ -61,8 +85,9 @@ public class LispTerminal {
try { try {
keyStroke = terminal.pollInput(); keyStroke = terminal.pollInput();
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
// TODO - Issue #299 // Issue #299
terminal.putCharacter('\n'); terminal.putCharacter('\n');
terminal.close();
System.exit(0); System.exit(0);
} }
@ -70,9 +95,6 @@ public class LispTerminal {
} }
private synchronized void handleKey(KeyStroke keyStroke) { private synchronized void handleKey(KeyStroke keyStroke) {
if (keyStroke == null)
return;
doKey(keyStroke); doKey(keyStroke);
terminal.flush(); terminal.flush();
} }
@ -121,22 +143,31 @@ public class LispTerminal {
} }
private synchronized boolean isPossibleToMoveLeft(TerminalPosition cursorPosition) { private synchronized boolean isPossibleToMoveLeft(TerminalPosition cursorPosition) {
return distanceFromOrigin(cursorPosition) > 0; return getDistanceFromOrigin(cursorPosition) > 0;
} }
private synchronized int distanceFromOrigin(TerminalPosition cursorPosition) { private synchronized int getDistanceFromOrigin(TerminalPosition cursorPosition) {
int columnDifference = cursorPosition.getColumn() - origin.getColumn(); int columnDifference = cursorPosition.getColumn() - originColumn;
int rowDifference = cursorPosition.getRow() - origin.getRow(); int rowDifference = cursorPosition.getRow() - originRow;
int totalColumns = terminal.getTerminalSize().getColumns(); int totalColumns = terminalSize.getColumns();
return columnDifference + (totalColumns * rowDifference); 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) { private synchronized void retractCursor(TerminalPosition cursorPosition) {
TerminalPosition newPosition = cursorPosition.withRelativeColumn(-1); TerminalPosition newPosition = cursorPosition.withRelativeColumn(-1);
if (isAtStartOfRow(cursorPosition)) if (isAtStartOfRow(cursorPosition))
newPosition = cursorPosition.withColumn(terminal.getTerminalSize().getColumns()).withRelativeRow(-1); newPosition = cursorPosition.withColumn(terminalSize.getColumns()).withRelativeRow(-1);
terminal.setCursorPosition(newPosition); terminal.setCursorPosition(newPosition);
@ -154,7 +185,7 @@ public class LispTerminal {
} }
private synchronized boolean isPossibleToMoveRight(TerminalPosition cursorPosition) { private synchronized boolean isPossibleToMoveRight(TerminalPosition cursorPosition) {
return distanceFromOrigin(cursorPosition) < inputLine.length(); return getDistanceFromOrigin(cursorPosition) < inputLine.length();
} }
private synchronized void advanceCursor(TerminalPosition cursorPosition) { private synchronized void advanceCursor(TerminalPosition cursorPosition) {
@ -162,16 +193,10 @@ public class LispTerminal {
moveCursorToNextRow(cursorPosition); moveCursorToNextRow(cursorPosition);
else else
terminal.setCursorPosition(cursorPosition.withRelativeColumn(1)); terminal.setCursorPosition(cursorPosition.withRelativeColumn(1));
TerminalPosition newPosition = terminal.getCursorPosition();
// FIXME - use distance from origin to set leading edge
if (distanceFromOrigin(newPosition) == inputLine.length())
leadingEdge = terminal.getCursorPosition();
} }
private synchronized boolean isAtEndOfRow(TerminalPosition cursorPosition) { private synchronized boolean isAtEndOfRow(TerminalPosition cursorPosition) {
return cursorPosition.getColumn() >= terminal.getTerminalSize().getColumns() - 1; return cursorPosition.getColumn() >= terminalSize.getColumns() - 1;
} }
private void moveCursorToNextRow(TerminalPosition cursorPosition) { private void moveCursorToNextRow(TerminalPosition cursorPosition) {
@ -182,40 +207,42 @@ public class LispTerminal {
} }
private boolean isEndOfBuffer() { private boolean isEndOfBuffer() {
return terminal.getCursorPosition().getRow() == terminal.getTerminalSize().getRows() - 1; return terminal.getCursorPosition().getRow() == terminalSize.getRows() - 1;
} }
private void createNewRowForCursor() { private void createNewRowForCursor() {
TerminalPosition originalPosition = terminal.getCursorPosition(); TerminalPosition originalPosition = terminal.getCursorPosition();
terminal.putCharacter('\n'); terminal.putCharacter('\n');
terminal.setCursorPosition(originalPosition.withColumn(0)); terminal.setCursorPosition(originalPosition.withColumn(0));
origin = origin.withRelativeRow(-1); --originRow;
} }
private synchronized void doEnter() { private synchronized void doEnter() {
moveCursorToEndOfInput(); moveCursorToEndOfInput();
terminal.putCharacter('\n'); terminal.putCharacter('\n');
inputLine += "\n"; inputLine += "\n";
inputWriter.write(inputLine.getBytes());
try { inputWriter.flush();
inputWriter.write(inputLine.getBytes());
inputWriter.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
inputLine = ""; inputLine = "";
origin = terminal.getCursorPosition(); updateOrigin();
leadingEdge = origin; }
private synchronized void moveCursorToEndOfInput() {
terminal.setCursorPosition(getLeadingEdge());
}
private void updateOrigin() {
TerminalPosition cursorPosition = terminal.getCursorPosition();
originColumn = cursorPosition.getColumn();
originRow = cursorPosition.getRow();
} }
private synchronized void doBackspace() { private synchronized void doBackspace() {
TerminalPosition cursorPosition = terminal.getCursorPosition(); TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveLeft(cursorPosition)) { if (isPossibleToMoveLeft(cursorPosition)) {
String remaining = inputLine.substring(distanceFromOrigin(cursorPosition), inputLine.length()); String remaining = inputLine.substring(getDistanceFromOrigin(cursorPosition), inputLine.length());
inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition) - 1) + remaining; inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition) - 1) + remaining;
retractCursor(cursorPosition); retractCursor(cursorPosition);
@ -225,10 +252,6 @@ public class LispTerminal {
terminal.putCharacter(' '); terminal.putCharacter(' ');
retractCursor(cursorPosition); retractCursor(cursorPosition);
// FIXME - use distance from origin to set leading edge
if (distanceFromOrigin(leadingEdge) > inputLine.length())
leadingEdge = terminal.getCursorPosition();
} }
} }
@ -236,18 +259,14 @@ public class LispTerminal {
TerminalPosition cursorPosition = terminal.getCursorPosition(); TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveRight(cursorPosition)) { if (isPossibleToMoveRight(cursorPosition)) {
String remaining = inputLine.substring(distanceFromOrigin(cursorPosition) + 1, inputLine.length()); String remaining = inputLine.substring(getDistanceFromOrigin(cursorPosition) + 1, inputLine.length());
inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition)) + remaining; inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition)) + remaining;
for (char c : remaining.toCharArray()) for (char c : remaining.toCharArray())
terminal.putCharacter(c); terminal.putCharacter(c);
terminal.putCharacter(' '); terminal.putCharacter(' ');
terminal.setCursorPosition(cursorPosition); terminal.setCursorPosition(cursorPosition);
// FIXME - use distance from origin to set leading edge
if (distanceFromOrigin(leadingEdge) > inputLine.length())
leadingEdge = terminal.getCursorPosition();
} }
} }
@ -256,8 +275,8 @@ public class LispTerminal {
if (isPossibleToMoveRight(cursorPosition)) { if (isPossibleToMoveRight(cursorPosition)) {
String remaining = keyStroke.getCharacter() String remaining = keyStroke.getCharacter()
+ inputLine.substring(distanceFromOrigin(cursorPosition), inputLine.length()); + inputLine.substring(getDistanceFromOrigin(cursorPosition), inputLine.length());
inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition)) + remaining; inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition)) + remaining;
// FIXME - must have a way to push remainder on to a new line at the end of the buffer // FIXME - must have a way to push remainder on to a new line at the end of the buffer
@ -282,13 +301,8 @@ public class LispTerminal {
} }
public void writeOutput() { public void writeOutput() {
try { for (int c = outputReader.read(); c != EOF; c = outputReader.read())
for (int c = outputReader.read(); c != EOF; c = outputReader.read()) processOutput((char) c);
processOutput((char) c);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
terminal.setCursorVisible(true); terminal.setCursorVisible(true);
terminal.flush(); terminal.flush();
@ -321,12 +335,7 @@ public class LispTerminal {
terminal.flush(); terminal.flush();
terminal.setCursorVisible(true); terminal.setCursorVisible(true);
outputSegment = ""; outputSegment = "";
origin = terminal.getCursorPosition(); updateOrigin();
leadingEdge = origin;
}
private synchronized void moveCursorToEndOfInput() {
terminal.setCursorPosition(leadingEdge);
} }
private synchronized void moveCursorToNextRowIfNecessary() { private synchronized void moveCursorToNextRowIfNecessary() {
@ -338,13 +347,7 @@ public class LispTerminal {
public void finish() { public void finish() {
isFinished = true; isFinished = true;
inputWriter.close();
try {
inputWriter.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
} }

View File

@ -0,0 +1,81 @@
package terminal;
import java.io.*;
public interface SafePipedStream {
public static class SafePipedInputStream {
private PipedInputStream underlyingStream;
public SafePipedInputStream(PipedInputStream underlyingStream) {
this.underlyingStream = underlyingStream;
}
public int read() {
try {
return underlyingStream.read();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void close() {
try {
underlyingStream.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
public static class SafePipedOutputStream {
private PipedOutputStream underlyingStream;
public SafePipedOutputStream(PipedOutputStream underlyingStream) {
this.underlyingStream = underlyingStream;
}
public void write(byte[] b) {
try {
underlyingStream.write(b);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void flush() {
try {
underlyingStream.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void close() {
try {
underlyingStream.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
public static class UncheckedIOException extends RuntimeException {
private static final long serialVersionUID = 1L;
private IOException exception;
public UncheckedIOException(IOException exception) {
this.exception = exception;
}
@Override
public String getMessage() {
return exception.getMessage();
}
}
}

View File

@ -26,6 +26,13 @@ public class LispTerminalTest {
waitForFlushes(1); waitForFlushes(1);
} }
private void pressKeyTimes(KeyType keyType, int times) {
for (int i = times; i > 0; i--)
virtualTerminal.addInput(new KeyStroke(keyType));
waitForFlushes(times);
}
private void pressControlKey(KeyType keyType) { private void pressControlKey(KeyType keyType) {
virtualTerminal.addInput(new KeyStroke(keyType, true, false)); virtualTerminal.addInput(new KeyStroke(keyType, true, false));
waitForFlushes(1); waitForFlushes(1);
@ -56,7 +63,7 @@ public class LispTerminalTest {
outputWriter.write(END_OF_SEGMENT); outputWriter.write(END_OF_SEGMENT);
outputWriter.flush(); outputWriter.flush();
waitForFlushes(1); waitForFlushes(1);
} catch (IOException e) {} } catch (IOException ignored) {}
} }
private void waitForFlushes(int flushCount) { private void waitForFlushes(int flushCount) {
@ -67,7 +74,7 @@ public class LispTerminalTest {
flushListener.resetFlushCount(); flushListener.resetFlushCount();
} }
} catch (InterruptedException e) {} } catch (InterruptedException ignored) {}
} }
private void setColumns(int columns) { private void setColumns(int columns) {
@ -85,7 +92,7 @@ public class LispTerminalTest {
private void assertCharacterAtPosition(char character, int column, int row) { private void assertCharacterAtPosition(char character, int column, int row) {
TerminalPosition position = new TerminalPosition(column, row); TerminalPosition position = new TerminalPosition(column, row);
assertEquals(character, virtualTerminal.getCharacter(position).getCharacter()); assertEquals(String.valueOf(character), String.valueOf(virtualTerminal.getCharacter(position).getCharacter()));
} }
private void assertInputWritten(String expected) { private void assertInputWritten(String expected) {
@ -97,7 +104,7 @@ public class LispTerminalTest {
for (int c = inputReader.read(); c != -1; c = inputReader.read()) { for (int c = inputReader.read(); c != -1; c = inputReader.read()) {
actual += (char) c; actual += (char) c;
} }
} catch (IOException e) {} } catch (IOException ignored) {}
assertEquals(expected, actual); assertEquals(expected, actual);
} }
@ -106,7 +113,13 @@ public class LispTerminalTest {
try { try {
inputWriter.write(0); inputWriter.write(0);
fail("input stream not closed"); fail("input stream not closed");
} catch (IOException e) {} } 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 @Before
@ -153,8 +166,7 @@ public class LispTerminalTest {
setColumns(5); setColumns(5);
enterCharacters("123451"); enterCharacters("123451");
assertCursorPosition(1, 1); assertCursorPosition(1, 1);
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 2);
pressKey(KeyType.ArrowLeft);
assertCursorPosition(4, 0); assertCursorPosition(4, 0);
} }
@ -181,13 +193,9 @@ public class LispTerminalTest {
setColumns(5); setColumns(5);
enterCharacters("123451"); enterCharacters("123451");
assertCursorPosition(1, 1); assertCursorPosition(1, 1);
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 3);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
assertCursorPosition(3, 0); assertCursorPosition(3, 0);
pressKey(KeyType.ArrowRight); pressKeyTimes(KeyType.ArrowRight, 3);
pressKey(KeyType.ArrowRight);
pressKey(KeyType.ArrowRight);
assertCursorPosition(1, 1); assertCursorPosition(1, 1);
} }
@ -201,8 +209,7 @@ public class LispTerminalTest {
@Test @Test
public void characterIsInserted() { public void characterIsInserted() {
enterCharacters("abcd"); enterCharacters("abcd");
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 2);
pressKey(KeyType.ArrowLeft);
enterCharacter('x'); enterCharacter('x');
assertCharacterAtPosition('a', 0, 0); assertCharacterAtPosition('a', 0, 0);
assertCharacterAtPosition('b', 1, 0); assertCharacterAtPosition('b', 1, 0);
@ -215,8 +222,7 @@ public class LispTerminalTest {
public void characterIsInserted_PushesInputToNextRow() { public void characterIsInserted_PushesInputToNextRow() {
setColumns(4); setColumns(4);
enterCharacters("abcd"); enterCharacters("abcd");
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 2);
pressKey(KeyType.ArrowLeft);
enterCharacter('x'); enterCharacter('x');
assertCharacterAtPosition('a', 0, 0); assertCharacterAtPosition('a', 0, 0);
assertCharacterAtPosition('b', 1, 0); assertCharacterAtPosition('b', 1, 0);
@ -234,8 +240,7 @@ public class LispTerminalTest {
@Test @Test
public void backspaceWorksAfterInput() { public void backspaceWorksAfterInput() {
enterCharacters("12345"); enterCharacters("12345");
pressKey(KeyType.Backspace); pressKeyTimes(KeyType.Backspace, 2);
pressKey(KeyType.Backspace);
assertCursorPosition(3, 0); assertCursorPosition(3, 0);
assertCharacterAtPosition('1', 0, 0); assertCharacterAtPosition('1', 0, 0);
assertCharacterAtPosition('2', 1, 0); assertCharacterAtPosition('2', 1, 0);
@ -249,11 +254,7 @@ public class LispTerminalTest {
public void backspaceWorksAcrossRow() { public void backspaceWorksAcrossRow() {
setColumns(4); setColumns(4);
enterCharacters("1234567"); enterCharacters("1234567");
pressKey(KeyType.Backspace); pressKeyTimes(KeyType.Backspace, 5);
pressKey(KeyType.Backspace);
pressKey(KeyType.Backspace);
pressKey(KeyType.Backspace);
pressKey(KeyType.Backspace);
assertCursorPosition(2, 0); assertCursorPosition(2, 0);
assertCharacterAtPosition('1', 0, 0); assertCharacterAtPosition('1', 0, 0);
assertCharacterAtPosition('2', 1, 0); assertCharacterAtPosition('2', 1, 0);
@ -267,8 +268,7 @@ public class LispTerminalTest {
@Test @Test
public void backspaceWorksInMiddleOfInput() { public void backspaceWorksInMiddleOfInput() {
enterCharacters("12345"); enterCharacters("12345");
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 2);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.Backspace); pressKey(KeyType.Backspace);
assertCursorPosition(2, 0); assertCursorPosition(2, 0);
assertCharacterAtPosition('1', 0, 0); assertCharacterAtPosition('1', 0, 0);
@ -296,12 +296,8 @@ public class LispTerminalTest {
@Test @Test
public void deleteWorksAtStartOfInput() { public void deleteWorksAtStartOfInput() {
enterCharacters("del"); enterCharacters("del");
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 3);
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.Delete, 3);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.Delete);
pressKey(KeyType.Delete);
pressKey(KeyType.Delete);
assertCursorPosition(0, 0); assertCursorPosition(0, 0);
assertCharacterAtPosition(' ', 0, 0); assertCharacterAtPosition(' ', 0, 0);
assertCharacterAtPosition(' ', 1, 0); assertCharacterAtPosition(' ', 1, 0);
@ -312,11 +308,7 @@ public class LispTerminalTest {
public void deleteWorksAcrossRow() { public void deleteWorksAcrossRow() {
setColumns(4); setColumns(4);
enterCharacters("delete"); enterCharacters("delete");
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 5);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.Delete); pressKey(KeyType.Delete);
assertCursorPosition(1, 0); assertCursorPosition(1, 0);
assertCharacterAtPosition('d', 0, 0); assertCharacterAtPosition('d', 0, 0);
@ -343,8 +335,7 @@ public class LispTerminalTest {
@Test @Test
public void enterPressedInMiddleOfInput_WritesEntireLineToPipedStream() { public void enterPressedInMiddleOfInput_WritesEntireLineToPipedStream() {
enterCharacters("enter"); enterCharacters("enter");
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 2);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.Enter); pressKey(KeyType.Enter);
assertInputWritten("enter\n"); assertInputWritten("enter\n");
} }
@ -360,8 +351,7 @@ public class LispTerminalTest {
@Test @Test
public void controlDWorksInMiddleOfInput() { public void controlDWorksInMiddleOfInput() {
enterCharacters("control-d"); enterCharacters("control-d");
pressKey(KeyType.ArrowLeft); pressKeyTimes(KeyType.ArrowLeft, 2);
pressKey(KeyType.ArrowLeft);
enterControlCharacter('d'); enterControlCharacter('d');
assertInputStreamClosed(); assertInputStreamClosed();
assertInputWritten("control-d\n"); assertInputWritten("control-d\n");
@ -392,21 +382,14 @@ public class LispTerminalTest {
public void outputIsWritten() { public void outputIsWritten() {
produceOutput("output"); produceOutput("output");
assertCursorPosition(6, 0); assertCursorPosition(6, 0);
assertCharacterAtPosition('o', 0, 0); assertCharacterPositions(new char[][] { { 'o', 'u', 't', 'p', 'u', 't' } });
assertCharacterAtPosition('u', 1, 0);
assertCharacterAtPosition('t', 2, 0);
assertCharacterAtPosition('p', 3, 0);
assertCharacterAtPosition('u', 4, 0);
assertCharacterAtPosition('t', 5, 0);
} }
@Test @Test
public void endOfSegmentCharacterIsNotPrinted() { public void endOfSegmentCharacterIsNotPrinted() {
produceOutput("> " + END_OF_SEGMENT); produceOutput("> " + END_OF_SEGMENT);
assertCursorPosition(2, 0); assertCursorPosition(2, 0);
assertCharacterAtPosition('>', 0, 0); assertCharacterPositions(new char[][] { { '>', ' ', ' ' } });
assertCharacterAtPosition(' ', 1, 0);
assertCharacterAtPosition(' ', 2, 0);
} }
@Test @Test
@ -415,29 +398,18 @@ public class LispTerminalTest {
setRows(2); setRows(2);
enterCharacters("01201201"); enterCharacters("01201201");
assertCursorPosition(2, 1); assertCursorPosition(2, 1);
assertCharacterAtPosition('0', 0, 0); assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', ' ' } });
assertCharacterAtPosition('1', 1, 0);
assertCharacterAtPosition('2', 2, 0);
assertCharacterAtPosition('0', 0, 1);
assertCharacterAtPosition('1', 1, 1);
} }
// @Test // @Test
// public void insertingTextPushesInputPastEndOfBuffer() { // public void insertingTextPushesInputPastEndOfBuffer() {
// setColumns(2); // setColumns(3);
// setRows(3); // setRows(3);
// enterCharacters("00112"); // enterCharacters("00011122");
// // pressKeyTimes(KeyType.ArrowLeft, 4);
// pressKey(KeyType.ArrowLeft);
// pressKey(KeyType.ArrowLeft);
// pressKey(KeyType.ArrowLeft);
// enterCharacters("zz"); // enterCharacters("zz");
// assertCursorPosition(0, 1); // assertCursorPosition(0, 1);
// assertCharacterAtPosition('z', 0, 0); // assertCharacterPositions(new char[][] { { '1', 'z', 'z' }, { '1', '1', '2' }, { '2', ' ', ' ' } });
// assertCharacterAtPosition('z', 1, 0);
// assertCharacterAtPosition('1', 0, 1);
// assertCharacterAtPosition('1', 1, 1);
// assertCharacterAtPosition('2', 0, 2);
// } // }
@Test @Test
@ -453,64 +425,30 @@ public class LispTerminalTest {
setRows(2); setRows(2);
produceOutput("output"); produceOutput("output");
assertCursorPosition(0, 1); assertCursorPosition(0, 1);
assertCharacterAtPosition('p', 0, 0); assertCharacterPositions(new char[][] { { 'p', 'u', 't' }, { ' ', ' ', ' ' } });
assertCharacterAtPosition('u', 1, 0);
assertCharacterAtPosition('t', 2, 0);
assertCharacterAtPosition(' ', 0, 1);
assertCharacterAtPosition(' ', 1, 1);
assertCharacterAtPosition(' ', 2, 1);
} }
@Test @Test
public void printedOutputDoesNotOverwriteInput() { public void printedOutputDoesNotOverwriteInput() {
setColumns(3); setColumns(3);
enterCharacters("01201201"); enterCharacters("01201201");
pressKeyTimes(KeyType.ArrowLeft, 5);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
produceOutput("out"); produceOutput("out");
assertCursorPosition(0, 4); assertCursorPosition(0, 4);
assertCharacterAtPosition('0', 0, 0); assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', '2' }, { '0', '1', 'o' },
assertCharacterAtPosition('1', 1, 0); { 'u', 't', ' ' } });
assertCharacterAtPosition('2', 2, 0);
assertCharacterAtPosition('0', 0, 1);
assertCharacterAtPosition('1', 1, 1);
assertCharacterAtPosition('2', 2, 1);
assertCharacterAtPosition('0', 0, 2);
assertCharacterAtPosition('1', 1, 2);
assertCharacterAtPosition('o', 2, 2);
assertCharacterAtPosition('u', 0, 3);
assertCharacterAtPosition('t', 1, 3);
assertCharacterAtPosition(' ', 2, 3);
} }
@Test @Test
public void printedOutputDoesNotOverwriteInput_AfterEnter() { public void printedOutputDoesNotOverwriteInput_AfterEnter() {
setColumns(3); setColumns(3);
enterCharacters("01201201"); enterCharacters("01201201");
pressKeyTimes(KeyType.ArrowLeft, 5);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.ArrowLeft);
pressKey(KeyType.Enter); pressKey(KeyType.Enter);
produceOutput("out"); produceOutput("out");
assertCursorPosition(0, 4); assertCursorPosition(0, 4);
assertCharacterAtPosition('0', 0, 0); assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', '2' }, { '0', '1', ' ' },
assertCharacterAtPosition('1', 1, 0); { 'o', 'u', 't' } });
assertCharacterAtPosition('2', 2, 0);
assertCharacterAtPosition('0', 0, 1);
assertCharacterAtPosition('1', 1, 1);
assertCharacterAtPosition('2', 2, 1);
assertCharacterAtPosition('0', 0, 2);
assertCharacterAtPosition('1', 1, 2);
assertCharacterAtPosition(' ', 2, 2);
assertCharacterAtPosition('o', 0, 3);
assertCharacterAtPosition('u', 1, 3);
assertCharacterAtPosition('t', 2, 3);
} }
} }