Improve terminal performance
This commit is contained in:
parent
cef7be6c25
commit
38710e21bf
|
@ -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,7 +71,11 @@ public class LispTerminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processNextKey() {
|
private void processNextKey() {
|
||||||
handleKey(getKeyStroke());
|
KeyStroke keyStroke = getKeyStroke();
|
||||||
|
|
||||||
|
if (keyStroke != null)
|
||||||
|
handleKey(keyStroke);
|
||||||
|
else
|
||||||
takeNap();
|
takeNap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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";
|
||||||
|
|
||||||
try {
|
|
||||||
inputWriter.write(inputLine.getBytes());
|
inputWriter.write(inputLine.getBytes());
|
||||||
inputWriter.flush();
|
inputWriter.flush();
|
||||||
} catch (IOException e) {
|
inputLine = "";
|
||||||
// TODO Auto-generated catch block
|
updateOrigin();
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputLine = "";
|
private synchronized void moveCursorToEndOfInput() {
|
||||||
origin = terminal.getCursorPosition();
|
terminal.setCursorPosition(getLeadingEdge());
|
||||||
leadingEdge = origin;
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
try {
|
|
||||||
inputWriter.close();
|
inputWriter.close();
|
||||||
} catch (IOException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue