Fix terminal resize issues
This commit is contained in:
parent
a8eff1ad70
commit
5cb6212d2a
|
@ -2,4 +2,10 @@ package interpreter;
|
|||
|
||||
public class FileLispInterpreter extends LispInterpreter {
|
||||
|
||||
@Override
|
||||
protected void afterInterpreting() {
|
||||
environment.getOutput().println();
|
||||
environment.getOutput().close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@ public class InteractiveLispInterpreter extends LispInterpreter {
|
|||
private static final String PROMPT = "~ ";
|
||||
|
||||
@Override
|
||||
protected void printGreeting() {
|
||||
protected void beforeInterpreting() {
|
||||
environment.getOutput().println(GREETING);
|
||||
environment.getOutput().println();
|
||||
environment.getOutput().flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void displayPrompt() {
|
||||
protected void prompt() {
|
||||
environment.getOutput().print(environment.decorateOutput(PROMPT));
|
||||
environment.getOutput().flush();
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class InteractiveLispInterpreter extends LispInterpreter {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void printFarewell() {
|
||||
protected void afterInterpreting() {
|
||||
environment.getOutput().println();
|
||||
environment.getOutput().println();
|
||||
environment.getOutput().close();
|
||||
|
|
|
@ -7,7 +7,7 @@ import error.LispException;
|
|||
import parser.LispParser;
|
||||
import sexpression.SExpression;
|
||||
|
||||
public abstract class LispInterpreter {
|
||||
public class LispInterpreter {
|
||||
|
||||
protected RuntimeEnvironment environment;
|
||||
private LispParser parser;
|
||||
|
@ -18,21 +18,21 @@ public abstract class LispInterpreter {
|
|||
|
||||
public void interpret() {
|
||||
createParser();
|
||||
printGreeting();
|
||||
beforeInterpreting();
|
||||
|
||||
for (displayPrompt(); !parser.isEof(); displayPrompt())
|
||||
for (prompt(); !parser.isEof(); prompt())
|
||||
printValueOfNextSExpression();
|
||||
|
||||
printFarewell();
|
||||
afterInterpreting();
|
||||
}
|
||||
|
||||
private void createParser() {
|
||||
parser = new LispParser(environment.getInput(), environment.getInputName());
|
||||
}
|
||||
|
||||
protected void printGreeting() {}
|
||||
protected void beforeInterpreting() {}
|
||||
|
||||
protected void displayPrompt() {}
|
||||
protected void prompt() {}
|
||||
|
||||
protected void printValueOfNextSExpression() {
|
||||
try {
|
||||
|
@ -50,8 +50,9 @@ public abstract class LispInterpreter {
|
|||
environment.getOutput().flush();
|
||||
}
|
||||
|
||||
protected void printFarewell() {
|
||||
protected void afterInterpreting() {
|
||||
environment.getOutput().println();
|
||||
environment.getOutput().flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -166,7 +166,12 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
|
|||
}
|
||||
|
||||
private LispInterpreter createInterpreter() {
|
||||
return isInteractive ? new InteractiveLispInterpreter() : new FileLispInterpreter();
|
||||
if (isFileBased)
|
||||
return new FileLispInterpreter();
|
||||
else if (isInteractive)
|
||||
return new InteractiveLispInterpreter();
|
||||
|
||||
return new LispInterpreter();
|
||||
}
|
||||
|
||||
public static class InterpreterAlreadyBuiltException extends CriticalLispException {
|
||||
|
|
|
@ -9,6 +9,8 @@ import com.googlecode.lanterna.terminal.*;
|
|||
|
||||
import interpreter.*;
|
||||
import terminal.LispTerminal;
|
||||
import terminal.SafeStream.SafePipedOutputStream;
|
||||
import terminal.SafeStream.UncheckedIOException;
|
||||
|
||||
public class LispMain {
|
||||
|
||||
|
@ -17,108 +19,55 @@ public class LispMain {
|
|||
private static final String ANSI_GREEN = "\u001B[32m";
|
||||
private static final String ANSI_YELLOW = "\u001B[33m";
|
||||
private static final String ANSI_PURPLE = "\u001B[35m";
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
LispMain lispMain = new LispMain();
|
||||
|
||||
if (args.length == 0)
|
||||
lispMain.runInteractive();
|
||||
else
|
||||
lispMain.runWithFile(args[0]);
|
||||
}
|
||||
|
||||
private PipedInputStream inputReader;
|
||||
private PipedOutputStream inputWriter;
|
||||
private PipedInputStream outputReader;
|
||||
private PipedOutputStream outputWriter;
|
||||
private SafePipedOutputStream safeOutputWriter;
|
||||
private LispTerminal lispTerminal;
|
||||
|
||||
private LispMain() throws IOException {
|
||||
inputReader = new PipedInputStream();
|
||||
inputWriter = new PipedOutputStream(inputReader);
|
||||
outputReader = new PipedInputStream();
|
||||
outputWriter = new PipedOutputStream(outputReader);
|
||||
lispTerminal = new LispTerminal(createIOSafeTerminal(), inputWriter, outputReader);
|
||||
}
|
||||
|
||||
private IOSafeTerminal createIOSafeTerminal() throws IOException {
|
||||
return IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter(new DefaultTerminalFactory().createTerminal());
|
||||
Terminal defaultTerminal = new DefaultTerminalFactory().createTerminal();
|
||||
|
||||
return IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter(defaultTerminal);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
new LispMain().run(args);
|
||||
}
|
||||
|
||||
private void run(String[] args) {
|
||||
if (args.length == 0)
|
||||
private void runInteractive() {
|
||||
initializeTerminal();
|
||||
lispTerminal.start();
|
||||
|
||||
LispInterpreter interpreter = buildInterpreter(args);
|
||||
interpreter.interpret();
|
||||
buildInteractiveInterpreter().interpret();
|
||||
}
|
||||
|
||||
private LispInterpreter buildInterpreter(String[] args) {
|
||||
private LispInterpreter buildInteractiveInterpreter() {
|
||||
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
|
||||
configureInput(args, builder);
|
||||
configureOutput(args, builder);
|
||||
configureTerminatingFunctions(args, builder);
|
||||
configureDecorators(args, builder);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void configureTerminatingFunctions(String[] args, LispInterpreterBuilder builder) {
|
||||
if (args.length > 0) {
|
||||
builder.setTerminationFunction(() -> System.exit(0));
|
||||
builder.setErrorTerminationFunction(() -> System.exit(1));
|
||||
} else {
|
||||
PrintStream outputStream = new PrintStream(outputWriter);
|
||||
builder.setInput(inputReader, "terminal");
|
||||
builder.setOutput(outputStream);
|
||||
builder.setErrorOutput(outputStream);
|
||||
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(s -> s);
|
||||
builder.setWarningOutputDecorator(s -> s);
|
||||
builder.setErrorOutputDecorator(s -> s);
|
||||
builder.setCriticalOutputDecorator(s -> s);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void shutdown() {
|
||||
try {
|
||||
lispTerminal.stop();
|
||||
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)
|
||||
builder.useFile(args[0]);
|
||||
else
|
||||
builder.setInput(inputReader, "stdin");
|
||||
}
|
||||
|
||||
private static Function<String, String> makeColorDecorator(String color) {
|
||||
return new Function<String, String>() {
|
||||
|
||||
@Override
|
||||
public String apply(String s) {
|
||||
return color + s + ANSI_RESET;
|
||||
}
|
||||
};
|
||||
safeOutputWriter.close();
|
||||
}
|
||||
|
||||
private static Function<String, String> makeInteractiveDecorator(String color) {
|
||||
|
@ -131,4 +80,51 @@ public class LispMain {
|
|||
};
|
||||
}
|
||||
|
||||
private void initializeTerminal() {
|
||||
try {
|
||||
initalizeTerminalWithException();
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initalizeTerminalWithException() throws IOException {
|
||||
inputReader = new PipedInputStream();
|
||||
inputWriter = new PipedOutputStream(inputReader);
|
||||
outputReader = new PipedInputStream();
|
||||
outputWriter = new PipedOutputStream(outputReader);
|
||||
safeOutputWriter = new SafePipedOutputStream(outputWriter);
|
||||
lispTerminal = new LispTerminal(createIOSafeTerminal(), inputWriter, outputReader);
|
||||
}
|
||||
|
||||
private void runWithFile(String fileName) {
|
||||
buildFileInterpreter(fileName).interpret();
|
||||
}
|
||||
|
||||
private LispInterpreter buildFileInterpreter(String fileName) {
|
||||
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
|
||||
builder.useFile(fileName);
|
||||
builder.setOutput(System.out);
|
||||
builder.setErrorOutput(System.err);
|
||||
builder.setTerminationFunction(() -> System.exit(0));
|
||||
builder.setErrorTerminationFunction(() -> System.exit(1));
|
||||
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));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static Function<String, String> makeColorDecorator(String color) {
|
||||
return new Function<String, String>() {
|
||||
|
||||
@Override
|
||||
public String apply(String s) {
|
||||
return color + s + ANSI_RESET;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ import java.util.concurrent.*;
|
|||
|
||||
import com.googlecode.lanterna.*;
|
||||
import com.googlecode.lanterna.input.*;
|
||||
import com.googlecode.lanterna.terminal.*;
|
||||
import com.googlecode.lanterna.terminal.IOSafeTerminal;
|
||||
|
||||
import terminal.SafePipedStream.*;
|
||||
import terminal.SafeStream.*;
|
||||
|
||||
public class LispTerminal {
|
||||
|
||||
|
@ -21,42 +21,60 @@ 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;
|
||||
private String inputLine;
|
||||
private String outputSegment;
|
||||
private boolean isFinished;
|
||||
private boolean isStopped;
|
||||
private int originColumn;
|
||||
private int originRow;
|
||||
|
||||
public LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||
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();
|
||||
this.inputLine = "";
|
||||
this.outputSegment = "";
|
||||
this.isFinished = false;
|
||||
this.isStopped = false;
|
||||
|
||||
addResizeListener();
|
||||
updateOrigin();
|
||||
terminal.addResizeListener((t, newSize) -> resize());
|
||||
}
|
||||
|
||||
private void addResizeListener() {
|
||||
terminal.addResizeListener(new TerminalResizeListener() {
|
||||
|
||||
@Override
|
||||
public void onResized(Terminal terminal, TerminalSize newSize) {
|
||||
resize();
|
||||
}
|
||||
});
|
||||
private synchronized void updateOrigin() {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
originColumn = cursorPosition.getColumn();
|
||||
originRow = cursorPosition.getRow();
|
||||
}
|
||||
|
||||
private synchronized void resize() {
|
||||
terminalSize = terminal.getTerminalSize();
|
||||
terminal.clearScreen();
|
||||
terminal.setCursorPosition(0, 0);
|
||||
updateOrigin();
|
||||
putString(inputLine);
|
||||
moveCursorToEndOfInput();
|
||||
}
|
||||
|
||||
private synchronized void putString(String characters) {
|
||||
for (char c : characters.toCharArray())
|
||||
terminal.putCharacter(c);
|
||||
}
|
||||
|
||||
private synchronized void moveCursorToEndOfInput() {
|
||||
terminal.setCursorPosition(getLeadingEdge());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
@ -66,7 +84,7 @@ public class LispTerminal {
|
|||
}
|
||||
|
||||
private void readInput() {
|
||||
while (!isFinished)
|
||||
while (!isStopped)
|
||||
processNextKey();
|
||||
}
|
||||
|
||||
|
@ -149,25 +167,6 @@ public class LispTerminal {
|
|||
updateOrigin();
|
||||
}
|
||||
|
||||
private synchronized void moveCursorToEndOfInput() {
|
||||
terminal.setCursorPosition(getLeadingEdge());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private synchronized void doLeftArrow() {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
|
||||
|
@ -256,11 +255,6 @@ public class LispTerminal {
|
|||
moveCursorLeft(cursorPosition);
|
||||
}
|
||||
|
||||
private synchronized void putString(String characters) {
|
||||
for (char c : characters.toCharArray())
|
||||
terminal.putCharacter(c);
|
||||
}
|
||||
|
||||
private synchronized void doDelete() {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
|
||||
|
@ -331,7 +325,7 @@ public class LispTerminal {
|
|||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
isFinished = true;
|
||||
isStopped = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,7 +376,7 @@ public class LispTerminal {
|
|||
}
|
||||
|
||||
public void stop() {
|
||||
isFinished = true;
|
||||
isStopped = true;
|
||||
inputWriter.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package terminal;
|
|||
|
||||
import java.io.*;
|
||||
|
||||
public interface SafePipedStream {
|
||||
public interface SafeStream {
|
||||
|
||||
public static class SafePipedInputStream {
|
||||
|
|
@ -473,4 +473,15 @@ public class LispTerminalTest {
|
|||
{ 'o', 'u', 't' } });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resizeIsHandledGracefully() {
|
||||
enterCharacters("resize");
|
||||
pressKey(KeyType.Enter);
|
||||
enterCharacters("test");
|
||||
setColumns(10);
|
||||
assertCursorPosition(4, 0);
|
||||
assertCharacterPositions(new char[][] { { 't', 'e', 's', 't', ' ', ' ', ' ', ' ', ' ', ' ' },
|
||||
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' } });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue