Clean up terminal code

This commit is contained in:
Mike Cifelli 2017-03-21 09:25:40 -04:00
parent fc96894d14
commit a8eff1ad70
3 changed files with 165 additions and 153 deletions

View File

@ -41,7 +41,7 @@ public class LispMain {
private void run(String[] args) { private void run(String[] args) {
if (args.length == 0) if (args.length == 0)
lispTerminal.run(); lispTerminal.start();
LispInterpreter interpreter = buildInterpreter(args); LispInterpreter interpreter = buildInterpreter(args);
interpreter.interpret(); interpreter.interpret();
@ -85,7 +85,7 @@ public class LispMain {
private void shutdown() { private void shutdown() {
try { try {
lispTerminal.finish(); lispTerminal.stop();
outputWriter.close(); outputWriter.close();
} catch (IOException e) { } catch (IOException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block

View File

@ -1,5 +1,6 @@
package terminal; package terminal;
import static com.googlecode.lanterna.input.KeyType.*;
import static terminal.ControlSequenceHandler.isEscape; import static terminal.ControlSequenceHandler.isEscape;
import static util.Characters.EOF; import static util.Characters.EOF;
@ -54,17 +55,17 @@ public class LispTerminal {
}); });
} }
private void resize() { private synchronized void resize() {
terminalSize = terminal.getTerminalSize(); terminalSize = terminal.getTerminalSize();
} }
public void run() { public void start() {
executorService.execute(this::readInput); executorService.execute(this::readInput);
executorService.execute(this::writeOutput); executorService.execute(this::writeOutput);
executorService.shutdown(); executorService.shutdown();
} }
public void readInput() { private void readInput() {
while (!isFinished) while (!isFinished)
processNextKey(); processNextKey();
} }
@ -83,11 +84,10 @@ public class LispTerminal {
try { try {
keyStroke = terminal.pollInput(); keyStroke = terminal.pollInput();
} catch (IllegalStateException e) { } catch (IllegalStateException e) { // issue #299
// Issue #299 moveCursorToEndOfInput();
terminal.putCharacter('\n'); terminal.putCharacter('\n');
terminal.close(); stop();
System.exit(0);
} }
return keyStroke; return keyStroke;
@ -108,7 +108,7 @@ public class LispTerminal {
private synchronized void doControlKey(KeyStroke keyStroke) { private synchronized void doControlKey(KeyStroke keyStroke) {
KeyType keyType = keyStroke.getKeyType(); KeyType keyType = keyStroke.getKeyType();
if (keyType == KeyType.Character) if (keyType == Character)
doControlCharacter(keyStroke); doControlCharacter(keyStroke);
} }
@ -119,99 +119,26 @@ public class LispTerminal {
private synchronized void doControlD() { private synchronized void doControlD() {
doEnter(); doEnter();
finish(); stop();
} }
private void doNormalKey(KeyStroke keyStroke) { private synchronized void doNormalKey(KeyStroke keyStroke) {
KeyType keyType = keyStroke.getKeyType(); KeyType keyType = keyStroke.getKeyType();
if (keyType == KeyType.ArrowLeft) if (keyType == Enter)
moveCursorLeft();
else if (keyType == KeyType.ArrowRight)
moveCursorRight();
else if (keyType == KeyType.Enter)
doEnter(); doEnter();
else if (keyType == KeyType.Backspace) else if (keyType == ArrowLeft)
doLeftArrow();
else if (keyType == ArrowRight)
doRightArrow();
else if (keyType == Backspace)
doBackspace(); doBackspace();
else if (keyType == KeyType.Delete) else if (keyType == Delete)
doDelete(); doDelete();
else if (keyType == KeyType.Character) else if (keyType == Character)
doCharacter(keyStroke.getCharacter()); doCharacter(keyStroke.getCharacter());
} }
private synchronized void moveCursorLeft() {
TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveLeft(cursorPosition))
retractCursor(cursorPosition);
}
private synchronized boolean isPossibleToMoveLeft(TerminalPosition cursorPosition) {
return getDistanceFromOrigin(cursorPosition) > 0;
}
private synchronized int getDistanceFromOrigin(TerminalPosition cursorPosition) {
int columnDifference = cursorPosition.getColumn() - originColumn;
int rowDifference = cursorPosition.getRow() - originRow;
int totalColumns = terminalSize.getColumns();
return columnDifference + (totalColumns * rowDifference);
}
private synchronized void retractCursor(TerminalPosition cursorPosition) {
TerminalPosition newPosition = cursorPosition.withRelativeColumn(-1);
if (isAtStartOfRow(cursorPosition))
newPosition = cursorPosition.withColumn(terminalSize.getColumns()).withRelativeRow(-1);
terminal.setCursorPosition(newPosition);
}
private boolean isAtStartOfRow(TerminalPosition cursorPosition) {
return cursorPosition.getColumn() == 0;
}
private synchronized void moveCursorRight() {
TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveRight(cursorPosition))
advanceCursor(cursorPosition);
}
private synchronized boolean isPossibleToMoveRight(TerminalPosition cursorPosition) {
return getDistanceFromOrigin(cursorPosition) < inputLine.length();
}
private synchronized void advanceCursor(TerminalPosition cursorPosition) {
if (isEndOfRow(cursorPosition))
moveCursorToNextRow(cursorPosition);
else
terminal.setCursorPosition(cursorPosition.withRelativeColumn(1));
}
private synchronized boolean isEndOfRow(TerminalPosition cursorPosition) {
return cursorPosition.getColumn() >= terminalSize.getColumns() - 1;
}
private synchronized void moveCursorToNextRow(TerminalPosition cursorPosition) {
if (isEndOfBuffer(cursorPosition))
createNewRowForCursor(cursorPosition);
else
terminal.setCursorPosition(cursorPosition.withColumn(0).withRelativeRow(1));
}
private synchronized boolean isEndOfBuffer(TerminalPosition cursorPosition) {
return cursorPosition.getRow() == terminalSize.getRows() - 1;
}
private synchronized void createNewRowForCursor(TerminalPosition cursorPosition) {
terminal.setCursorPosition(cursorPosition);
terminal.putCharacter('\n');
terminal.setCursorPosition(cursorPosition.withColumn(0));
--originRow;
}
private synchronized void doEnter() { private synchronized void doEnter() {
moveCursorToEndOfInput(); moveCursorToEndOfInput();
terminal.putCharacter('\n'); terminal.putCharacter('\n');
@ -241,71 +168,122 @@ public class LispTerminal {
originRow = cursorPosition.getRow(); originRow = cursorPosition.getRow();
} }
private synchronized void doLeftArrow() {
TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveLeft(cursorPosition))
moveCursorLeft(cursorPosition);
}
private synchronized boolean isPossibleToMoveLeft(TerminalPosition cursorPosition) {
return getDistanceFromOrigin(cursorPosition) > 0;
}
private synchronized int getDistanceFromOrigin(TerminalPosition cursorPosition) {
int columnDifference = cursorPosition.getColumn() - originColumn;
int rowDifference = cursorPosition.getRow() - originRow;
int totalColumns = terminalSize.getColumns();
return columnDifference + (totalColumns * rowDifference);
}
private synchronized void moveCursorLeft(TerminalPosition cursorPosition) {
TerminalPosition newPosition = cursorPosition.withRelativeColumn(-1);
if (isAtStartOfRow(cursorPosition))
newPosition = cursorPosition.withColumn(terminalSize.getColumns()).withRelativeRow(-1);
terminal.setCursorPosition(newPosition);
}
private synchronized boolean isAtStartOfRow(TerminalPosition cursorPosition) {
return cursorPosition.getColumn() == 0;
}
private synchronized void doRightArrow() {
TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveRight(cursorPosition))
moveCursorRight(cursorPosition);
}
private synchronized boolean isPossibleToMoveRight(TerminalPosition cursorPosition) {
return getDistanceFromOrigin(cursorPosition) < inputLine.length();
}
private synchronized void moveCursorRight(TerminalPosition cursorPosition) {
if (isEndOfRow(cursorPosition))
moveCursorToNextRow(cursorPosition);
else
terminal.setCursorPosition(cursorPosition.withRelativeColumn(1));
}
private synchronized boolean isEndOfRow(TerminalPosition cursorPosition) {
return cursorPosition.getColumn() >= terminalSize.getColumns() - 1;
}
private synchronized void moveCursorToNextRow(TerminalPosition cursorPosition) {
if (isEndOfBuffer(cursorPosition))
createNewRowForCursor(cursorPosition);
else
terminal.setCursorPosition(cursorPosition.withColumn(0).withRelativeRow(1));
}
private synchronized boolean isEndOfBuffer(TerminalPosition cursorPosition) {
return cursorPosition.getRow() == terminalSize.getRows() - 1;
}
private synchronized void createNewRowForCursor(TerminalPosition cursorPosition) {
terminal.setCursorPosition(cursorPosition);
terminal.putCharacter('\n');
terminal.setCursorPosition(cursorPosition.withColumn(0));
--originRow;
}
private synchronized void doBackspace() { private synchronized void doBackspace() {
TerminalPosition cursorPosition = terminal.getCursorPosition(); TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveLeft(cursorPosition)) { if (isPossibleToMoveLeft(cursorPosition))
String remaining = inputLine.substring(getDistanceFromOrigin(cursorPosition), inputLine.length()); deletePreviousCharacter(cursorPosition);
inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition) - 1) + remaining; }
retractCursor(cursorPosition); private synchronized void deletePreviousCharacter(TerminalPosition cursorPosition) {
int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
String remaining = inputLine.substring(distanceFromOrigin, inputLine.length());
inputLine = inputLine.substring(0, distanceFromOrigin - 1) + remaining;
moveCursorLeft(cursorPosition);
putString(remaining + " ");
moveCursorLeft(cursorPosition);
}
for (char c : remaining.toCharArray()) private synchronized void putString(String characters) {
terminal.putCharacter(c); for (char c : characters.toCharArray())
terminal.putCharacter(c);
terminal.putCharacter(' ');
retractCursor(cursorPosition);
}
} }
private synchronized void doDelete() { private synchronized void doDelete() {
TerminalPosition cursorPosition = terminal.getCursorPosition(); TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveRight(cursorPosition)) { if (isPossibleToMoveRight(cursorPosition))
String remaining = inputLine.substring(getDistanceFromOrigin(cursorPosition) + 1, inputLine.length()); deleteCharacterAtPosition(cursorPosition);
inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition)) + remaining; }
for (char c : remaining.toCharArray()) private synchronized void deleteCharacterAtPosition(TerminalPosition cursorPosition) {
terminal.putCharacter(c); int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
String remaining = inputLine.substring(distanceFromOrigin + 1, inputLine.length());
terminal.putCharacter(' '); inputLine = inputLine.substring(0, distanceFromOrigin) + remaining;
terminal.setCursorPosition(cursorPosition); putString(remaining + " ");
} terminal.setCursorPosition(cursorPosition);
} }
private synchronized void doCharacter(Character character) { private synchronized void doCharacter(Character character) {
TerminalPosition cursorPosition = terminal.getCursorPosition(); TerminalPosition cursorPosition = terminal.getCursorPosition();
if (isPossibleToMoveRight(cursorPosition)) if (!isBufferFilled())
insertCharacter(character, cursorPosition); if (isPossibleToMoveRight(cursorPosition))
else insertCharacter(character, cursorPosition);
appendCharacter(character, cursorPosition); else
} appendCharacter(character, cursorPosition);
private synchronized void insertCharacter(Character character, TerminalPosition cursorPosition) {
if (!isBufferFilled()) {
int oldOriginRow = originRow;
advanceCursor(getLeadingEdge());
if (originRow != oldOriginRow) {
terminal.setCursorPosition(new TerminalPosition(originColumn, originRow));
for (char c : inputLine.toCharArray())
terminal.putCharacter(c);
cursorPosition = cursorPosition.withRelativeRow(-1);
}
terminal.setCursorPosition(cursorPosition);
int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
String remaining = character + inputLine.substring(distanceFromOrigin, inputLine.length());
inputLine = inputLine.substring(0, distanceFromOrigin) + remaining;
for (char c : remaining.toCharArray())
terminal.putCharacter(c);
advanceCursor(cursorPosition);
}
} }
private synchronized boolean isBufferFilled() { private synchronized boolean isBufferFilled() {
@ -315,10 +293,38 @@ public class LispTerminal {
return (row == terminalSize.getRows() - 1) && (column >= terminalSize.getColumns() - 1) && (originRow <= 0); return (row == terminalSize.getRows() - 1) && (column >= terminalSize.getColumns() - 1) && (originRow <= 0);
} }
private synchronized void insertCharacter(Character character, TerminalPosition cursorPosition) {
cursorPosition = shiftPositionIfNewRowAdded(cursorPosition);
terminal.setCursorPosition(cursorPosition);
int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
String remaining = character + inputLine.substring(distanceFromOrigin, inputLine.length());
inputLine = inputLine.substring(0, distanceFromOrigin) + remaining;
putString(remaining);
moveCursorRight(cursorPosition);
}
private synchronized TerminalPosition shiftPositionIfNewRowAdded(TerminalPosition cursorPosition) {
int oldOriginRow = originRow;
moveCursorRight(getLeadingEdge());
return isNewRowAdded(oldOriginRow) ? adjustCursorPosition(cursorPosition) : cursorPosition;
}
private synchronized boolean isNewRowAdded(int oldOriginRow) {
return originRow != oldOriginRow;
}
private synchronized TerminalPosition adjustCursorPosition(TerminalPosition cursorPosition) {
terminal.setCursorPosition(new TerminalPosition(originColumn, originRow));
putString(inputLine);
return cursorPosition.withRelativeRow(-1);
}
private synchronized void appendCharacter(Character character, TerminalPosition cursorPosition) { private synchronized void appendCharacter(Character character, TerminalPosition cursorPosition) {
terminal.putCharacter(character); terminal.putCharacter(character);
inputLine += character; inputLine += character;
advanceCursor(cursorPosition); moveCursorRight(cursorPosition);
} }
private void takeNap() { private void takeNap() {
@ -329,7 +335,7 @@ public class LispTerminal {
} }
} }
public void writeOutput() { private void writeOutput() {
for (int c = outputReader.read(); c != EOF; c = outputReader.read()) for (int c = outputReader.read(); c != EOF; c = outputReader.read())
processOutput((char) c); processOutput((char) c);
@ -342,7 +348,7 @@ public class LispTerminal {
if (isEscape(c)) if (isEscape(c))
parseControlSequence(); parseControlSequence();
else if (isEndOfSegment(c)) else if (isEndOfSegment(c))
printSegment(); writeSegment();
else else
outputSegment += c; outputSegment += c;
} }
@ -353,7 +359,7 @@ public class LispTerminal {
return c == END_OF_SEGMENT; return c == END_OF_SEGMENT;
} }
private synchronized void printSegment() { private synchronized void writeSegment() {
terminal.setCursorVisible(false); terminal.setCursorVisible(false);
printSegmentCharacters(); printSegmentCharacters();
terminal.setCursorVisible(true); terminal.setCursorVisible(true);
@ -363,10 +369,7 @@ public class LispTerminal {
private synchronized void printSegmentCharacters() { private synchronized void printSegmentCharacters() {
moveCursorToEndOfInput(); moveCursorToEndOfInput();
putString(outputSegment);
for (char c : outputSegment.toCharArray())
terminal.putCharacter(c);
moveCursorToNextRowIfNecessary(); moveCursorToNextRowIfNecessary();
terminal.flush(); terminal.flush();
} }
@ -378,7 +381,7 @@ public class LispTerminal {
moveCursorToNextRow(cursorPosition); moveCursorToNextRow(cursorPosition);
} }
public void finish() { public void stop() {
isFinished = true; isFinished = true;
inputWriter.close(); inputWriter.close();
} }

View File

@ -135,12 +135,12 @@ public class LispTerminalTest {
flushListener = new FlushListener(); flushListener = new FlushListener();
virtualTerminal.addVirtualTerminalListener(flushListener); virtualTerminal.addVirtualTerminalListener(flushListener);
lispTerminal = new LispTerminal(virtualTerminal, inputWriter, outputReader); lispTerminal = new LispTerminal(virtualTerminal, inputWriter, outputReader);
lispTerminal.run(); lispTerminal.start();
} }
@After @After
public void tearDown() throws IOException { public void tearDown() throws IOException {
lispTerminal.finish(); lispTerminal.stop();
outputWriter.close(); outputWriter.close();
} }
@ -425,6 +425,15 @@ public class LispTerminalTest {
assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', '1', '1' }, { '2', '2', ' ' } }); assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', '1', '1' }, { '2', '2', ' ' } });
} }
@Test
public void appendingTextDoesNothingWhenBufferFilled() {
setColumns(3);
setRows(3);
enterCharacters("000111222333444");
assertCursorPosition(2, 2);
assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', '1', '1' }, { '2', '2', ' ' } });
}
@Test @Test
public void printedOutputToEndOfRow_MovesCursorToNextRow() { public void printedOutputToEndOfRow_MovesCursorToNextRow() {
setColumns(3); setColumns(3);