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 { 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 = "~ "; private static final String PROMPT = "~ ";
@Override @Override
protected void printGreeting() { protected void beforeInterpreting() {
environment.getOutput().println(GREETING); environment.getOutput().println(GREETING);
environment.getOutput().println(); environment.getOutput().println();
environment.getOutput().flush(); environment.getOutput().flush();
} }
@Override @Override
protected void displayPrompt() { protected void prompt() {
environment.getOutput().print(environment.decorateOutput(PROMPT)); environment.getOutput().print(environment.decorateOutput(PROMPT));
environment.getOutput().flush(); environment.getOutput().flush();
} }
@ -25,7 +25,7 @@ public class InteractiveLispInterpreter extends LispInterpreter {
} }
@Override @Override
protected void printFarewell() { protected void afterInterpreting() {
environment.getOutput().println(); environment.getOutput().println();
environment.getOutput().println(); environment.getOutput().println();
environment.getOutput().close(); environment.getOutput().close();

View File

@ -7,7 +7,7 @@ import error.LispException;
import parser.LispParser; import parser.LispParser;
import sexpression.SExpression; import sexpression.SExpression;
public abstract class LispInterpreter { public class LispInterpreter {
protected RuntimeEnvironment environment; protected RuntimeEnvironment environment;
private LispParser parser; private LispParser parser;
@ -18,21 +18,21 @@ public abstract class LispInterpreter {
public void interpret() { public void interpret() {
createParser(); createParser();
printGreeting(); beforeInterpreting();
for (displayPrompt(); !parser.isEof(); displayPrompt()) for (prompt(); !parser.isEof(); prompt())
printValueOfNextSExpression(); printValueOfNextSExpression();
printFarewell(); afterInterpreting();
} }
private void createParser() { private void createParser() {
parser = new LispParser(environment.getInput(), environment.getInputName()); parser = new LispParser(environment.getInput(), environment.getInputName());
} }
protected void printGreeting() {} protected void beforeInterpreting() {}
protected void displayPrompt() {} protected void prompt() {}
protected void printValueOfNextSExpression() { protected void printValueOfNextSExpression() {
try { try {
@ -50,8 +50,9 @@ public abstract class LispInterpreter {
environment.getOutput().flush(); environment.getOutput().flush();
} }
protected void printFarewell() { protected void afterInterpreting() {
environment.getOutput().println(); environment.getOutput().println();
environment.getOutput().flush();
} }
} }

View File

@ -166,7 +166,12 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
} }
private LispInterpreter createInterpreter() { 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 { public static class InterpreterAlreadyBuiltException extends CriticalLispException {

View File

@ -9,6 +9,8 @@ import com.googlecode.lanterna.terminal.*;
import interpreter.*; import interpreter.*;
import terminal.LispTerminal; import terminal.LispTerminal;
import terminal.SafeStream.SafePipedOutputStream;
import terminal.SafeStream.UncheckedIOException;
public class LispMain { public class LispMain {
@ -17,108 +19,55 @@ 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";
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 PipedInputStream inputReader;
private PipedOutputStream inputWriter; private PipedOutputStream inputWriter;
private PipedInputStream outputReader; private PipedInputStream outputReader;
private PipedOutputStream outputWriter; private PipedOutputStream outputWriter;
private SafePipedOutputStream safeOutputWriter;
private LispTerminal lispTerminal; 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 { 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 { private void runInteractive() {
new LispMain().run(args); initializeTerminal();
lispTerminal.start();
buildInteractiveInterpreter().interpret();
} }
private void run(String[] args) { private LispInterpreter buildInteractiveInterpreter() {
if (args.length == 0)
lispTerminal.start();
LispInterpreter interpreter = buildInterpreter(args);
interpreter.interpret();
}
private LispInterpreter buildInterpreter(String[] args) {
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance(); LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
configureInput(args, builder); PrintStream outputStream = new PrintStream(outputWriter);
configureOutput(args, builder); builder.setInput(inputReader, "terminal");
configureTerminatingFunctions(args, builder); builder.setOutput(outputStream);
configureDecorators(args, builder); builder.setErrorOutput(outputStream);
builder.setTerminationFunction(this::shutdown);
builder.setErrorTerminationFunction(this::shutdown);
builder.setOutputDecorator(makeInteractiveDecorator(ANSI_GREEN));
builder.setValueOutputDecorator(s -> s);
builder.setWarningOutputDecorator(s -> s);
builder.setErrorOutputDecorator(s -> s);
builder.setCriticalOutputDecorator(s -> s);
return builder.build(); 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 {
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);
}
}
private void shutdown() { private void shutdown() {
try { lispTerminal.stop();
lispTerminal.stop(); safeOutputWriter.close();
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;
}
};
} }
private static Function<String, String> makeInteractiveDecorator(String color) { 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.*;
import com.googlecode.lanterna.input.*; 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 { public class LispTerminal {
@ -21,42 +21,60 @@ public class LispTerminal {
private SafePipedOutputStream inputWriter; private SafePipedOutputStream inputWriter;
private SafePipedInputStream outputReader; private SafePipedInputStream outputReader;
private ExecutorService executorService; private ExecutorService executorService;
// private ControlSequenceHandler controlSequenceHandler; private ControlSequenceHandler controlSequenceHandler;
private TerminalSize terminalSize; private TerminalSize terminalSize;
private int originColumn;
private int originRow;
private String inputLine; private String inputLine;
private String outputSegment; private String outputSegment;
private boolean isFinished; private boolean isStopped;
private int originColumn;
private int originRow;
public LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) { public LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) {
this.terminal = terminal; this.terminal = terminal;
this.inputWriter = new SafePipedOutputStream(inputWriter); this.inputWriter = new SafePipedOutputStream(inputWriter);
this.outputReader = new SafePipedInputStream(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.terminalSize = terminal.getTerminalSize(); this.terminalSize = terminal.getTerminalSize();
this.originColumn = terminal.getCursorPosition().getColumn();
this.originRow = terminal.getCursorPosition().getRow();
this.inputLine = ""; this.inputLine = "";
this.outputSegment = ""; this.outputSegment = "";
this.isFinished = false; this.isStopped = false;
addResizeListener(); updateOrigin();
terminal.addResizeListener((t, newSize) -> resize());
} }
private void addResizeListener() { private synchronized void updateOrigin() {
terminal.addResizeListener(new TerminalResizeListener() { TerminalPosition cursorPosition = terminal.getCursorPosition();
originColumn = cursorPosition.getColumn();
@Override originRow = cursorPosition.getRow();
public void onResized(Terminal terminal, TerminalSize newSize) {
resize();
}
});
} }
private synchronized void resize() { private synchronized void resize() {
terminalSize = terminal.getTerminalSize(); 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() { public void start() {
@ -66,7 +84,7 @@ public class LispTerminal {
} }
private void readInput() { private void readInput() {
while (!isFinished) while (!isStopped)
processNextKey(); processNextKey();
} }
@ -149,25 +167,6 @@ public class LispTerminal {
updateOrigin(); 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() { private synchronized void doLeftArrow() {
TerminalPosition cursorPosition = terminal.getCursorPosition(); TerminalPosition cursorPosition = terminal.getCursorPosition();
@ -256,11 +255,6 @@ public class LispTerminal {
moveCursorLeft(cursorPosition); moveCursorLeft(cursorPosition);
} }
private synchronized void putString(String characters) {
for (char c : characters.toCharArray())
terminal.putCharacter(c);
}
private synchronized void doDelete() { private synchronized void doDelete() {
TerminalPosition cursorPosition = terminal.getCursorPosition(); TerminalPosition cursorPosition = terminal.getCursorPosition();
@ -331,7 +325,7 @@ public class LispTerminal {
try { try {
Thread.sleep(1); Thread.sleep(1);
} catch (InterruptedException e) { } catch (InterruptedException e) {
isFinished = true; isStopped = true;
} }
} }
@ -382,7 +376,7 @@ public class LispTerminal {
} }
public void stop() { public void stop() {
isFinished = true; isStopped = true;
inputWriter.close(); inputWriter.close();
} }

View File

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

View File

@ -473,4 +473,15 @@ public class LispTerminalTest {
{ 'o', 'u', 't' } }); { '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', ' ', ' ', ' ', ' ', ' ', ' ' },
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' } });
}
} }