Fix terminal resize issues

This commit is contained in:
Mike Cifelli 2017-03-21 12:07:22 -04:00
parent a8eff1ad70
commit 5cb6212d2a
8 changed files with 156 additions and 143 deletions

View File

@ -2,4 +2,10 @@ package interpreter;
public class FileLispInterpreter extends LispInterpreter {
@Override
protected void afterInterpreting() {
environment.getOutput().println();
environment.getOutput().close();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ package terminal;
import java.io.*;
public interface SafePipedStream {
public interface SafeStream {
public static class SafePipedInputStream {

View File

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