Improve terminal performance
This commit is contained in:
		
							parent
							
								
									cef7be6c25
								
							
						
					
					
						commit
						38710e21bf
					
				@ -6,21 +6,24 @@ import static util.Characters.EOF;
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.util.concurrent.*;
 | 
			
		||||
 | 
			
		||||
import com.googlecode.lanterna.TerminalPosition;
 | 
			
		||||
import com.googlecode.lanterna.*;
 | 
			
		||||
import com.googlecode.lanterna.input.*;
 | 
			
		||||
import com.googlecode.lanterna.terminal.IOSafeTerminal;
 | 
			
		||||
import com.googlecode.lanterna.terminal.*;
 | 
			
		||||
 | 
			
		||||
import terminal.SafePipedStream.*;
 | 
			
		||||
 | 
			
		||||
public class LispTerminal {
 | 
			
		||||
 | 
			
		||||
    public static final char END_OF_SEGMENT = 'x';
 | 
			
		||||
 | 
			
		||||
    private IOSafeTerminal terminal;
 | 
			
		||||
    private PipedOutputStream inputWriter;
 | 
			
		||||
    private PipedInputStream outputReader;
 | 
			
		||||
    private SafePipedOutputStream inputWriter;
 | 
			
		||||
    private SafePipedInputStream outputReader;
 | 
			
		||||
    private ExecutorService executorService;
 | 
			
		||||
    private ControlSequenceHandler controlSequenceHandler;
 | 
			
		||||
    private TerminalPosition origin;
 | 
			
		||||
    private TerminalPosition leadingEdge;
 | 
			
		||||
    private TerminalSize terminalSize;
 | 
			
		||||
    private int originColumn;
 | 
			
		||||
    private int originRow;
 | 
			
		||||
    private String inputLine;
 | 
			
		||||
    private String outputSegment;
 | 
			
		||||
    private boolean isFinished;
 | 
			
		||||
@ -28,15 +31,32 @@ public class LispTerminal {
 | 
			
		||||
    public LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) {
 | 
			
		||||
        // FIXME - add resize handler
 | 
			
		||||
        this.terminal = terminal;
 | 
			
		||||
        this.inputWriter = inputWriter;
 | 
			
		||||
        this.outputReader = outputReader;
 | 
			
		||||
        this.inputWriter = new SafePipedOutputStream(inputWriter);
 | 
			
		||||
        this.outputReader = new SafePipedInputStream(outputReader);
 | 
			
		||||
        this.executorService = Executors.newFixedThreadPool(2);
 | 
			
		||||
        this.controlSequenceHandler = new ControlSequenceHandler();
 | 
			
		||||
        this.origin = terminal.getCursorPosition();
 | 
			
		||||
        this.leadingEdge = origin;
 | 
			
		||||
        this.terminalSize = terminal.getTerminalSize();
 | 
			
		||||
        this.originColumn = terminal.getCursorPosition().getColumn();
 | 
			
		||||
        this.originRow = terminal.getCursorPosition().getRow();
 | 
			
		||||
        this.inputLine = "";
 | 
			
		||||
        this.outputSegment = "";
 | 
			
		||||
        this.isFinished = false;
 | 
			
		||||
 | 
			
		||||
        addResizeListener();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void addResizeListener() {
 | 
			
		||||
        terminal.addResizeListener(new TerminalResizeListener() {
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onResized(Terminal terminal, TerminalSize newSize) {
 | 
			
		||||
                resize();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void resize() {
 | 
			
		||||
        terminalSize = terminal.getTerminalSize();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void run() {
 | 
			
		||||
@ -51,7 +71,11 @@ public class LispTerminal {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void processNextKey() {
 | 
			
		||||
        handleKey(getKeyStroke());
 | 
			
		||||
        KeyStroke keyStroke = getKeyStroke();
 | 
			
		||||
 | 
			
		||||
        if (keyStroke != null)
 | 
			
		||||
            handleKey(keyStroke);
 | 
			
		||||
        else
 | 
			
		||||
            takeNap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -61,8 +85,9 @@ public class LispTerminal {
 | 
			
		||||
        try {
 | 
			
		||||
            keyStroke = terminal.pollInput();
 | 
			
		||||
        } catch (IllegalStateException e) {
 | 
			
		||||
            // TODO - Issue #299
 | 
			
		||||
            // Issue #299
 | 
			
		||||
            terminal.putCharacter('\n');
 | 
			
		||||
            terminal.close();
 | 
			
		||||
            System.exit(0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -70,9 +95,6 @@ public class LispTerminal {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized void handleKey(KeyStroke keyStroke) {
 | 
			
		||||
        if (keyStroke == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        doKey(keyStroke);
 | 
			
		||||
        terminal.flush();
 | 
			
		||||
    }
 | 
			
		||||
@ -121,22 +143,31 @@ public class LispTerminal {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized boolean isPossibleToMoveLeft(TerminalPosition cursorPosition) {
 | 
			
		||||
        return distanceFromOrigin(cursorPosition) > 0;
 | 
			
		||||
        return getDistanceFromOrigin(cursorPosition) > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized int distanceFromOrigin(TerminalPosition cursorPosition) {
 | 
			
		||||
        int columnDifference = cursorPosition.getColumn() - origin.getColumn();
 | 
			
		||||
        int rowDifference = cursorPosition.getRow() - origin.getRow();
 | 
			
		||||
        int totalColumns = terminal.getTerminalSize().getColumns();
 | 
			
		||||
    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 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 retractCursor(TerminalPosition cursorPosition) {
 | 
			
		||||
        TerminalPosition newPosition = cursorPosition.withRelativeColumn(-1);
 | 
			
		||||
 | 
			
		||||
        if (isAtStartOfRow(cursorPosition))
 | 
			
		||||
            newPosition = cursorPosition.withColumn(terminal.getTerminalSize().getColumns()).withRelativeRow(-1);
 | 
			
		||||
            newPosition = cursorPosition.withColumn(terminalSize.getColumns()).withRelativeRow(-1);
 | 
			
		||||
 | 
			
		||||
        terminal.setCursorPosition(newPosition);
 | 
			
		||||
 | 
			
		||||
@ -154,7 +185,7 @@ public class LispTerminal {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized boolean isPossibleToMoveRight(TerminalPosition cursorPosition) {
 | 
			
		||||
        return distanceFromOrigin(cursorPosition) < inputLine.length();
 | 
			
		||||
        return getDistanceFromOrigin(cursorPosition) < inputLine.length();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized void advanceCursor(TerminalPosition cursorPosition) {
 | 
			
		||||
@ -162,16 +193,10 @@ public class LispTerminal {
 | 
			
		||||
            moveCursorToNextRow(cursorPosition);
 | 
			
		||||
        else
 | 
			
		||||
            terminal.setCursorPosition(cursorPosition.withRelativeColumn(1));
 | 
			
		||||
 | 
			
		||||
        TerminalPosition newPosition = terminal.getCursorPosition();
 | 
			
		||||
 | 
			
		||||
        // FIXME - use distance from origin to set leading edge
 | 
			
		||||
        if (distanceFromOrigin(newPosition) == inputLine.length())
 | 
			
		||||
            leadingEdge = terminal.getCursorPosition();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized boolean isAtEndOfRow(TerminalPosition cursorPosition) {
 | 
			
		||||
        return cursorPosition.getColumn() >= terminal.getTerminalSize().getColumns() - 1;
 | 
			
		||||
        return cursorPosition.getColumn() >= terminalSize.getColumns() - 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void moveCursorToNextRow(TerminalPosition cursorPosition) {
 | 
			
		||||
@ -182,40 +207,42 @@ public class LispTerminal {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isEndOfBuffer() {
 | 
			
		||||
        return terminal.getCursorPosition().getRow() == terminal.getTerminalSize().getRows() - 1;
 | 
			
		||||
        return terminal.getCursorPosition().getRow() == terminalSize.getRows() - 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createNewRowForCursor() {
 | 
			
		||||
        TerminalPosition originalPosition = terminal.getCursorPosition();
 | 
			
		||||
        terminal.putCharacter('\n');
 | 
			
		||||
        terminal.setCursorPosition(originalPosition.withColumn(0));
 | 
			
		||||
        origin = origin.withRelativeRow(-1);
 | 
			
		||||
        --originRow;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized void doEnter() {
 | 
			
		||||
        moveCursorToEndOfInput();
 | 
			
		||||
        terminal.putCharacter('\n');
 | 
			
		||||
        inputLine += "\n";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
        inputWriter.write(inputLine.getBytes());
 | 
			
		||||
        inputWriter.flush();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            // TODO Auto-generated catch block
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        inputLine = "";
 | 
			
		||||
        updateOrigin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        inputLine = "";
 | 
			
		||||
        origin = terminal.getCursorPosition();
 | 
			
		||||
        leadingEdge = origin;
 | 
			
		||||
    private synchronized void moveCursorToEndOfInput() {
 | 
			
		||||
        terminal.setCursorPosition(getLeadingEdge());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateOrigin() {
 | 
			
		||||
        TerminalPosition cursorPosition = terminal.getCursorPosition();
 | 
			
		||||
        originColumn = cursorPosition.getColumn();
 | 
			
		||||
        originRow = cursorPosition.getRow();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized void doBackspace() {
 | 
			
		||||
        TerminalPosition cursorPosition = terminal.getCursorPosition();
 | 
			
		||||
 | 
			
		||||
        if (isPossibleToMoveLeft(cursorPosition)) {
 | 
			
		||||
            String remaining = inputLine.substring(distanceFromOrigin(cursorPosition), inputLine.length());
 | 
			
		||||
            inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition) - 1) + remaining;
 | 
			
		||||
            String remaining = inputLine.substring(getDistanceFromOrigin(cursorPosition), inputLine.length());
 | 
			
		||||
            inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition) - 1) + remaining;
 | 
			
		||||
 | 
			
		||||
            retractCursor(cursorPosition);
 | 
			
		||||
 | 
			
		||||
@ -225,10 +252,6 @@ public class LispTerminal {
 | 
			
		||||
            terminal.putCharacter(' ');
 | 
			
		||||
 | 
			
		||||
            retractCursor(cursorPosition);
 | 
			
		||||
 | 
			
		||||
            // FIXME - use distance from origin to set leading edge
 | 
			
		||||
            if (distanceFromOrigin(leadingEdge) > inputLine.length())
 | 
			
		||||
                leadingEdge = terminal.getCursorPosition();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -236,18 +259,14 @@ public class LispTerminal {
 | 
			
		||||
        TerminalPosition cursorPosition = terminal.getCursorPosition();
 | 
			
		||||
 | 
			
		||||
        if (isPossibleToMoveRight(cursorPosition)) {
 | 
			
		||||
            String remaining = inputLine.substring(distanceFromOrigin(cursorPosition) + 1, inputLine.length());
 | 
			
		||||
            inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition)) + remaining;
 | 
			
		||||
            String remaining = inputLine.substring(getDistanceFromOrigin(cursorPosition) + 1, inputLine.length());
 | 
			
		||||
            inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition)) + remaining;
 | 
			
		||||
 | 
			
		||||
            for (char c : remaining.toCharArray())
 | 
			
		||||
                terminal.putCharacter(c);
 | 
			
		||||
 | 
			
		||||
            terminal.putCharacter(' ');
 | 
			
		||||
            terminal.setCursorPosition(cursorPosition);
 | 
			
		||||
 | 
			
		||||
            // FIXME - use distance from origin to set leading edge
 | 
			
		||||
            if (distanceFromOrigin(leadingEdge) > inputLine.length())
 | 
			
		||||
                leadingEdge = terminal.getCursorPosition();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -256,8 +275,8 @@ public class LispTerminal {
 | 
			
		||||
 | 
			
		||||
        if (isPossibleToMoveRight(cursorPosition)) {
 | 
			
		||||
            String remaining = keyStroke.getCharacter()
 | 
			
		||||
                               + inputLine.substring(distanceFromOrigin(cursorPosition), inputLine.length());
 | 
			
		||||
            inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition)) + remaining;
 | 
			
		||||
                               + inputLine.substring(getDistanceFromOrigin(cursorPosition), inputLine.length());
 | 
			
		||||
            inputLine = inputLine.substring(0, getDistanceFromOrigin(cursorPosition)) + remaining;
 | 
			
		||||
 | 
			
		||||
            // FIXME - must have a way to push remainder on to a new line at the end of the buffer
 | 
			
		||||
 | 
			
		||||
@ -282,13 +301,8 @@ public class LispTerminal {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void writeOutput() {
 | 
			
		||||
        try {
 | 
			
		||||
        for (int c = outputReader.read(); c != EOF; c = outputReader.read())
 | 
			
		||||
            processOutput((char) c);
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            // TODO Auto-generated catch block
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        terminal.setCursorVisible(true);
 | 
			
		||||
        terminal.flush();
 | 
			
		||||
@ -321,12 +335,7 @@ public class LispTerminal {
 | 
			
		||||
        terminal.flush();
 | 
			
		||||
        terminal.setCursorVisible(true);
 | 
			
		||||
        outputSegment = "";
 | 
			
		||||
        origin = terminal.getCursorPosition();
 | 
			
		||||
        leadingEdge = origin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized void moveCursorToEndOfInput() {
 | 
			
		||||
        terminal.setCursorPosition(leadingEdge);
 | 
			
		||||
        updateOrigin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized void moveCursorToNextRowIfNecessary() {
 | 
			
		||||
@ -338,13 +347,7 @@ public class LispTerminal {
 | 
			
		||||
 | 
			
		||||
    public void finish() {
 | 
			
		||||
        isFinished = true;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
        inputWriter.close();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            // TODO Auto-generated catch block
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										81
									
								
								src/terminal/SafePipedStream.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/terminal/SafePipedStream.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
			
		||||
package terminal;
 | 
			
		||||
 | 
			
		||||
import java.io.*;
 | 
			
		||||
 | 
			
		||||
public interface SafePipedStream {
 | 
			
		||||
 | 
			
		||||
    public static class SafePipedInputStream {
 | 
			
		||||
 | 
			
		||||
        private PipedInputStream underlyingStream;
 | 
			
		||||
 | 
			
		||||
        public SafePipedInputStream(PipedInputStream underlyingStream) {
 | 
			
		||||
            this.underlyingStream = underlyingStream;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int read() {
 | 
			
		||||
            try {
 | 
			
		||||
                return underlyingStream.read();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                throw new UncheckedIOException(e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void close() {
 | 
			
		||||
            try {
 | 
			
		||||
                underlyingStream.close();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                throw new UncheckedIOException(e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class SafePipedOutputStream {
 | 
			
		||||
 | 
			
		||||
        private PipedOutputStream underlyingStream;
 | 
			
		||||
 | 
			
		||||
        public SafePipedOutputStream(PipedOutputStream underlyingStream) {
 | 
			
		||||
            this.underlyingStream = underlyingStream;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void write(byte[] b) {
 | 
			
		||||
            try {
 | 
			
		||||
                underlyingStream.write(b);
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                throw new UncheckedIOException(e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void flush() {
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                underlyingStream.flush();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                throw new UncheckedIOException(e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void close() {
 | 
			
		||||
            try {
 | 
			
		||||
                underlyingStream.close();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                throw new UncheckedIOException(e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class UncheckedIOException extends RuntimeException {
 | 
			
		||||
 | 
			
		||||
        private static final long serialVersionUID = 1L;
 | 
			
		||||
        private IOException exception;
 | 
			
		||||
 | 
			
		||||
        public UncheckedIOException(IOException exception) {
 | 
			
		||||
            this.exception = exception;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public String getMessage() {
 | 
			
		||||
            return exception.getMessage();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -26,6 +26,13 @@ public class LispTerminalTest {
 | 
			
		||||
        waitForFlushes(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void pressKeyTimes(KeyType keyType, int times) {
 | 
			
		||||
        for (int i = times; i > 0; i--)
 | 
			
		||||
            virtualTerminal.addInput(new KeyStroke(keyType));
 | 
			
		||||
 | 
			
		||||
        waitForFlushes(times);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void pressControlKey(KeyType keyType) {
 | 
			
		||||
        virtualTerminal.addInput(new KeyStroke(keyType, true, false));
 | 
			
		||||
        waitForFlushes(1);
 | 
			
		||||
@ -56,7 +63,7 @@ public class LispTerminalTest {
 | 
			
		||||
            outputWriter.write(END_OF_SEGMENT);
 | 
			
		||||
            outputWriter.flush();
 | 
			
		||||
            waitForFlushes(1);
 | 
			
		||||
        } catch (IOException e) {}
 | 
			
		||||
        } catch (IOException ignored) {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void waitForFlushes(int flushCount) {
 | 
			
		||||
@ -67,7 +74,7 @@ public class LispTerminalTest {
 | 
			
		||||
 | 
			
		||||
                flushListener.resetFlushCount();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (InterruptedException e) {}
 | 
			
		||||
        } catch (InterruptedException ignored) {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setColumns(int columns) {
 | 
			
		||||
@ -85,7 +92,7 @@ public class LispTerminalTest {
 | 
			
		||||
 | 
			
		||||
    private void assertCharacterAtPosition(char character, int column, int row) {
 | 
			
		||||
        TerminalPosition position = new TerminalPosition(column, row);
 | 
			
		||||
        assertEquals(character, virtualTerminal.getCharacter(position).getCharacter());
 | 
			
		||||
        assertEquals(String.valueOf(character), String.valueOf(virtualTerminal.getCharacter(position).getCharacter()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void assertInputWritten(String expected) {
 | 
			
		||||
@ -97,7 +104,7 @@ public class LispTerminalTest {
 | 
			
		||||
            for (int c = inputReader.read(); c != -1; c = inputReader.read()) {
 | 
			
		||||
                actual += (char) c;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (IOException e) {}
 | 
			
		||||
        } catch (IOException ignored) {}
 | 
			
		||||
 | 
			
		||||
        assertEquals(expected, actual);
 | 
			
		||||
    }
 | 
			
		||||
@ -106,7 +113,13 @@ public class LispTerminalTest {
 | 
			
		||||
        try {
 | 
			
		||||
            inputWriter.write(0);
 | 
			
		||||
            fail("input stream not closed");
 | 
			
		||||
        } catch (IOException e) {}
 | 
			
		||||
        } catch (IOException ignored) {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void assertCharacterPositions(char[][] positions) {
 | 
			
		||||
        for (int row = 0; row < positions.length; row++)
 | 
			
		||||
            for (int column = 0; column < positions[row].length; column++)
 | 
			
		||||
                assertCharacterAtPosition(positions[row][column], column, row);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Before
 | 
			
		||||
@ -153,8 +166,7 @@ public class LispTerminalTest {
 | 
			
		||||
        setColumns(5);
 | 
			
		||||
        enterCharacters("123451");
 | 
			
		||||
        assertCursorPosition(1, 1);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 2);
 | 
			
		||||
        assertCursorPosition(4, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -181,13 +193,9 @@ public class LispTerminalTest {
 | 
			
		||||
        setColumns(5);
 | 
			
		||||
        enterCharacters("123451");
 | 
			
		||||
        assertCursorPosition(1, 1);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 3);
 | 
			
		||||
        assertCursorPosition(3, 0);
 | 
			
		||||
        pressKey(KeyType.ArrowRight);
 | 
			
		||||
        pressKey(KeyType.ArrowRight);
 | 
			
		||||
        pressKey(KeyType.ArrowRight);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowRight, 3);
 | 
			
		||||
        assertCursorPosition(1, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -201,8 +209,7 @@ public class LispTerminalTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void characterIsInserted() {
 | 
			
		||||
        enterCharacters("abcd");
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 2);
 | 
			
		||||
        enterCharacter('x');
 | 
			
		||||
        assertCharacterAtPosition('a', 0, 0);
 | 
			
		||||
        assertCharacterAtPosition('b', 1, 0);
 | 
			
		||||
@ -215,8 +222,7 @@ public class LispTerminalTest {
 | 
			
		||||
    public void characterIsInserted_PushesInputToNextRow() {
 | 
			
		||||
        setColumns(4);
 | 
			
		||||
        enterCharacters("abcd");
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 2);
 | 
			
		||||
        enterCharacter('x');
 | 
			
		||||
        assertCharacterAtPosition('a', 0, 0);
 | 
			
		||||
        assertCharacterAtPosition('b', 1, 0);
 | 
			
		||||
@ -234,8 +240,7 @@ public class LispTerminalTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void backspaceWorksAfterInput() {
 | 
			
		||||
        enterCharacters("12345");
 | 
			
		||||
        pressKey(KeyType.Backspace);
 | 
			
		||||
        pressKey(KeyType.Backspace);
 | 
			
		||||
        pressKeyTimes(KeyType.Backspace, 2);
 | 
			
		||||
        assertCursorPosition(3, 0);
 | 
			
		||||
        assertCharacterAtPosition('1', 0, 0);
 | 
			
		||||
        assertCharacterAtPosition('2', 1, 0);
 | 
			
		||||
@ -249,11 +254,7 @@ public class LispTerminalTest {
 | 
			
		||||
    public void backspaceWorksAcrossRow() {
 | 
			
		||||
        setColumns(4);
 | 
			
		||||
        enterCharacters("1234567");
 | 
			
		||||
        pressKey(KeyType.Backspace);
 | 
			
		||||
        pressKey(KeyType.Backspace);
 | 
			
		||||
        pressKey(KeyType.Backspace);
 | 
			
		||||
        pressKey(KeyType.Backspace);
 | 
			
		||||
        pressKey(KeyType.Backspace);
 | 
			
		||||
        pressKeyTimes(KeyType.Backspace, 5);
 | 
			
		||||
        assertCursorPosition(2, 0);
 | 
			
		||||
        assertCharacterAtPosition('1', 0, 0);
 | 
			
		||||
        assertCharacterAtPosition('2', 1, 0);
 | 
			
		||||
@ -267,8 +268,7 @@ public class LispTerminalTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void backspaceWorksInMiddleOfInput() {
 | 
			
		||||
        enterCharacters("12345");
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 2);
 | 
			
		||||
        pressKey(KeyType.Backspace);
 | 
			
		||||
        assertCursorPosition(2, 0);
 | 
			
		||||
        assertCharacterAtPosition('1', 0, 0);
 | 
			
		||||
@ -296,12 +296,8 @@ public class LispTerminalTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void deleteWorksAtStartOfInput() {
 | 
			
		||||
        enterCharacters("del");
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.Delete);
 | 
			
		||||
        pressKey(KeyType.Delete);
 | 
			
		||||
        pressKey(KeyType.Delete);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 3);
 | 
			
		||||
        pressKeyTimes(KeyType.Delete, 3);
 | 
			
		||||
        assertCursorPosition(0, 0);
 | 
			
		||||
        assertCharacterAtPosition(' ', 0, 0);
 | 
			
		||||
        assertCharacterAtPosition(' ', 1, 0);
 | 
			
		||||
@ -312,11 +308,7 @@ public class LispTerminalTest {
 | 
			
		||||
    public void deleteWorksAcrossRow() {
 | 
			
		||||
        setColumns(4);
 | 
			
		||||
        enterCharacters("delete");
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 5);
 | 
			
		||||
        pressKey(KeyType.Delete);
 | 
			
		||||
        assertCursorPosition(1, 0);
 | 
			
		||||
        assertCharacterAtPosition('d', 0, 0);
 | 
			
		||||
@ -343,8 +335,7 @@ public class LispTerminalTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void enterPressedInMiddleOfInput_WritesEntireLineToPipedStream() {
 | 
			
		||||
        enterCharacters("enter");
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 2);
 | 
			
		||||
        pressKey(KeyType.Enter);
 | 
			
		||||
        assertInputWritten("enter\n");
 | 
			
		||||
    }
 | 
			
		||||
@ -360,8 +351,7 @@ public class LispTerminalTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void controlDWorksInMiddleOfInput() {
 | 
			
		||||
        enterCharacters("control-d");
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 2);
 | 
			
		||||
        enterControlCharacter('d');
 | 
			
		||||
        assertInputStreamClosed();
 | 
			
		||||
        assertInputWritten("control-d\n");
 | 
			
		||||
@ -392,21 +382,14 @@ public class LispTerminalTest {
 | 
			
		||||
    public void outputIsWritten() {
 | 
			
		||||
        produceOutput("output");
 | 
			
		||||
        assertCursorPosition(6, 0);
 | 
			
		||||
        assertCharacterAtPosition('o', 0, 0);
 | 
			
		||||
        assertCharacterAtPosition('u', 1, 0);
 | 
			
		||||
        assertCharacterAtPosition('t', 2, 0);
 | 
			
		||||
        assertCharacterAtPosition('p', 3, 0);
 | 
			
		||||
        assertCharacterAtPosition('u', 4, 0);
 | 
			
		||||
        assertCharacterAtPosition('t', 5, 0);
 | 
			
		||||
        assertCharacterPositions(new char[][] { { 'o', 'u', 't', 'p', 'u', 't' } });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void endOfSegmentCharacterIsNotPrinted() {
 | 
			
		||||
        produceOutput("> " + END_OF_SEGMENT);
 | 
			
		||||
        assertCursorPosition(2, 0);
 | 
			
		||||
        assertCharacterAtPosition('>', 0, 0);
 | 
			
		||||
        assertCharacterAtPosition(' ', 1, 0);
 | 
			
		||||
        assertCharacterAtPosition(' ', 2, 0);
 | 
			
		||||
        assertCharacterPositions(new char[][] { { '>', ' ', ' ' } });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -415,29 +398,18 @@ public class LispTerminalTest {
 | 
			
		||||
        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);
 | 
			
		||||
        assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', ' ' } });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
//    @Test
 | 
			
		||||
//    public void insertingTextPushesInputPastEndOfBuffer() {
 | 
			
		||||
//        setColumns(2);
 | 
			
		||||
//        setColumns(3);
 | 
			
		||||
//        setRows(3);
 | 
			
		||||
//        enterCharacters("00112");
 | 
			
		||||
//
 | 
			
		||||
//        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
//        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
//        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
//        enterCharacters("00011122");
 | 
			
		||||
//        pressKeyTimes(KeyType.ArrowLeft, 4);
 | 
			
		||||
//        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);
 | 
			
		||||
//        assertCharacterPositions(new char[][] { { '1', 'z', 'z' }, { '1', '1', '2' }, { '2', ' ', ' ' } });
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -453,64 +425,30 @@ public class LispTerminalTest {
 | 
			
		||||
        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);
 | 
			
		||||
        assertCharacterPositions(new char[][] { { 'p', 'u', 't' }, { ' ', ' ', ' ' } });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void printedOutputDoesNotOverwriteInput() {
 | 
			
		||||
        setColumns(3);
 | 
			
		||||
        enterCharacters("01201201");
 | 
			
		||||
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 5);
 | 
			
		||||
        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);
 | 
			
		||||
        assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', '2' }, { '0', '1', 'o' },
 | 
			
		||||
                                                { 'u', 't', ' ' } });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void printedOutputDoesNotOverwriteInput_AfterEnter() {
 | 
			
		||||
        setColumns(3);
 | 
			
		||||
        enterCharacters("01201201");
 | 
			
		||||
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKey(KeyType.ArrowLeft);
 | 
			
		||||
        pressKeyTimes(KeyType.ArrowLeft, 5);
 | 
			
		||||
        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);
 | 
			
		||||
        assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', '2' }, { '0', '1', ' ' },
 | 
			
		||||
                                                { 'o', 'u', 't' } });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user