Add colors to the interactive interpreter
This commit is contained in:
parent
058e937c3e
commit
b298e118e3
|
@ -1,7 +1,5 @@
|
||||||
package interpreter;
|
package interpreter;
|
||||||
|
|
||||||
import sexpression.SExpression;
|
|
||||||
|
|
||||||
public class InteractiveLispInterpreter extends LispInterpreter {
|
public class InteractiveLispInterpreter extends LispInterpreter {
|
||||||
|
|
||||||
private static final String PROMPT = "~ ";
|
private static final String PROMPT = "~ ";
|
||||||
|
@ -13,9 +11,9 @@ public class InteractiveLispInterpreter extends LispInterpreter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void printSExpression(SExpression sExpression) {
|
protected void evaluateAndPrintNextSExpression() {
|
||||||
environment.getOutput().println();
|
environment.getOutput().println();
|
||||||
super.printSExpression(sExpression);
|
super.evaluateAndPrintNextSExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class LispInterpreter {
|
||||||
|
|
||||||
protected void prompt() {}
|
protected void prompt() {}
|
||||||
|
|
||||||
private void evaluateAndPrintNextSExpression() {
|
protected void evaluateAndPrintNextSExpression() {
|
||||||
try {
|
try {
|
||||||
evaluateAndPrintNextSExpressionWithException();
|
evaluateAndPrintNextSExpressionWithException();
|
||||||
} catch (LispException e) {
|
} catch (LispException e) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import com.googlecode.lanterna.terminal.*;
|
||||||
|
|
||||||
import interpreter.*;
|
import interpreter.*;
|
||||||
import terminal.LispTerminal;
|
import terminal.LispTerminal;
|
||||||
import terminal.SafeStream.SafePipedOutputStream;
|
import terminal.SafeStream.SafeOutputStream;
|
||||||
import terminal.SafeStream.UncheckedIOException;
|
import terminal.SafeStream.UncheckedIOException;
|
||||||
|
|
||||||
public class LispMain {
|
public class LispMain {
|
||||||
|
@ -35,7 +35,7 @@ public class LispMain {
|
||||||
private PipedInputStream outputReader;
|
private PipedInputStream outputReader;
|
||||||
private PipedOutputStream outputWriter;
|
private PipedOutputStream outputWriter;
|
||||||
private PrintStream outputStream;
|
private PrintStream outputStream;
|
||||||
private SafePipedOutputStream safeOutputWriter;
|
private SafeOutputStream safeOutputWriter;
|
||||||
private LispTerminal lispTerminal;
|
private LispTerminal lispTerminal;
|
||||||
|
|
||||||
private void runInteractive() {
|
private void runInteractive() {
|
||||||
|
@ -60,7 +60,7 @@ public class LispMain {
|
||||||
outputReader = new PipedInputStream();
|
outputReader = new PipedInputStream();
|
||||||
outputWriter = new PipedOutputStream(outputReader);
|
outputWriter = new PipedOutputStream(outputReader);
|
||||||
outputStream = new PrintStream(outputWriter);
|
outputStream = new PrintStream(outputWriter);
|
||||||
safeOutputWriter = new SafePipedOutputStream(outputWriter);
|
safeOutputWriter = new SafeOutputStream(outputWriter);
|
||||||
lispTerminal = new LispTerminal(createIOSafeTerminal(), inputWriter, outputReader);
|
lispTerminal = new LispTerminal(createIOSafeTerminal(), inputWriter, outputReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,11 +82,11 @@ public class LispMain {
|
||||||
builder.setErrorOutput(outputStream);
|
builder.setErrorOutput(outputStream);
|
||||||
builder.setTerminationFunction(this::shutdown);
|
builder.setTerminationFunction(this::shutdown);
|
||||||
builder.setErrorTerminationFunction(this::shutdown);
|
builder.setErrorTerminationFunction(this::shutdown);
|
||||||
builder.setPromptDecorator(makeSegmentDecorator(ANSI_GREEN));
|
builder.setPromptDecorator(s -> s + END_OF_SEGMENT);
|
||||||
builder.setValueOutputDecorator(s -> s);
|
builder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN));
|
||||||
builder.setWarningOutputDecorator(s -> s);
|
builder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW));
|
||||||
builder.setErrorOutputDecorator(s -> s);
|
builder.setErrorOutputDecorator(makeColorDecorator(ANSI_RED));
|
||||||
builder.setCriticalOutputDecorator(s -> s);
|
builder.setCriticalOutputDecorator(makeColorDecorator(ANSI_PURPLE));
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
@ -98,16 +98,6 @@ public class LispMain {
|
||||||
safeOutputWriter.close();
|
safeOutputWriter.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Function<String, String> makeSegmentDecorator(String color) {
|
|
||||||
return new Function<String, String>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String s) {
|
|
||||||
return s + END_OF_SEGMENT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runWithFile(String fileName) {
|
private void runWithFile(String fileName) {
|
||||||
buildFileInterpreter(fileName).interpret();
|
buildFileInterpreter(fileName).interpret();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package terminal;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.terminal.IOSafeTerminal;
|
||||||
|
|
||||||
|
public interface ControlSequence {
|
||||||
|
|
||||||
|
default String getCode() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
default void applyTo(IOSafeTerminal terminal) {}
|
||||||
|
|
||||||
|
public static class NullControlSequence implements ControlSequence {}
|
||||||
|
}
|
|
@ -1,25 +1,63 @@
|
||||||
package terminal;
|
package terminal;
|
||||||
|
|
||||||
import static terminal.ControlSequenceHandler.Command.SGR;
|
import static java.lang.Character.isDigit;
|
||||||
|
import static terminal.ControlSequenceLookup.lookupControlSequence;
|
||||||
|
import static util.Characters.*;
|
||||||
|
|
||||||
|
import terminal.SafeStream.SafeInputStream;
|
||||||
|
|
||||||
class ControlSequenceHandler {
|
class ControlSequenceHandler {
|
||||||
|
|
||||||
|
private static final char ESCAPE = '\u001B';
|
||||||
|
|
||||||
public static final boolean isEscape(char c) {
|
public static final boolean isEscape(char c) {
|
||||||
return c == '\u001B';
|
return c == ESCAPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean inControlSequence;
|
private SafeInputStream input;
|
||||||
private int code;
|
private String code;
|
||||||
private Command command;
|
private int currentCharacter;
|
||||||
|
|
||||||
public ControlSequenceHandler() {
|
public ControlSequenceHandler() {
|
||||||
this.inControlSequence = false;
|
this.input = null;
|
||||||
this.code = 0;
|
this.code = "";
|
||||||
this.command = SGR;
|
this.currentCharacter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enum Command {
|
public ControlSequence parse(SafeInputStream inputStream) {
|
||||||
SGR
|
input = inputStream;
|
||||||
|
code = "";
|
||||||
|
|
||||||
|
readCharacter();
|
||||||
|
if (isExpectedFirstCharacter())
|
||||||
|
readCode();
|
||||||
|
|
||||||
|
return lookupControlSequence((char) currentCharacter, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readCharacter() {
|
||||||
|
currentCharacter = input.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExpectedFirstCharacter() {
|
||||||
|
return isCharacter() && isLeftBracket();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCharacter() {
|
||||||
|
return currentCharacter != EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLeftBracket() {
|
||||||
|
return (char) currentCharacter == LEFT_SQUARE_BRACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readCode() {
|
||||||
|
for (readCharacter(); isPartOfCode(); readCharacter())
|
||||||
|
code += (char) currentCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPartOfCode() {
|
||||||
|
return isCharacter() && isDigit((char) currentCharacter);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package terminal;
|
||||||
|
|
||||||
|
import static terminal.SelectGraphicRendition.SGR_COMMAND;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import terminal.ControlSequence.NullControlSequence;
|
||||||
|
|
||||||
|
public class ControlSequenceLookup {
|
||||||
|
|
||||||
|
private static Map<Character, Map<String, ControlSequence>> controlSequenceMap = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map<String, ControlSequence> selectGraphicRenditionMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (SelectGraphicRendition sgr : SelectGraphicRendition.values())
|
||||||
|
selectGraphicRenditionMap.put(sgr.getCode(), sgr);
|
||||||
|
|
||||||
|
controlSequenceMap.put(SGR_COMMAND, selectGraphicRenditionMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ControlSequence lookupControlSequence(char command, String code) {
|
||||||
|
Map<String, ControlSequence> commandMap = controlSequenceMap.getOrDefault(command, new HashMap<>());
|
||||||
|
|
||||||
|
return commandMap.getOrDefault(code, new NullControlSequence());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,10 +18,10 @@ public class LispTerminal {
|
||||||
public static final char END_OF_SEGMENT = 'x';
|
public static final char END_OF_SEGMENT = 'x';
|
||||||
|
|
||||||
private IOSafeTerminal terminal;
|
private IOSafeTerminal terminal;
|
||||||
private SafePipedOutputStream inputWriter;
|
private SafeOutputStream inputWriter;
|
||||||
private SafePipedInputStream outputReader;
|
private SafeInputStream outputReader;
|
||||||
private ExecutorService executorService;
|
|
||||||
private ControlSequenceHandler controlSequenceHandler;
|
private ControlSequenceHandler controlSequenceHandler;
|
||||||
|
private ExecutorService executorService;
|
||||||
private TerminalSize terminalSize;
|
private TerminalSize terminalSize;
|
||||||
private String inputLine;
|
private String inputLine;
|
||||||
private String outputSegment;
|
private String outputSegment;
|
||||||
|
@ -31,20 +31,20 @@ public class LispTerminal {
|
||||||
|
|
||||||
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 SafeOutputStream(inputWriter);
|
||||||
this.outputReader = new SafePipedInputStream(outputReader);
|
this.outputReader = new SafeInputStream(outputReader);
|
||||||
this.executorService = Executors.newFixedThreadPool(2);
|
|
||||||
this.controlSequenceHandler = new ControlSequenceHandler();
|
this.controlSequenceHandler = new ControlSequenceHandler();
|
||||||
|
this.executorService = Executors.newFixedThreadPool(2);
|
||||||
this.terminalSize = terminal.getTerminalSize();
|
this.terminalSize = terminal.getTerminalSize();
|
||||||
this.inputLine = "";
|
this.inputLine = "";
|
||||||
this.outputSegment = "";
|
this.outputSegment = "";
|
||||||
this.isStopped = false;
|
this.isStopped = false;
|
||||||
|
|
||||||
updateOrigin();
|
setOriginToCurrentPosition();
|
||||||
terminal.addResizeListener((t, newSize) -> resize());
|
terminal.addResizeListener((t, newSize) -> resize());
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void updateOrigin() {
|
private synchronized void setOriginToCurrentPosition() {
|
||||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||||
originColumn = cursorPosition.getColumn();
|
originColumn = cursorPosition.getColumn();
|
||||||
originRow = cursorPosition.getRow();
|
originRow = cursorPosition.getRow();
|
||||||
|
@ -54,12 +54,12 @@ public class LispTerminal {
|
||||||
terminalSize = terminal.getTerminalSize();
|
terminalSize = terminal.getTerminalSize();
|
||||||
terminal.clearScreen();
|
terminal.clearScreen();
|
||||||
terminal.setCursorPosition(0, 0);
|
terminal.setCursorPosition(0, 0);
|
||||||
updateOrigin();
|
setOriginToCurrentPosition();
|
||||||
putString(inputLine);
|
putStringToTerminal(inputLine);
|
||||||
moveCursorToEndOfInput();
|
moveCursorToEndOfInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void putString(String characters) {
|
private synchronized void putStringToTerminal(String characters) {
|
||||||
for (char c : characters.toCharArray())
|
for (char c : characters.toCharArray())
|
||||||
terminal.putCharacter(c);
|
terminal.putCharacter(c);
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ public class LispTerminal {
|
||||||
private void doControlC() {
|
private void doControlC() {
|
||||||
moveCursorToEndOfInput();
|
moveCursorToEndOfInput();
|
||||||
terminal.putCharacter('\n');
|
terminal.putCharacter('\n');
|
||||||
updateOrigin();
|
setOriginToCurrentPosition();
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ public class LispTerminal {
|
||||||
inputWriter.write(inputLine.getBytes());
|
inputWriter.write(inputLine.getBytes());
|
||||||
inputWriter.flush();
|
inputWriter.flush();
|
||||||
inputLine = "";
|
inputLine = "";
|
||||||
updateOrigin();
|
setOriginToCurrentPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void doLeftArrow() {
|
private synchronized void doLeftArrow() {
|
||||||
|
@ -257,7 +257,7 @@ public class LispTerminal {
|
||||||
String remaining = inputLine.substring(distanceFromOrigin, inputLine.length());
|
String remaining = inputLine.substring(distanceFromOrigin, inputLine.length());
|
||||||
inputLine = inputLine.substring(0, distanceFromOrigin - 1) + remaining;
|
inputLine = inputLine.substring(0, distanceFromOrigin - 1) + remaining;
|
||||||
moveCursorLeft(cursorPosition);
|
moveCursorLeft(cursorPosition);
|
||||||
putString(remaining + " ");
|
putStringToTerminal(remaining + " ");
|
||||||
moveCursorLeft(cursorPosition);
|
moveCursorLeft(cursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ public class LispTerminal {
|
||||||
int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
|
int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
|
||||||
String remaining = inputLine.substring(distanceFromOrigin + 1, inputLine.length());
|
String remaining = inputLine.substring(distanceFromOrigin + 1, inputLine.length());
|
||||||
inputLine = inputLine.substring(0, distanceFromOrigin) + remaining;
|
inputLine = inputLine.substring(0, distanceFromOrigin) + remaining;
|
||||||
putString(remaining + " ");
|
putStringToTerminal(remaining + " ");
|
||||||
terminal.setCursorPosition(cursorPosition);
|
terminal.setCursorPosition(cursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ public class LispTerminal {
|
||||||
int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
|
int distanceFromOrigin = getDistanceFromOrigin(cursorPosition);
|
||||||
String remaining = character + inputLine.substring(distanceFromOrigin, inputLine.length());
|
String remaining = character + inputLine.substring(distanceFromOrigin, inputLine.length());
|
||||||
inputLine = inputLine.substring(0, distanceFromOrigin) + remaining;
|
inputLine = inputLine.substring(0, distanceFromOrigin) + remaining;
|
||||||
putString(remaining);
|
putStringToTerminal(remaining);
|
||||||
moveCursorRight(cursorPosition);
|
moveCursorRight(cursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ public class LispTerminal {
|
||||||
|
|
||||||
private synchronized TerminalPosition adjustCursorPosition(TerminalPosition cursorPosition) {
|
private synchronized TerminalPosition adjustCursorPosition(TerminalPosition cursorPosition) {
|
||||||
terminal.setCursorPosition(new TerminalPosition(originColumn, originRow));
|
terminal.setCursorPosition(new TerminalPosition(originColumn, originRow));
|
||||||
putString(inputLine);
|
putStringToTerminal(inputLine);
|
||||||
|
|
||||||
return cursorPosition.withRelativeRow(-1);
|
return cursorPosition.withRelativeRow(-1);
|
||||||
}
|
}
|
||||||
|
@ -343,16 +343,12 @@ public class LispTerminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void processOutput(char c) {
|
private synchronized void processOutput(char c) {
|
||||||
if (isEscape(c))
|
if (isEndOfSegment(c))
|
||||||
parseControlSequence();
|
|
||||||
else if (isEndOfSegment(c))
|
|
||||||
writeSegment();
|
writeSegment();
|
||||||
else
|
else
|
||||||
outputSegment += c;
|
outputSegment += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void parseControlSequence() {}
|
|
||||||
|
|
||||||
private synchronized boolean isEndOfSegment(char c) {
|
private synchronized boolean isEndOfSegment(char c) {
|
||||||
return c == END_OF_SEGMENT;
|
return c == END_OF_SEGMENT;
|
||||||
}
|
}
|
||||||
|
@ -362,16 +358,31 @@ public class LispTerminal {
|
||||||
printSegmentCharacters();
|
printSegmentCharacters();
|
||||||
terminal.setCursorVisible(true);
|
terminal.setCursorVisible(true);
|
||||||
outputSegment = "";
|
outputSegment = "";
|
||||||
updateOrigin();
|
setOriginToCurrentPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void printSegmentCharacters() {
|
private synchronized void printSegmentCharacters() {
|
||||||
moveCursorToEndOfInput();
|
moveCursorToEndOfInput();
|
||||||
putString(outputSegment);
|
putOutputToTerminal();
|
||||||
moveCursorToNextRowIfNecessary();
|
moveCursorToNextRowIfNecessary();
|
||||||
terminal.flush();
|
terminal.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized void putOutputToTerminal() {
|
||||||
|
SafeInputStream input = new SafeInputStream(new ByteArrayInputStream(outputSegment.getBytes()));
|
||||||
|
|
||||||
|
for (int c = input.read(); c != EOF; c = input.read())
|
||||||
|
if (isEscape((char) c))
|
||||||
|
applyControlSequence(input);
|
||||||
|
else
|
||||||
|
terminal.putCharacter((char) c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyControlSequence(SafeInputStream input) {
|
||||||
|
ControlSequence controlSequence = controlSequenceHandler.parse(input);
|
||||||
|
controlSequence.applyTo(terminal);
|
||||||
|
}
|
||||||
|
|
||||||
private synchronized void moveCursorToNextRowIfNecessary() {
|
private synchronized void moveCursorToNextRowIfNecessary() {
|
||||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,11 @@ import java.io.*;
|
||||||
|
|
||||||
public interface SafeStream {
|
public interface SafeStream {
|
||||||
|
|
||||||
public static class SafePipedInputStream {
|
public static class SafeInputStream {
|
||||||
|
|
||||||
private PipedInputStream underlyingStream;
|
private InputStream underlyingStream;
|
||||||
|
|
||||||
public SafePipedInputStream(PipedInputStream underlyingStream) {
|
public SafeInputStream(InputStream underlyingStream) {
|
||||||
this.underlyingStream = underlyingStream;
|
this.underlyingStream = underlyingStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,11 +29,11 @@ public interface SafeStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SafePipedOutputStream {
|
public static class SafeOutputStream {
|
||||||
|
|
||||||
private PipedOutputStream underlyingStream;
|
private OutputStream underlyingStream;
|
||||||
|
|
||||||
public SafePipedOutputStream(PipedOutputStream underlyingStream) {
|
public SafeOutputStream(OutputStream underlyingStream) {
|
||||||
this.underlyingStream = underlyingStream;
|
this.underlyingStream = underlyingStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package terminal;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.TextColor;
|
||||||
|
import com.googlecode.lanterna.terminal.IOSafeTerminal;
|
||||||
|
|
||||||
|
public enum SelectGraphicRendition implements ControlSequence {
|
||||||
|
|
||||||
|
RESET {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyTo(IOSafeTerminal terminal) {
|
||||||
|
terminal.resetColorAndSGR();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
RED {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return "31";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyTo(IOSafeTerminal terminal) {
|
||||||
|
terminal.setForegroundColor(TextColor.ANSI.RED);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
GREEN {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return "32";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyTo(IOSafeTerminal terminal) {
|
||||||
|
terminal.setForegroundColor(TextColor.ANSI.GREEN);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
YELLOW {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return "33";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyTo(IOSafeTerminal terminal) {
|
||||||
|
terminal.setForegroundColor(TextColor.ANSI.YELLOW);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
PURPLE {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return "35";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyTo(IOSafeTerminal terminal) {
|
||||||
|
terminal.setForegroundColor(TextColor.ANSI.MAGENTA);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final char SGR_COMMAND = 'm';
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package terminal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static terminal.ControlSequenceHandler.isEscape;
|
||||||
|
import static terminal.SelectGraphicRendition.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import terminal.ControlSequence.NullControlSequence;
|
||||||
|
import terminal.SafeStream.SafeInputStream;
|
||||||
|
import testutil.TestUtilities;
|
||||||
|
import util.Characters;
|
||||||
|
|
||||||
|
public class ControlSequenceHandlerTest {
|
||||||
|
|
||||||
|
private ControlSequenceHandler handler;
|
||||||
|
|
||||||
|
private Object readRemaining(SafeInputStream input) {
|
||||||
|
String remaining = "";
|
||||||
|
|
||||||
|
for (int c = input.read(); c != Characters.EOF; c = input.read())
|
||||||
|
remaining += (char) c;
|
||||||
|
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SafeInputStream createSafeInputStream(String data) {
|
||||||
|
return new SafeInputStream(TestUtilities.createInputStreamFromString(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
handler = new ControlSequenceHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isEscapeDetectsNonEscapeCharacter() {
|
||||||
|
assertFalse(isEscape('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isEscapeDetectsEscapeCharacter() {
|
||||||
|
assertTrue(isEscape('\u001b'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void correctlyParsesControlSequence_LeavesRestOfStreamIntact() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[32mdata");
|
||||||
|
handler.parse(input);
|
||||||
|
assertEquals("data", readRemaining(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unterminatedControlSequence_OnlyConsumesFirstNonSequenceCharacter() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[32data");
|
||||||
|
handler.parse(input);
|
||||||
|
assertEquals("ata", readRemaining(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void malformedControlSequence_OnlyConsumesOneCharacter() {
|
||||||
|
SafeInputStream input = createSafeInputStream("32mdata");
|
||||||
|
handler.parse(input);
|
||||||
|
assertEquals("2mdata", readRemaining(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_EOF() {
|
||||||
|
SafeInputStream input = createSafeInputStream("");
|
||||||
|
assertTrue(handler.parse(input) instanceof NullControlSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_EOF_AfterFirstCharacter() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[");
|
||||||
|
assertTrue(handler.parse(input) instanceof NullControlSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_UnterminatedControlSequence() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[data");
|
||||||
|
assertTrue(handler.parse(input) instanceof NullControlSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_MalformedControlSequence() {
|
||||||
|
SafeInputStream input = createSafeInputStream("32mdata");
|
||||||
|
assertTrue(handler.parse(input) instanceof NullControlSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_SGR_Reset() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[0m");
|
||||||
|
assertEquals(RESET, handler.parse(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_SGR_Red() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[31m");
|
||||||
|
assertEquals(RED, handler.parse(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_SGR_Green() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[32m");
|
||||||
|
assertEquals(GREEN, handler.parse(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_SGR_Yellow() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[33m");
|
||||||
|
assertEquals(YELLOW, handler.parse(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsedControlSequenceIsCorrectType_SGR_Purple() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[35m");
|
||||||
|
assertEquals(PURPLE, handler.parse(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseMultipleControlSequences() {
|
||||||
|
SafeInputStream input = createSafeInputStream("[35m[32m[0m");
|
||||||
|
assertEquals(PURPLE, handler.parse(input));
|
||||||
|
assertEquals(GREEN, handler.parse(input));
|
||||||
|
assertEquals(RESET, handler.parse(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package terminal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static terminal.SelectGraphicRendition.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.TextColor;
|
||||||
|
import com.googlecode.lanterna.terminal.virtual.*;
|
||||||
|
|
||||||
|
import terminal.ControlSequence.NullControlSequence;
|
||||||
|
|
||||||
|
public class ControlSequenceTest {
|
||||||
|
|
||||||
|
private Set<String> indicatorSet;
|
||||||
|
|
||||||
|
private VirtualTerminal createTerminalWithIndicators() {
|
||||||
|
return new DefaultVirtualTerminal() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetColorAndSGR() {
|
||||||
|
indicatorSet.add("RESET");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setForegroundColor(TextColor color) {
|
||||||
|
indicatorSet.add(color.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
indicatorSet = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nullControlSequenceDoesNothing() {
|
||||||
|
ControlSequence nullControlSequence = new NullControlSequence();
|
||||||
|
VirtualTerminal terminal = createTerminalWithIndicators();
|
||||||
|
nullControlSequence.applyTo(terminal);
|
||||||
|
assertTrue(indicatorSet.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void controlSequenceUpdatesTerminal_SGR_Reset() {
|
||||||
|
VirtualTerminal terminal = createTerminalWithIndicators();
|
||||||
|
RESET.applyTo(terminal);
|
||||||
|
assertTrue(indicatorSet.contains("RESET"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void controlSequenceUpdatesTerminal_SGR_Red() {
|
||||||
|
VirtualTerminal terminal = createTerminalWithIndicators();
|
||||||
|
RED.applyTo(terminal);
|
||||||
|
assertTrue(indicatorSet.contains("RED"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void controlSequenceUpdatesTerminal_SGR_Green() {
|
||||||
|
VirtualTerminal terminal = createTerminalWithIndicators();
|
||||||
|
GREEN.applyTo(terminal);
|
||||||
|
assertTrue(indicatorSet.contains("GREEN"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void controlSequenceUpdatesTerminal_SGR_Yellow() {
|
||||||
|
VirtualTerminal terminal = createTerminalWithIndicators();
|
||||||
|
YELLOW.applyTo(terminal);
|
||||||
|
assertTrue(indicatorSet.contains("YELLOW"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void controlSequenceUpdatesTerminal_SGR_Purple() {
|
||||||
|
VirtualTerminal terminal = createTerminalWithIndicators();
|
||||||
|
PURPLE.applyTo(terminal);
|
||||||
|
assertTrue(indicatorSet.contains("MAGENTA"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -508,4 +508,10 @@ public class LispTerminalTest {
|
||||||
assertCharacterPositions(new char[][] { { ' ', ' ', ' ' }, { ' ', ' ', ' ' } });
|
assertCharacterPositions(new char[][] { { ' ', ' ', ' ' }, { ' ', ' ', ' ' } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void controlSequenceIsNotPrinted() {
|
||||||
|
produceOutput("\u001B[32mcontrol\u001B[0mseq");
|
||||||
|
assertCharacterPositions(new char[][] { { 'c', 'o', 'n', 't', 'r', 'o', 'l', 's', 'e', 'q' } });
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue