diff --git a/src/main/LispMain.java b/src/main/LispMain.java index c4026af..3755dcd 100644 --- a/src/main/LispMain.java +++ b/src/main/LispMain.java @@ -76,10 +76,10 @@ public class LispMain { 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)); + builder.setValueOutputDecorator(s -> s); + builder.setWarningOutputDecorator(s -> s); + builder.setErrorOutputDecorator(s -> s); + builder.setCriticalOutputDecorator(s -> s); } } diff --git a/src/terminal/LispTerminal.java b/src/terminal/LispTerminal.java index 391a883..4667be8 100644 --- a/src/terminal/LispTerminal.java +++ b/src/terminal/LispTerminal.java @@ -20,6 +20,7 @@ public class LispTerminal { private ExecutorService executorService; private ControlSequenceHandler controlSequenceHandler; private TerminalPosition origin; + private TerminalPosition leadingEdge; private String inputLine; private String outputSegment; private boolean isFinished; @@ -31,6 +32,7 @@ public class LispTerminal { this.executorService = Executors.newFixedThreadPool(2); this.controlSequenceHandler = new ControlSequenceHandler(); this.origin = terminal.getCursorPosition(); + this.leadingEdge = origin; this.inputLine = ""; this.outputSegment = ""; this.isFinished = false; @@ -130,10 +132,15 @@ public class LispTerminal { } private synchronized void retractCursor(TerminalPosition cursorPosition) { + TerminalPosition newPosition = cursorPosition.withRelativeColumn(-1); + if (isAtStartOfRow(cursorPosition)) - terminal.setCursorPosition(terminal.getTerminalSize().getColumns(), cursorPosition.getRow() - 1); - else - terminal.setCursorPosition(cursorPosition.withRelativeColumn(-1)); + newPosition = cursorPosition.withColumn(terminal.getTerminalSize().getColumns()).withRelativeRow(-1); + + terminal.setCursorPosition(newPosition); + + if (distanceFromOrigin(newPosition) == inputLine.length()) + leadingEdge = terminal.getCursorPosition(); } private boolean isAtStartOfRow(TerminalPosition cursorPosition) { @@ -153,16 +160,40 @@ public class LispTerminal { private synchronized void advanceCursor(TerminalPosition cursorPosition) { if (isAtEndOfRow(cursorPosition)) - terminal.setCursorPosition(0, cursorPosition.getRow() + 1); + moveCursorToNextRow(cursorPosition); else terminal.setCursorPosition(cursorPosition.withRelativeColumn(1)); + + TerminalPosition newPosition = terminal.getCursorPosition(); + + if (distanceFromOrigin(newPosition) == inputLine.length()) + leadingEdge = terminal.getCursorPosition(); } private synchronized boolean isAtEndOfRow(TerminalPosition cursorPosition) { - return cursorPosition.getColumn() == terminal.getTerminalSize().getColumns() - 1; + return cursorPosition.getColumn() >= terminal.getTerminalSize().getColumns() - 1; + } + + private void moveCursorToNextRow(TerminalPosition cursorPosition) { + if (isEndOfBuffer()) + createNewRowForCursor(); + else + terminal.setCursorPosition(cursorPosition.withColumn(0).withRelativeRow(1)); + } + + private boolean isEndOfBuffer() { + return terminal.getCursorPosition().getRow() == terminal.getTerminalSize().getRows() - 1; + } + + private void createNewRowForCursor() { + TerminalPosition originalPosition = terminal.getCursorPosition(); + terminal.putCharacter('\n'); + terminal.setCursorPosition(originalPosition.withColumn(0)); + origin = origin.withRelativeRow(-1); } private synchronized void doEnter() { + moveCursorToEndOfInput(); terminal.putCharacter('\n'); inputLine += "\n"; @@ -176,6 +207,7 @@ public class LispTerminal { inputLine = ""; origin = terminal.getCursorPosition(); + leadingEdge = origin; } private synchronized void doBackspace() { @@ -270,14 +302,28 @@ public class LispTerminal { private synchronized void printSegment() { terminal.setCursorVisible(false); + moveCursorToEndOfInput(); for (char c : outputSegment.toCharArray()) terminal.putCharacter(c); + moveCursorToNextRowIfNecessary(); terminal.flush(); terminal.setCursorVisible(true); outputSegment = ""; origin = terminal.getCursorPosition(); + leadingEdge = origin; + } + + private synchronized void moveCursorToEndOfInput() { + terminal.setCursorPosition(leadingEdge); + } + + private synchronized void moveCursorToNextRowIfNecessary() { + TerminalPosition cursorPosition = terminal.getCursorPosition(); + + if (isAtEndOfRow(cursorPosition)) + moveCursorToNextRow(cursorPosition); } public void finish() { diff --git a/test/terminal/LispTerminalTest.java b/test/terminal/LispTerminalTest.java index 7bc44a3..3a554d2 100644 --- a/test/terminal/LispTerminalTest.java +++ b/test/terminal/LispTerminalTest.java @@ -74,14 +74,18 @@ public class LispTerminalTest { virtualTerminal.setTerminalSize(new TerminalSize(columns, virtualTerminal.getTerminalSize().getRows())); } + private void setRows(int rows) { + virtualTerminal.setTerminalSize(new TerminalSize(virtualTerminal.getTerminalSize().getColumns(), rows)); + } + private void assertCursorPosition(int column, int row) { - assertEquals(column, virtualTerminal.getCursorBufferPosition().getColumn()); - assertEquals(row, virtualTerminal.getCursorBufferPosition().getRow()); + assertEquals(column, virtualTerminal.getCursorPosition().getColumn()); + assertEquals(row, virtualTerminal.getCursorPosition().getRow()); } private void assertCharacterAtPosition(char character, int column, int row) { TerminalPosition position = new TerminalPosition(column, row); - assertEquals(character, virtualTerminal.getBufferCharacter(position).getCharacter()); + assertEquals(character, virtualTerminal.getCharacter(position).getCharacter()); } private void assertInputWritten(String expected) { @@ -387,6 +391,7 @@ public class LispTerminalTest { @Test public void outputIsWritten() { produceOutput("output"); + assertCursorPosition(6, 0); assertCharacterAtPosition('o', 0, 0); assertCharacterAtPosition('u', 1, 0); assertCharacterAtPosition('t', 2, 0); @@ -404,4 +409,108 @@ public class LispTerminalTest { assertCharacterAtPosition(' ', 2, 0); } + @Test + public void enterTextPastLastLineOfBuffer() { + setColumns(3); + setRows(2); + enterCharacters("01201201"); + assertCursorPosition(2, 1); + assertCharacterAtPosition('0', 0, 0); + assertCharacterAtPosition('1', 1, 0); + assertCharacterAtPosition('2', 2, 0); + assertCharacterAtPosition('0', 0, 1); + assertCharacterAtPosition('1', 1, 1); + } + +// @Test +// public void insertingTextPushesInputPastEndOfBuffer() { +// setColumns(2); +// setRows(3); +// enterCharacters("00112"); +// +// pressKey(KeyType.ArrowLeft); +// pressKey(KeyType.ArrowLeft); +// pressKey(KeyType.ArrowLeft); +// enterCharacters("zz"); +// assertCursorPosition(0, 1); +// assertCharacterAtPosition('z', 0, 0); +// assertCharacterAtPosition('z', 1, 0); +// assertCharacterAtPosition('1', 0, 1); +// assertCharacterAtPosition('1', 1, 1); +// assertCharacterAtPosition('2', 0, 2); +// } + + @Test + public void printedOutputToEndOfRow_MovesCursorToNextRow() { + setColumns(3); + produceOutput("out"); + assertCursorPosition(0, 1); + } + + @Test + public void printedOutputToEndOfBuffer_MovesCursorToNewRow() { + setColumns(3); + setRows(2); + produceOutput("output"); + assertCursorPosition(0, 1); + assertCharacterAtPosition('p', 0, 0); + assertCharacterAtPosition('u', 1, 0); + assertCharacterAtPosition('t', 2, 0); + assertCharacterAtPosition(' ', 0, 1); + assertCharacterAtPosition(' ', 1, 1); + assertCharacterAtPosition(' ', 2, 1); + } + + @Test + public void printedOutputDoesNotOverwriteInput() { + setColumns(3); + enterCharacters("01201201"); + + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.ArrowLeft); + produceOutput("out"); + assertCursorPosition(0, 4); + assertCharacterAtPosition('0', 0, 0); + assertCharacterAtPosition('1', 1, 0); + assertCharacterAtPosition('2', 2, 0); + assertCharacterAtPosition('0', 0, 1); + assertCharacterAtPosition('1', 1, 1); + assertCharacterAtPosition('2', 2, 1); + assertCharacterAtPosition('0', 0, 2); + assertCharacterAtPosition('1', 1, 2); + assertCharacterAtPosition('o', 2, 2); + assertCharacterAtPosition('u', 0, 3); + assertCharacterAtPosition('t', 1, 3); + assertCharacterAtPosition(' ', 2, 3); + } + @Test + public void printedOutputDoesNotOverwriteInput_AfterEnter() { + setColumns(3); + enterCharacters("01201201"); + + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.ArrowLeft); + pressKey(KeyType.Enter); + produceOutput("out"); + assertCursorPosition(0, 4); + assertCharacterAtPosition('0', 0, 0); + assertCharacterAtPosition('1', 1, 0); + assertCharacterAtPosition('2', 2, 0); + assertCharacterAtPosition('0', 0, 1); + assertCharacterAtPosition('1', 1, 1); + assertCharacterAtPosition('2', 2, 1); + assertCharacterAtPosition('0', 0, 2); + assertCharacterAtPosition('1', 1, 2); + assertCharacterAtPosition(' ', 2, 2); + assertCharacterAtPosition('o', 0, 3); + assertCharacterAtPosition('u', 1, 3); + assertCharacterAtPosition('t', 2, 3); + } + }