Add a crude interactive terminal implementation
This commit is contained in:
parent
462673ba64
commit
31ca72e534
|
@ -9,24 +9,26 @@ public class InteractiveLispInterpreter extends LispInterpreter {
|
||||||
protected void printGreeting() {
|
protected void printGreeting() {
|
||||||
environment.getOutput().println(GREETING);
|
environment.getOutput().println(GREETING);
|
||||||
environment.getOutput().println();
|
environment.getOutput().println();
|
||||||
|
environment.getOutput().flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void displayPrompt() {
|
protected void displayPrompt() {
|
||||||
environment.getOutput().print(PROMPT);
|
environment.getOutput().print(environment.decorateOutput(PROMPT));
|
||||||
|
environment.getOutput().flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void erasePrompt() {
|
protected void printValueOfNextSExpression() {
|
||||||
for (int i = 0; i < PROMPT.length(); i++) {
|
environment.getOutput().println();
|
||||||
environment.getOutput().print("\b");
|
super.printValueOfNextSExpression();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void printFarewell() {
|
protected void printFarewell() {
|
||||||
environment.getOutput().println();
|
environment.getOutput().println();
|
||||||
environment.getOutput().println();
|
environment.getOutput().println();
|
||||||
|
environment.getOutput().close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,11 +34,10 @@ public abstract class LispInterpreter {
|
||||||
|
|
||||||
protected void displayPrompt() {}
|
protected void displayPrompt() {}
|
||||||
|
|
||||||
private void printValueOfNextSExpression() {
|
protected void printValueOfNextSExpression() {
|
||||||
try {
|
try {
|
||||||
printValueOfNextSExpressionWithException();
|
printValueOfNextSExpressionWithException();
|
||||||
} catch (LispException e) {
|
} catch (LispException e) {
|
||||||
erasePrompt();
|
|
||||||
environment.getErrorManager().handle(e);
|
environment.getErrorManager().handle(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,12 +46,10 @@ public abstract class LispInterpreter {
|
||||||
SExpression sExpression = parser.getNextSExpression();
|
SExpression sExpression = parser.getNextSExpression();
|
||||||
String result = environment.decorateValueOutput(String.valueOf(eval(sExpression)));
|
String result = environment.decorateValueOutput(String.valueOf(eval(sExpression)));
|
||||||
|
|
||||||
erasePrompt();
|
|
||||||
environment.getOutput().println(result);
|
environment.getOutput().println(result);
|
||||||
|
environment.getOutput().flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void erasePrompt() {}
|
|
||||||
|
|
||||||
protected void printFarewell() {
|
protected void printFarewell() {
|
||||||
environment.getOutput().println();
|
environment.getOutput().println();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import interpreter.*;
|
import interpreter.*;
|
||||||
|
@ -12,38 +13,96 @@ public class LispMain {
|
||||||
private static final String ANSI_GREEN = "\u001B[32m";
|
private static final String ANSI_GREEN = "\u001B[32m";
|
||||||
private static final String ANSI_YELLOW = "\u001B[33m";
|
private static final String ANSI_YELLOW = "\u001B[33m";
|
||||||
private static final String ANSI_PURPLE = "\u001B[35m";
|
private static final String ANSI_PURPLE = "\u001B[35m";
|
||||||
|
private PipedInputStream inputReader;
|
||||||
|
private PipedOutputStream inputWriter;
|
||||||
|
private PipedInputStream outputReader;
|
||||||
|
private PipedOutputStream outputWriter;
|
||||||
|
private LispTerminal terminal;
|
||||||
|
|
||||||
private LispMain() {}
|
private LispMain() throws IOException {
|
||||||
|
inputReader = new PipedInputStream();
|
||||||
public static void main(String[] args) {
|
inputWriter = new PipedOutputStream(inputReader);
|
||||||
LispTerminal terminal = new LispTerminal();
|
outputReader = new PipedInputStream();
|
||||||
|
outputWriter = new PipedOutputStream(outputReader);
|
||||||
terminal.run();
|
terminal = new LispTerminal(inputWriter, outputReader);
|
||||||
|
|
||||||
// LispInterpreter interpreter = buildInterpreter(args);
|
|
||||||
// interpreter.interpret();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LispInterpreter buildInterpreter(String[] args) {
|
public static void main(String[] args) throws IOException {
|
||||||
|
LispMain main = new LispMain();
|
||||||
|
|
||||||
|
main.run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run(String[] args) {
|
||||||
|
if (args.length == 0)
|
||||||
|
terminal.run();
|
||||||
|
|
||||||
|
LispInterpreter interpreter = buildInterpreter(args);
|
||||||
|
interpreter.interpret();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LispInterpreter buildInterpreter(String[] args) {
|
||||||
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
|
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
|
||||||
configureInput(args, builder);
|
configureInput(args, builder);
|
||||||
builder.setOutput(System.out);
|
configureOutput(args, builder);
|
||||||
builder.setErrorOutput(System.err);
|
configureTerminatingFunctions(args, builder);
|
||||||
builder.setTerminationFunction(() -> System.exit(0));
|
configureDecorators(args, builder);
|
||||||
builder.setErrorTerminationFunction(() -> System.exit(1));
|
|
||||||
builder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN));
|
|
||||||
builder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW));
|
|
||||||
builder.setErrorOutputDecorator(makeColorDecorator(ANSI_RED));
|
|
||||||
builder.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE));
|
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void configureInput(String[] args, LispInterpreterBuilder builder) {
|
private void configureTerminatingFunctions(String[] args, LispInterpreterBuilder builder) {
|
||||||
|
if (args.length > 0) {
|
||||||
|
builder.setTerminationFunction(() -> System.exit(0));
|
||||||
|
builder.setErrorTerminationFunction(() -> System.exit(1));
|
||||||
|
} else {
|
||||||
|
builder.setTerminationFunction(this::shutdown);
|
||||||
|
builder.setErrorTerminationFunction(this::shutdown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureDecorators(String[] args, LispInterpreterBuilder builder) {
|
||||||
|
if (args.length > 0) {
|
||||||
|
builder.setOutputDecorator(makeColorDecorator(ANSI_GREEN));
|
||||||
|
builder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN));
|
||||||
|
builder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW));
|
||||||
|
builder.setErrorOutputDecorator(makeColorDecorator(ANSI_RED));
|
||||||
|
builder.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE));
|
||||||
|
} else {
|
||||||
|
builder.setOutputDecorator(makeInteractiveDecorator(ANSI_GREEN));
|
||||||
|
builder.setValueOutputDecorator(makeInteractiveDecorator(ANSI_GREEN));
|
||||||
|
builder.setWarningOutputDecorator(makeInteractiveDecorator(ANSI_YELLOW));
|
||||||
|
builder.setErrorOutputDecorator(makeInteractiveDecorator(ANSI_RED));
|
||||||
|
builder.setCriticalOutputDecorator(makeInteractiveDecorator(ANSI_PURPLE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdown() {
|
||||||
|
try {
|
||||||
|
terminal.finish();
|
||||||
|
outputWriter.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureOutput(String[] args, LispInterpreterBuilder builder) {
|
||||||
|
if (args.length > 0) {
|
||||||
|
builder.setOutput(System.out);
|
||||||
|
builder.setErrorOutput(System.err);
|
||||||
|
} else {
|
||||||
|
PrintStream outputStream = new PrintStream(outputWriter);
|
||||||
|
builder.setOutput(outputStream);
|
||||||
|
builder.setErrorOutput(outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureInput(String[] args, LispInterpreterBuilder builder) {
|
||||||
if (args.length > 0)
|
if (args.length > 0)
|
||||||
builder.useFile(args[0]);
|
builder.useFile(args[0]);
|
||||||
else
|
else
|
||||||
builder.setInput(System.in, "stdin");
|
builder.setInput(inputReader, "stdin");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Function<String, String> makeColorDecorator(String color) {
|
private static Function<String, String> makeColorDecorator(String color) {
|
||||||
|
@ -56,4 +115,14 @@ public class LispMain {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Function<String, String> makeInteractiveDecorator(String color) {
|
||||||
|
return new Function<String, String>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apply(String s) {
|
||||||
|
return s + 'x';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,34 @@
|
||||||
package terminal;
|
package terminal;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
import com.googlecode.lanterna.TerminalPosition;
|
import com.googlecode.lanterna.TerminalPosition;
|
||||||
import com.googlecode.lanterna.input.*;
|
import com.googlecode.lanterna.input.*;
|
||||||
import com.googlecode.lanterna.terminal.*;
|
import com.googlecode.lanterna.terminal.*;
|
||||||
|
|
||||||
public class LispTerminal implements Runnable {
|
public class LispTerminal {
|
||||||
|
|
||||||
|
// private static final String ANSI_RESET = "001B[0m";
|
||||||
|
// private static final String ANSI_RED = "\u001B[31m";
|
||||||
|
// private static final String ANSI_GREEN = "\u001B[32m";
|
||||||
|
// private static final String ANSI_YELLOW = "\u001B[33m";
|
||||||
|
// private static final String ANSI_PURPLE = "\u001B[35m";
|
||||||
|
|
||||||
private IOSafeTerminal terminal;
|
private IOSafeTerminal terminal;
|
||||||
private boolean isFinished;
|
private boolean isFinished;
|
||||||
private String currentLine;
|
private String currentLine;
|
||||||
private TerminalPosition origin;
|
private TerminalPosition origin;
|
||||||
|
private PipedOutputStream inputWriter;
|
||||||
|
private PipedInputStream outputReader;
|
||||||
|
private ExecutorService executor;
|
||||||
|
|
||||||
public LispTerminal() {
|
LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||||
|
this.terminal = terminal;
|
||||||
|
initialize(inputWriter, outputReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LispTerminal(PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||||
try {
|
try {
|
||||||
Terminal unsafe = new DefaultTerminalFactory().createTerminal();
|
Terminal unsafe = new DefaultTerminalFactory().createTerminal();
|
||||||
this.terminal = IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter(unsafe);
|
this.terminal = IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter(unsafe);
|
||||||
|
@ -22,15 +37,52 @@ public class LispTerminal implements Runnable {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialize(inputWriter, outputReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize(PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||||
this.isFinished = false;
|
this.isFinished = false;
|
||||||
this.currentLine = "";
|
this.currentLine = "";
|
||||||
this.origin = terminal.getCursorPosition();
|
this.origin = terminal.getCursorPosition();
|
||||||
|
this.inputWriter = inputWriter;
|
||||||
|
this.outputReader = outputReader;
|
||||||
|
this.executor = Executors.newFixedThreadPool(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
|
executor.execute(this::readInput);
|
||||||
|
executor.execute(this::writeOutput);
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readInput() {
|
||||||
while (!isFinished) {
|
while (!isFinished) {
|
||||||
handleKeyStroke(getKeyStroke());
|
handleKeyStroke(getKeyStroke());
|
||||||
terminal.flush();
|
terminal.flush();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
isFinished = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeOutput() {
|
||||||
|
try {
|
||||||
|
for (int c = outputReader.read(); c != -1; c = outputReader.read()) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (c == 'x') {
|
||||||
|
terminal.flush();
|
||||||
|
origin = terminal.getCursorPosition();
|
||||||
|
} else {
|
||||||
|
terminal.putCharacter((char) c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.close();
|
terminal.close();
|
||||||
|
@ -40,23 +92,24 @@ public class LispTerminal implements Runnable {
|
||||||
KeyStroke keyStroke = null;
|
KeyStroke keyStroke = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
keyStroke = terminal.readInput();
|
keyStroke = terminal.pollInput();
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
// TODO - lantera is trying to exit private mode on ctrl-c (which we didn't enter)
|
terminal.putCharacter('\n');
|
||||||
terminal.putCharacter('`');
|
System.exit(0);
|
||||||
isFinished = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyStroke;
|
return keyStroke;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleKeyStroke(KeyStroke keyStroke) {
|
private synchronized void handleKeyStroke(KeyStroke keyStroke) {
|
||||||
if (keyStroke == null)
|
if (keyStroke == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
KeyType keyType = keyStroke.getKeyType();
|
KeyType keyType = keyStroke.getKeyType();
|
||||||
|
|
||||||
if (keyType == KeyType.ArrowLeft)
|
if (keyStroke.isCtrlDown())
|
||||||
|
doControlKeyCharacter(keyStroke);
|
||||||
|
else if (keyType == KeyType.ArrowLeft)
|
||||||
moveCursorLeft();
|
moveCursorLeft();
|
||||||
else if (keyType == KeyType.ArrowRight)
|
else if (keyType == KeyType.ArrowRight)
|
||||||
moveCursorRight();
|
moveCursorRight();
|
||||||
|
@ -70,13 +123,33 @@ public class LispTerminal implements Runnable {
|
||||||
doCharacter(keyStroke);
|
doCharacter(keyStroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doEnter() {
|
private synchronized void doControlKeyCharacter(KeyStroke keyStroke) {
|
||||||
|
KeyType keyType = keyStroke.getKeyType();
|
||||||
|
|
||||||
|
if (keyType == KeyType.Character)
|
||||||
|
if (keyStroke.getCharacter() == 'd') {
|
||||||
|
doEnter();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void doEnter() {
|
||||||
terminal.putCharacter('\n');
|
terminal.putCharacter('\n');
|
||||||
|
currentLine += "\n";
|
||||||
|
|
||||||
|
try {
|
||||||
|
inputWriter.write(currentLine.getBytes());
|
||||||
|
inputWriter.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
currentLine = "";
|
currentLine = "";
|
||||||
origin = terminal.getCursorPosition();
|
origin = terminal.getCursorPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moveCursorLeft() {
|
private synchronized void moveCursorLeft() {
|
||||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||||
|
|
||||||
if (isPossibleToMoveLeft(cursorPosition))
|
if (isPossibleToMoveLeft(cursorPosition))
|
||||||
|
@ -86,38 +159,43 @@ public class LispTerminal implements Runnable {
|
||||||
terminal.setCursorPosition(cursorPosition.withRelativeColumn(-1));
|
terminal.setCursorPosition(cursorPosition.withRelativeColumn(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPossibleToMoveLeft(TerminalPosition cursorPosition) {
|
private synchronized boolean isPossibleToMoveLeft(TerminalPosition cursorPosition) {
|
||||||
return distanceFromOrigin(cursorPosition) > 0;
|
return distanceFromOrigin(cursorPosition) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int distanceFromOrigin(TerminalPosition cursorPosition) {
|
private synchronized int distanceFromOrigin(TerminalPosition cursorPosition) {
|
||||||
return cursorPosition.getColumn()
|
int cursorColumn = cursorPosition.getColumn();
|
||||||
+ (terminal.getTerminalSize().getColumns() * (cursorPosition.getRow() - origin.getRow()));
|
int cursorRow = cursorPosition.getRow();
|
||||||
|
int originColumn = origin.getColumn();
|
||||||
|
int originRow = origin.getRow();
|
||||||
|
int totalColumns = terminal.getTerminalSize().getColumns();
|
||||||
|
|
||||||
|
return cursorColumn - originColumn + (totalColumns * (cursorRow - originRow));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moveCursorRight() {
|
private synchronized void moveCursorRight() {
|
||||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||||
|
|
||||||
if (isPossibleToMoveRight(cursorPosition))
|
if (isPossibleToMoveRight(cursorPosition))
|
||||||
advanceCursor(cursorPosition);
|
advanceCursor(cursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void advanceCursor(TerminalPosition cursorPosition) {
|
private synchronized void advanceCursor(TerminalPosition cursorPosition) {
|
||||||
if (isCursorAtEndOfRow(cursorPosition))
|
if (isCursorAtEndOfRow(cursorPosition))
|
||||||
terminal.setCursorPosition(0, cursorPosition.getRow() + 1);
|
terminal.setCursorPosition(0, cursorPosition.getRow() + 1);
|
||||||
else
|
else
|
||||||
terminal.setCursorPosition(cursorPosition.withRelativeColumn(1));
|
terminal.setCursorPosition(cursorPosition.withRelativeColumn(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCursorAtEndOfRow(TerminalPosition cursorPosition) {
|
private synchronized boolean isCursorAtEndOfRow(TerminalPosition cursorPosition) {
|
||||||
return cursorPosition.getColumn() == terminal.getTerminalSize().getColumns() - 1;
|
return cursorPosition.getColumn() == terminal.getTerminalSize().getColumns() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPossibleToMoveRight(TerminalPosition cursorPosition) {
|
private synchronized boolean isPossibleToMoveRight(TerminalPosition cursorPosition) {
|
||||||
return distanceFromOrigin(cursorPosition) < currentLine.length();
|
return distanceFromOrigin(cursorPosition) < currentLine.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doBackspace() {
|
private synchronized void doBackspace() {
|
||||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||||
|
|
||||||
if (isPossibleToMoveLeft(cursorPosition)) {
|
if (isPossibleToMoveLeft(cursorPosition)) {
|
||||||
|
@ -141,7 +219,7 @@ public class LispTerminal implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doDelete() {
|
private synchronized void doDelete() {
|
||||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||||
|
|
||||||
if (isPossibleToMoveRight(cursorPosition)) {
|
if (isPossibleToMoveRight(cursorPosition)) {
|
||||||
|
@ -156,7 +234,7 @@ public class LispTerminal implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doCharacter(KeyStroke keyStroke) {
|
private synchronized void doCharacter(KeyStroke keyStroke) {
|
||||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||||
|
|
||||||
if (isPossibleToMoveRight(cursorPosition)) {
|
if (isPossibleToMoveRight(cursorPosition)) {
|
||||||
|
@ -176,4 +254,15 @@ public class LispTerminal implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void finish() {
|
||||||
|
isFinished = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
inputWriter.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
package terminal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.*;
|
||||||
|
import com.googlecode.lanterna.input.*;
|
||||||
|
import com.googlecode.lanterna.terminal.virtual.*;
|
||||||
|
|
||||||
|
public class LispTerminalAttempt {
|
||||||
|
|
||||||
|
private PipedInputStream inputReader;
|
||||||
|
private PipedOutputStream inputWriter;
|
||||||
|
private PipedInputStream outputReader;
|
||||||
|
private PipedOutputStream outputWriter;
|
||||||
|
private VirtualTerminal virtualTerminal;
|
||||||
|
private LispTerminal lispTerminal;
|
||||||
|
|
||||||
|
private void pressKey(KeyType keyType) {
|
||||||
|
virtualTerminal.addInput(new KeyStroke(keyType));
|
||||||
|
sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enterCharacter(char character) {
|
||||||
|
virtualTerminal.addInput(new KeyStroke(character, false, false));
|
||||||
|
sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enterCharacters(String characters) {
|
||||||
|
for (char c : characters.toCharArray())
|
||||||
|
virtualTerminal.addInput(new KeyStroke(c, false, false));
|
||||||
|
|
||||||
|
sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleep() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(15);
|
||||||
|
} catch (InterruptedException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCursorPosition(int column, int row) {
|
||||||
|
assertTrue(virtualTerminal.getCursorBufferPosition().getColumn() == column);
|
||||||
|
assertTrue(virtualTerminal.getCursorBufferPosition().getRow() == row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCharacterAtPosition(char character, int column, int row) {
|
||||||
|
TerminalPosition position = new TerminalPosition(column, row);
|
||||||
|
assertTrue(virtualTerminal.getBufferCharacter(position).getCharacter() == character);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setColumns(int columns) {
|
||||||
|
virtualTerminal.setTerminalSize(new TerminalSize(columns, virtualTerminal.getTerminalSize().getRows()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws IOException {
|
||||||
|
inputReader = new PipedInputStream();
|
||||||
|
inputWriter = new PipedOutputStream(inputReader);
|
||||||
|
outputReader = new PipedInputStream();
|
||||||
|
outputWriter = new PipedOutputStream(outputReader);
|
||||||
|
virtualTerminal = new DefaultVirtualTerminal();
|
||||||
|
lispTerminal = new LispTerminal(virtualTerminal, inputWriter, outputReader);
|
||||||
|
lispTerminal.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws IOException {
|
||||||
|
lispTerminal.finish();
|
||||||
|
outputWriter.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void leftArrowDoesNotMovePastOrigin() {
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
assertCursorPosition(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void leftArrowWorksAfterEnteringCharacters() {
|
||||||
|
enterCharacters("abc");
|
||||||
|
assertCursorPosition(3, 0);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
assertCursorPosition(2, 0);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
assertCursorPosition(1, 0);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
assertCursorPosition(0, 0);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
assertCursorPosition(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void leftArrowWorksAcrossRows() {
|
||||||
|
setColumns(5);
|
||||||
|
enterCharacters("123451");
|
||||||
|
assertCursorPosition(1, 1);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
assertCursorPosition(4, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rightArrowDoesNotMovePastEndOfInput() {
|
||||||
|
pressKey(KeyType.ArrowRight);
|
||||||
|
assertCursorPosition(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rightArrowWorksAfterMovingLeft() {
|
||||||
|
enterCharacters("12");
|
||||||
|
assertCursorPosition(2, 0);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
assertCursorPosition(1, 0);
|
||||||
|
pressKey(KeyType.ArrowRight);
|
||||||
|
assertCursorPosition(2, 0);
|
||||||
|
pressKey(KeyType.ArrowRight);
|
||||||
|
assertCursorPosition(2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rightArrowWorksAcrossRow() {
|
||||||
|
setColumns(5);
|
||||||
|
enterCharacters("123451");
|
||||||
|
assertCursorPosition(1, 1);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
assertCursorPosition(3, 0);
|
||||||
|
pressKey(KeyType.ArrowRight);
|
||||||
|
pressKey(KeyType.ArrowRight);
|
||||||
|
pressKey(KeyType.ArrowRight);
|
||||||
|
assertCursorPosition(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void characterKeyIsEchoed() {
|
||||||
|
enterCharacter('a');
|
||||||
|
assertCursorPosition(1, 0);
|
||||||
|
assertCharacterAtPosition('a', 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void characterIsInserted() {
|
||||||
|
enterCharacters("abcd");
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
enterCharacter('x');
|
||||||
|
assertCharacterAtPosition('a', 0, 0);
|
||||||
|
assertCharacterAtPosition('b', 1, 0);
|
||||||
|
assertCharacterAtPosition('x', 2, 0);
|
||||||
|
assertCharacterAtPosition('c', 3, 0);
|
||||||
|
assertCharacterAtPosition('d', 4, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void characterIsInserted_PushesInputToNextRow() {
|
||||||
|
setColumns(4);
|
||||||
|
enterCharacters("abcd");
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
enterCharacter('x');
|
||||||
|
assertCharacterAtPosition('a', 0, 0);
|
||||||
|
assertCharacterAtPosition('b', 1, 0);
|
||||||
|
assertCharacterAtPosition('x', 2, 0);
|
||||||
|
assertCharacterAtPosition('c', 3, 0);
|
||||||
|
assertCharacterAtPosition('d', 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void backspaceDoesNothingAtOrigin() {
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
assertCursorPosition(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void backspaceWorksAfterInput() {
|
||||||
|
enterCharacters("12345");
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
assertCursorPosition(3, 0);
|
||||||
|
assertCharacterAtPosition('1', 0, 0);
|
||||||
|
assertCharacterAtPosition('2', 1, 0);
|
||||||
|
assertCharacterAtPosition('3', 2, 0);
|
||||||
|
assertCharacterAtPosition(' ', 3, 0);
|
||||||
|
assertCharacterAtPosition(' ', 4, 0);
|
||||||
|
assertCharacterAtPosition(' ', 5, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void backspaceWorksAcrossRow() {
|
||||||
|
setColumns(4);
|
||||||
|
enterCharacters("1234567");
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void backspaceWorksInMiddleOfInput() {
|
||||||
|
enterCharacters("12345");
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
pressKey(KeyType.ArrowLeft);
|
||||||
|
pressKey(KeyType.Backspace);
|
||||||
|
assertCursorPosition(2, 0);
|
||||||
|
assertCharacterAtPosition('1', 0, 0);
|
||||||
|
assertCharacterAtPosition('2', 1, 0);
|
||||||
|
assertCharacterAtPosition('4', 2, 0);
|
||||||
|
assertCharacterAtPosition('5', 3, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue