From 5cb6212d2a1b2efc451157e0befbaa1e015a23db Mon Sep 17 00:00:00 2001 From: Mike Cifelli Date: Tue, 21 Mar 2017 12:07:22 -0400 Subject: [PATCH] Fix terminal resize issues --- src/interpreter/FileLispInterpreter.java | 6 + .../InteractiveLispInterpreter.java | 6 +- src/interpreter/LispInterpreter.java | 15 +- .../LispInterpreterBuilderImpl.java | 7 +- src/main/LispMain.java | 166 +++++++++--------- src/terminal/LispTerminal.java | 86 +++++---- .../{SafePipedStream.java => SafeStream.java} | 2 +- test/terminal/LispTerminalTest.java | 11 ++ 8 files changed, 156 insertions(+), 143 deletions(-) rename src/terminal/{SafePipedStream.java => SafeStream.java} (98%) diff --git a/src/interpreter/FileLispInterpreter.java b/src/interpreter/FileLispInterpreter.java index 4356ad9..b83e081 100644 --- a/src/interpreter/FileLispInterpreter.java +++ b/src/interpreter/FileLispInterpreter.java @@ -2,4 +2,10 @@ package interpreter; public class FileLispInterpreter extends LispInterpreter { + @Override + protected void afterInterpreting() { + environment.getOutput().println(); + environment.getOutput().close(); + } + } diff --git a/src/interpreter/InteractiveLispInterpreter.java b/src/interpreter/InteractiveLispInterpreter.java index 47f6050..2204714 100644 --- a/src/interpreter/InteractiveLispInterpreter.java +++ b/src/interpreter/InteractiveLispInterpreter.java @@ -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(); diff --git a/src/interpreter/LispInterpreter.java b/src/interpreter/LispInterpreter.java index e0e1033..d8dfaa2 100644 --- a/src/interpreter/LispInterpreter.java +++ b/src/interpreter/LispInterpreter.java @@ -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(); } } diff --git a/src/interpreter/LispInterpreterBuilderImpl.java b/src/interpreter/LispInterpreterBuilderImpl.java index 29fb195..645010a 100644 --- a/src/interpreter/LispInterpreterBuilderImpl.java +++ b/src/interpreter/LispInterpreterBuilderImpl.java @@ -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 { diff --git a/src/main/LispMain.java b/src/main/LispMain.java index 1caafbb..d3abc77 100644 --- a/src/main/LispMain.java +++ b/src/main/LispMain.java @@ -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 runInteractive() { + initializeTerminal(); + lispTerminal.start(); + buildInteractiveInterpreter().interpret(); } - private void run(String[] args) { - if (args.length == 0) - lispTerminal.start(); - - LispInterpreter interpreter = buildInterpreter(args); - interpreter.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); + PrintStream outputStream = new PrintStream(outputWriter); + builder.setInput(inputReader, "terminal"); + builder.setOutput(outputStream); + 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(); } - 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() { - 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 makeColorDecorator(String color) { - return new Function() { - - @Override - public String apply(String s) { - return color + s + ANSI_RESET; - } - }; + lispTerminal.stop(); + safeOutputWriter.close(); } private static Function 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 makeColorDecorator(String color) { + return new Function() { + + @Override + public String apply(String s) { + return color + s + ANSI_RESET; + } + }; + } + } diff --git a/src/terminal/LispTerminal.java b/src/terminal/LispTerminal.java index 9880674..e3a2b93 100644 --- a/src/terminal/LispTerminal.java +++ b/src/terminal/LispTerminal.java @@ -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(); } diff --git a/src/terminal/SafePipedStream.java b/src/terminal/SafeStream.java similarity index 98% rename from src/terminal/SafePipedStream.java rename to src/terminal/SafeStream.java index 334365b..e17994a 100644 --- a/src/terminal/SafePipedStream.java +++ b/src/terminal/SafeStream.java @@ -2,7 +2,7 @@ package terminal; import java.io.*; -public interface SafePipedStream { +public interface SafeStream { public static class SafePipedInputStream { diff --git a/test/terminal/LispTerminalTest.java b/test/terminal/LispTerminalTest.java index b996945..8f3b91b 100644 --- a/test/terminal/LispTerminalTest.java +++ b/test/terminal/LispTerminalTest.java @@ -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', ' ', ' ', ' ', ' ', ' ', ' ' }, + { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' } }); + } + }