diff --git a/LICENSE.txt b/LICENSE.txt
index 7e11b67fb..b62fe4571 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2002-2018, the original author or authors.
+Copyright (c) 2002-2023, the original author or authors.
All rights reserved.
https://opensource.org/licenses/BSD-3-Clause
diff --git a/builtins/pom.xml b/builtins/pom.xml
index b6aa8532b..ce5c19f2c 100644
--- a/builtins/pom.xml
+++ b/builtins/pom.xml
@@ -16,7 +16,7 @@
org.jlinejline-parent
- 3.24.2-SNAPSHOT
+ 3.25.0-SNAPSHOTjline-builtins
diff --git a/console/pom.xml b/console/pom.xml
index 3c2006008..828708f92 100644
--- a/console/pom.xml
+++ b/console/pom.xml
@@ -16,7 +16,7 @@
org.jlinejline-parent
- 3.24.2-SNAPSHOT
+ 3.25.0-SNAPSHOTjline-console
diff --git a/demo/pom.xml b/demo/pom.xml
index 231a766b8..e0269065b 100644
--- a/demo/pom.xml
+++ b/demo/pom.xml
@@ -16,7 +16,7 @@
org.jlinejline-parent
- 3.24.2-SNAPSHOT
+ 3.25.0-SNAPSHOTjline-demo
diff --git a/graal/pom.xml b/graal/pom.xml
index 52822be03..9d34013f0 100644
--- a/graal/pom.xml
+++ b/graal/pom.xml
@@ -16,7 +16,7 @@
org.jlinejline-parent
- 3.24.2-SNAPSHOT
+ 3.25.0-SNAPSHOTjline-graal
diff --git a/groovy/pom.xml b/groovy/pom.xml
index 3bf0fce82..900f919aa 100644
--- a/groovy/pom.xml
+++ b/groovy/pom.xml
@@ -14,7 +14,7 @@
org.jlinejline-parent
- 3.24.2-SNAPSHOT
+ 3.25.0-SNAPSHOTjline-groovyJLine Groovy
diff --git a/jansi-core/pom.xml b/jansi-core/pom.xml
new file mode 100644
index 000000000..ce670c1da
--- /dev/null
+++ b/jansi-core/pom.xml
@@ -0,0 +1,46 @@
+
+
+
+
+ 4.0.0
+
+
+ org.jline
+ jline-parent
+ 3.25.0-SNAPSHOT
+
+
+ jansi-core
+ Jansi Core
+
+
+ org.jansi.core
+
+
+
+
+ org.jline
+ jline-terminal
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+
+
diff --git a/jansi-core/src/main/java/org/jline/jansi/Ansi.java b/jansi-core/src/main/java/org/jline/jansi/Ansi.java
new file mode 100644
index 000000000..f27250814
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/Ansi.java
@@ -0,0 +1,955 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+
+/**
+ * Provides a fluent API for generating
+ * ANSI escape sequences.
+ *
+ * @since 1.0
+ */
+public class Ansi implements Appendable {
+
+ private static final char FIRST_ESC_CHAR = 27;
+ private static final char SECOND_ESC_CHAR = '[';
+
+ /**
+ * ANSI 8 colors for fluent API
+ */
+ public enum Color {
+ BLACK(0, "BLACK"),
+ RED(1, "RED"),
+ GREEN(2, "GREEN"),
+ YELLOW(3, "YELLOW"),
+ BLUE(4, "BLUE"),
+ MAGENTA(5, "MAGENTA"),
+ CYAN(6, "CYAN"),
+ WHITE(7, "WHITE"),
+ DEFAULT(9, "DEFAULT");
+
+ private final int value;
+ private final String name;
+
+ Color(int index, String name) {
+ this.value = index;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public int value() {
+ return value;
+ }
+
+ public int fg() {
+ return value + 30;
+ }
+
+ public int bg() {
+ return value + 40;
+ }
+
+ public int fgBright() {
+ return value + 90;
+ }
+
+ public int bgBright() {
+ return value + 100;
+ }
+ }
+
+ /**
+ * Display attributes, also know as
+ * SGR
+ * (Select Graphic Rendition) parameters.
+ */
+ public enum Attribute {
+ RESET(0, "RESET"),
+ INTENSITY_BOLD(1, "INTENSITY_BOLD"),
+ INTENSITY_FAINT(2, "INTENSITY_FAINT"),
+ ITALIC(3, "ITALIC_ON"),
+ UNDERLINE(4, "UNDERLINE_ON"),
+ BLINK_SLOW(5, "BLINK_SLOW"),
+ BLINK_FAST(6, "BLINK_FAST"),
+ NEGATIVE_ON(7, "NEGATIVE_ON"),
+ CONCEAL_ON(8, "CONCEAL_ON"),
+ STRIKETHROUGH_ON(9, "STRIKETHROUGH_ON"),
+ UNDERLINE_DOUBLE(21, "UNDERLINE_DOUBLE"),
+ INTENSITY_BOLD_OFF(22, "INTENSITY_BOLD_OFF"),
+ ITALIC_OFF(23, "ITALIC_OFF"),
+ UNDERLINE_OFF(24, "UNDERLINE_OFF"),
+ BLINK_OFF(25, "BLINK_OFF"),
+ NEGATIVE_OFF(27, "NEGATIVE_OFF"),
+ CONCEAL_OFF(28, "CONCEAL_OFF"),
+ STRIKETHROUGH_OFF(29, "STRIKETHROUGH_OFF");
+
+ private final int value;
+ private final String name;
+
+ Attribute(int index, String name) {
+ this.value = index;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public int value() {
+ return value;
+ }
+ }
+
+ /**
+ * ED (Erase in Display) / EL (Erase in Line) parameter (see
+ * CSI sequence J and K)
+ * @see Ansi#eraseScreen(Erase)
+ * @see Ansi#eraseLine(Erase)
+ */
+ public enum Erase {
+ FORWARD(0, "FORWARD"),
+ BACKWARD(1, "BACKWARD"),
+ ALL(2, "ALL");
+
+ private final int value;
+ private final String name;
+
+ Erase(int index, String name) {
+ this.value = index;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public int value() {
+ return value;
+ }
+ }
+
+ @FunctionalInterface
+ public interface Consumer {
+ void apply(Ansi ansi);
+ }
+
+ public static final String DISABLE = Ansi.class.getName() + ".disable";
+
+ private static Callable detector = () -> !Boolean.getBoolean(DISABLE);
+
+ public static void setDetector(final Callable detector) {
+ if (detector == null) throw new IllegalArgumentException();
+ Ansi.detector = detector;
+ }
+
+ public static boolean isDetected() {
+ try {
+ return detector.call();
+ } catch (Exception e) {
+ return true;
+ }
+ }
+
+ private static final InheritableThreadLocal holder = new InheritableThreadLocal() {
+ @Override
+ protected Boolean initialValue() {
+ return isDetected();
+ }
+ };
+
+ public static void setEnabled(final boolean flag) {
+ holder.set(flag);
+ }
+
+ public static boolean isEnabled() {
+ return holder.get();
+ }
+
+ public static Ansi ansi() {
+ if (isEnabled()) {
+ return new Ansi();
+ } else {
+ return new NoAnsi();
+ }
+ }
+
+ public static Ansi ansi(StringBuilder builder) {
+ if (isEnabled()) {
+ return new Ansi(builder);
+ } else {
+ return new NoAnsi(builder);
+ }
+ }
+
+ public static Ansi ansi(int size) {
+ if (isEnabled()) {
+ return new Ansi(size);
+ } else {
+ return new NoAnsi(size);
+ }
+ }
+
+ private static class NoAnsi extends Ansi {
+ public NoAnsi() {
+ super();
+ }
+
+ public NoAnsi(int size) {
+ super(size);
+ }
+
+ public NoAnsi(StringBuilder builder) {
+ super(builder);
+ }
+
+ @Override
+ public Ansi fg(Color color) {
+ return this;
+ }
+
+ @Override
+ public Ansi bg(Color color) {
+ return this;
+ }
+
+ @Override
+ public Ansi fgBright(Color color) {
+ return this;
+ }
+
+ @Override
+ public Ansi bgBright(Color color) {
+ return this;
+ }
+
+ @Override
+ public Ansi fg(int color) {
+ return this;
+ }
+
+ @Override
+ public Ansi fgRgb(int r, int g, int b) {
+ return this;
+ }
+
+ @Override
+ public Ansi bg(int color) {
+ return this;
+ }
+
+ @Override
+ public Ansi bgRgb(int r, int g, int b) {
+ return this;
+ }
+
+ @Override
+ public Ansi a(Attribute attribute) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursor(int row, int column) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorToColumn(int x) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorUp(int y) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorRight(int x) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorDown(int y) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorLeft(int x) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorDownLine() {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorDownLine(final int n) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorUpLine() {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorUpLine(final int n) {
+ return this;
+ }
+
+ @Override
+ public Ansi eraseScreen() {
+ return this;
+ }
+
+ @Override
+ public Ansi eraseScreen(Erase kind) {
+ return this;
+ }
+
+ @Override
+ public Ansi eraseLine() {
+ return this;
+ }
+
+ @Override
+ public Ansi eraseLine(Erase kind) {
+ return this;
+ }
+
+ @Override
+ public Ansi scrollUp(int rows) {
+ return this;
+ }
+
+ @Override
+ public Ansi scrollDown(int rows) {
+ return this;
+ }
+
+ @Override
+ public Ansi saveCursorPosition() {
+ return this;
+ }
+
+ @Override
+ public Ansi restoreCursorPosition() {
+ return this;
+ }
+
+ @Override
+ public Ansi reset() {
+ return this;
+ }
+ }
+
+ private final StringBuilder builder;
+ private final ArrayList attributeOptions = new ArrayList<>(5);
+
+ public Ansi() {
+ this(new StringBuilder(80));
+ }
+
+ public Ansi(Ansi parent) {
+ this(new StringBuilder(parent.builder));
+ attributeOptions.addAll(parent.attributeOptions);
+ }
+
+ public Ansi(int size) {
+ this(new StringBuilder(size));
+ }
+
+ public Ansi(StringBuilder builder) {
+ this.builder = builder;
+ }
+
+ public Ansi fg(Color color) {
+ attributeOptions.add(color.fg());
+ return this;
+ }
+
+ public Ansi fg(int color) {
+ attributeOptions.add(38);
+ attributeOptions.add(5);
+ attributeOptions.add(color & 0xff);
+ return this;
+ }
+
+ public Ansi fgRgb(int color) {
+ return fgRgb(color >> 16, color >> 8, color);
+ }
+
+ public Ansi fgRgb(int r, int g, int b) {
+ attributeOptions.add(38);
+ attributeOptions.add(2);
+ attributeOptions.add(r & 0xff);
+ attributeOptions.add(g & 0xff);
+ attributeOptions.add(b & 0xff);
+ return this;
+ }
+
+ public Ansi fgBlack() {
+ return this.fg(Color.BLACK);
+ }
+
+ public Ansi fgBlue() {
+ return this.fg(Color.BLUE);
+ }
+
+ public Ansi fgCyan() {
+ return this.fg(Color.CYAN);
+ }
+
+ public Ansi fgDefault() {
+ return this.fg(Color.DEFAULT);
+ }
+
+ public Ansi fgGreen() {
+ return this.fg(Color.GREEN);
+ }
+
+ public Ansi fgMagenta() {
+ return this.fg(Color.MAGENTA);
+ }
+
+ public Ansi fgRed() {
+ return this.fg(Color.RED);
+ }
+
+ public Ansi fgYellow() {
+ return this.fg(Color.YELLOW);
+ }
+
+ public Ansi bg(Color color) {
+ attributeOptions.add(color.bg());
+ return this;
+ }
+
+ public Ansi bg(int color) {
+ attributeOptions.add(48);
+ attributeOptions.add(5);
+ attributeOptions.add(color & 0xff);
+ return this;
+ }
+
+ public Ansi bgRgb(int color) {
+ return bgRgb(color >> 16, color >> 8, color);
+ }
+
+ public Ansi bgRgb(int r, int g, int b) {
+ attributeOptions.add(48);
+ attributeOptions.add(2);
+ attributeOptions.add(r & 0xff);
+ attributeOptions.add(g & 0xff);
+ attributeOptions.add(b & 0xff);
+ return this;
+ }
+
+ public Ansi bgCyan() {
+ return this.bg(Color.CYAN);
+ }
+
+ public Ansi bgDefault() {
+ return this.bg(Color.DEFAULT);
+ }
+
+ public Ansi bgGreen() {
+ return this.bg(Color.GREEN);
+ }
+
+ public Ansi bgMagenta() {
+ return this.bg(Color.MAGENTA);
+ }
+
+ public Ansi bgRed() {
+ return this.bg(Color.RED);
+ }
+
+ public Ansi bgYellow() {
+ return this.bg(Color.YELLOW);
+ }
+
+ public Ansi fgBright(Color color) {
+ attributeOptions.add(color.fgBright());
+ return this;
+ }
+
+ public Ansi fgBrightBlack() {
+ return this.fgBright(Color.BLACK);
+ }
+
+ public Ansi fgBrightBlue() {
+ return this.fgBright(Color.BLUE);
+ }
+
+ public Ansi fgBrightCyan() {
+ return this.fgBright(Color.CYAN);
+ }
+
+ public Ansi fgBrightDefault() {
+ return this.fgBright(Color.DEFAULT);
+ }
+
+ public Ansi fgBrightGreen() {
+ return this.fgBright(Color.GREEN);
+ }
+
+ public Ansi fgBrightMagenta() {
+ return this.fgBright(Color.MAGENTA);
+ }
+
+ public Ansi fgBrightRed() {
+ return this.fgBright(Color.RED);
+ }
+
+ public Ansi fgBrightYellow() {
+ return this.fgBright(Color.YELLOW);
+ }
+
+ public Ansi bgBright(Color color) {
+ attributeOptions.add(color.bgBright());
+ return this;
+ }
+
+ public Ansi bgBrightCyan() {
+ return this.bgBright(Color.CYAN);
+ }
+
+ public Ansi bgBrightDefault() {
+ return this.bgBright(Color.DEFAULT);
+ }
+
+ public Ansi bgBrightGreen() {
+ return this.bgBright(Color.GREEN);
+ }
+
+ public Ansi bgBrightMagenta() {
+ return this.bgBright(Color.MAGENTA);
+ }
+
+ public Ansi bgBrightRed() {
+ return this.bgBright(Color.RED);
+ }
+
+ public Ansi bgBrightYellow() {
+ return this.bgBright(Color.YELLOW);
+ }
+
+ public Ansi a(Attribute attribute) {
+ attributeOptions.add(attribute.value());
+ return this;
+ }
+
+ /**
+ * Moves the cursor to row n, column m. The values are 1-based.
+ * Any values less than 1 are mapped to 1.
+ *
+ * @param row row (1-based) from top
+ * @param column column (1 based) from left
+ * @return this Ansi instance
+ */
+ public Ansi cursor(final int row, final int column) {
+ return appendEscapeSequence('H', Math.max(1, row), Math.max(1, column));
+ }
+
+ /**
+ * Moves the cursor to column n. The parameter n is 1-based.
+ * If n is less than 1 it is moved to the first column.
+ *
+ * @param x the index (1-based) of the column to move to
+ * @return this Ansi instance
+ */
+ public Ansi cursorToColumn(final int x) {
+ return appendEscapeSequence('G', Math.max(1, x));
+ }
+
+ /**
+ * Moves the cursor up. If the parameter y is negative it moves the cursor down.
+ *
+ * @param y the number of lines to move up
+ * @return this Ansi instance
+ */
+ public Ansi cursorUp(final int y) {
+ return y > 0 ? appendEscapeSequence('A', y) : y < 0 ? cursorDown(-y) : this;
+ }
+
+ /**
+ * Moves the cursor down. If the parameter y is negative it moves the cursor up.
+ *
+ * @param y the number of lines to move down
+ * @return this Ansi instance
+ */
+ public Ansi cursorDown(final int y) {
+ return y > 0 ? appendEscapeSequence('B', y) : y < 0 ? cursorUp(-y) : this;
+ }
+
+ /**
+ * Moves the cursor right. If the parameter x is negative it moves the cursor left.
+ *
+ * @param x the number of characters to move right
+ * @return this Ansi instance
+ */
+ public Ansi cursorRight(final int x) {
+ return x > 0 ? appendEscapeSequence('C', x) : x < 0 ? cursorLeft(-x) : this;
+ }
+
+ /**
+ * Moves the cursor left. If the parameter x is negative it moves the cursor right.
+ *
+ * @param x the number of characters to move left
+ * @return this Ansi instance
+ */
+ public Ansi cursorLeft(final int x) {
+ return x > 0 ? appendEscapeSequence('D', x) : x < 0 ? cursorRight(-x) : this;
+ }
+
+ /**
+ * Moves the cursor relative to the current position. The cursor is moved right if x is
+ * positive, left if negative and down if y is positive and up if negative.
+ *
+ * @param x the number of characters to move horizontally
+ * @param y the number of lines to move vertically
+ * @return this Ansi instance
+ * @since 2.2
+ */
+ public Ansi cursorMove(final int x, final int y) {
+ return cursorRight(x).cursorDown(y);
+ }
+
+ /**
+ * Moves the cursor to the beginning of the line below.
+ *
+ * @return this Ansi instance
+ */
+ public Ansi cursorDownLine() {
+ return appendEscapeSequence('E');
+ }
+
+ /**
+ * Moves the cursor to the beginning of the n-th line below. If the parameter n is negative it
+ * moves the cursor to the beginning of the n-th line above.
+ *
+ * @param n the number of lines to move the cursor
+ * @return this Ansi instance
+ */
+ public Ansi cursorDownLine(final int n) {
+ return n < 0 ? cursorUpLine(-n) : appendEscapeSequence('E', n);
+ }
+
+ /**
+ * Moves the cursor to the beginning of the line above.
+ *
+ * @return this Ansi instance
+ */
+ public Ansi cursorUpLine() {
+ return appendEscapeSequence('F');
+ }
+
+ /**
+ * Moves the cursor to the beginning of the n-th line above. If the parameter n is negative it
+ * moves the cursor to the beginning of the n-th line below.
+ *
+ * @param n the number of lines to move the cursor
+ * @return this Ansi instance
+ */
+ public Ansi cursorUpLine(final int n) {
+ return n < 0 ? cursorDownLine(-n) : appendEscapeSequence('F', n);
+ }
+
+ public Ansi eraseScreen() {
+ return appendEscapeSequence('J', Erase.ALL.value());
+ }
+
+ public Ansi eraseScreen(final Erase kind) {
+ return appendEscapeSequence('J', kind.value());
+ }
+
+ public Ansi eraseLine() {
+ return appendEscapeSequence('K');
+ }
+
+ public Ansi eraseLine(final Erase kind) {
+ return appendEscapeSequence('K', kind.value());
+ }
+
+ public Ansi scrollUp(final int rows) {
+ if (rows == Integer.MIN_VALUE) {
+ return scrollDown(Integer.MAX_VALUE);
+ }
+ return rows > 0 ? appendEscapeSequence('S', rows) : rows < 0 ? scrollDown(-rows) : this;
+ }
+
+ public Ansi scrollDown(final int rows) {
+ if (rows == Integer.MIN_VALUE) {
+ return scrollUp(Integer.MAX_VALUE);
+ }
+ return rows > 0 ? appendEscapeSequence('T', rows) : rows < 0 ? scrollUp(-rows) : this;
+ }
+
+ public Ansi saveCursorPosition() {
+ saveCursorPositionSCO();
+ return saveCursorPositionDEC();
+ }
+
+ // SCO command
+ public Ansi saveCursorPositionSCO() {
+ return appendEscapeSequence('s');
+ }
+
+ // DEC command
+ public Ansi saveCursorPositionDEC() {
+ builder.append(FIRST_ESC_CHAR);
+ builder.append('7');
+ return this;
+ }
+
+ public Ansi restoreCursorPosition() {
+ restoreCursorPositionSCO();
+ return restoreCursorPositionDEC();
+ }
+
+ // SCO command
+ public Ansi restoreCursorPositionSCO() {
+ return appendEscapeSequence('u');
+ }
+
+ // DEC command
+ public Ansi restoreCursorPositionDEC() {
+ builder.append(FIRST_ESC_CHAR);
+ builder.append('8');
+ return this;
+ }
+
+ public Ansi reset() {
+ return a(Attribute.RESET);
+ }
+
+ public Ansi bold() {
+ return a(Attribute.INTENSITY_BOLD);
+ }
+
+ public Ansi boldOff() {
+ return a(Attribute.INTENSITY_BOLD_OFF);
+ }
+
+ public Ansi a(String value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(boolean value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(char value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(char[] value, int offset, int len) {
+ flushAttributes();
+ builder.append(value, offset, len);
+ return this;
+ }
+
+ public Ansi a(char[] value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(CharSequence value, int start, int end) {
+ flushAttributes();
+ builder.append(value, start, end);
+ return this;
+ }
+
+ public Ansi a(CharSequence value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(double value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(float value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(int value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(long value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(Object value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(StringBuffer value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi newline() {
+ flushAttributes();
+ builder.append(System.getProperty("line.separator"));
+ return this;
+ }
+
+ public Ansi format(String pattern, Object... args) {
+ flushAttributes();
+ builder.append(String.format(pattern, args));
+ return this;
+ }
+
+ /**
+ * Applies another function to this Ansi instance.
+ *
+ * @param fun the function to apply
+ * @return this Ansi instance
+ * @since 2.2
+ */
+ public Ansi apply(Consumer fun) {
+ fun.apply(this);
+ return this;
+ }
+
+ /**
+ * Uses the {@link AnsiRenderer}
+ * to generate the ANSI escape sequences for the supplied text.
+ *
+ * @param text text
+ * @return this
+ * @since 2.2
+ */
+ public Ansi render(final String text) {
+ a(AnsiRenderer.render(text));
+ return this;
+ }
+
+ /**
+ * String formats and renders the supplied arguments. Uses the {@link AnsiRenderer}
+ * to generate the ANSI escape sequences.
+ *
+ * @param text format
+ * @param args arguments
+ * @return this
+ * @since 2.2
+ */
+ public Ansi render(final String text, Object... args) {
+ a(String.format(AnsiRenderer.render(text), args));
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ flushAttributes();
+ return builder.toString();
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Private Helper Methods
+ ///////////////////////////////////////////////////////////////////
+
+ private Ansi appendEscapeSequence(char command) {
+ flushAttributes();
+ builder.append(FIRST_ESC_CHAR);
+ builder.append(SECOND_ESC_CHAR);
+ builder.append(command);
+ return this;
+ }
+
+ private Ansi appendEscapeSequence(char command, int option) {
+ flushAttributes();
+ builder.append(FIRST_ESC_CHAR);
+ builder.append(SECOND_ESC_CHAR);
+ builder.append(option);
+ builder.append(command);
+ return this;
+ }
+
+ private Ansi appendEscapeSequence(char command, Object... options) {
+ flushAttributes();
+ return _appendEscapeSequence(command, options);
+ }
+
+ private void flushAttributes() {
+ if (attributeOptions.isEmpty()) return;
+ if (attributeOptions.size() == 1 && attributeOptions.get(0) == 0) {
+ builder.append(FIRST_ESC_CHAR);
+ builder.append(SECOND_ESC_CHAR);
+ builder.append('m');
+ } else {
+ _appendEscapeSequence('m', attributeOptions.toArray());
+ }
+ attributeOptions.clear();
+ }
+
+ private Ansi _appendEscapeSequence(char command, Object... options) {
+ builder.append(FIRST_ESC_CHAR);
+ builder.append(SECOND_ESC_CHAR);
+ int size = options.length;
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ builder.append(';');
+ }
+ if (options[i] != null) {
+ builder.append(options[i]);
+ }
+ }
+ builder.append(command);
+ return this;
+ }
+
+ @Override
+ public Ansi append(CharSequence csq) {
+ builder.append(csq);
+ return this;
+ }
+
+ @Override
+ public Ansi append(CharSequence csq, int start, int end) {
+ builder.append(csq, start, end);
+ return this;
+ }
+
+ @Override
+ public Ansi append(char c) {
+ builder.append(c);
+ return this;
+ }
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/AnsiColors.java b/jansi-core/src/main/java/org/jline/jansi/AnsiColors.java
new file mode 100644
index 000000000..b26b56c58
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/AnsiColors.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+/**
+ * Colors support.
+ *
+ * @since 2.1
+ */
+public enum AnsiColors {
+ Colors16("16 colors"),
+ Colors256("256 colors"),
+ TrueColor("24-bit colors");
+
+ private final String description;
+
+ AnsiColors(String description) {
+ this.description = description;
+ }
+
+ String getDescription() {
+ return description;
+ }
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/AnsiConsole.java b/jansi-core/src/main/java/org/jline/jansi/AnsiConsole.java
new file mode 100644
index 000000000..48d8e7497
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/AnsiConsole.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+
+import org.jline.jansi.io.AnsiOutputStream;
+import org.jline.jansi.io.AnsiProcessor;
+import org.jline.jansi.io.FastBufferedOutputStream;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
+import org.jline.terminal.impl.DumbTerminal;
+import org.jline.terminal.spi.SystemStream;
+import org.jline.terminal.spi.TerminalExt;
+import org.jline.terminal.spi.TerminalProvider;
+import org.jline.utils.OSUtils;
+
+/**
+ * Provides consistent access to an ANSI aware console PrintStream or an ANSI codes stripping PrintStream
+ * if not on a terminal (see
+ * Jansi native
+ * CLibrary isatty(int)).
+ *
The native library used is named jansi and is loaded using HawtJNI Runtime
+ * Library
+ *
+ * @since 1.0
+ * @see #systemInstall()
+ * @see #out()
+ * @see #err()
+ * @see #ansiStream(boolean) for more details on ANSI mode selection
+ */
+public class AnsiConsole {
+
+ /**
+ * The default mode which Jansi will use, can be either force, strip
+ * or default (the default).
+ * If this property is set, it will override jansi.passthrough,
+ * jansi.strip and jansi.force properties.
+ */
+ public static final String JANSI_MODE = "jansi.mode";
+ /**
+ * Jansi mode specific to the standard output stream.
+ */
+ public static final String JANSI_OUT_MODE = "jansi.out.mode";
+ /**
+ * Jansi mode specific to the standard error stream.
+ */
+ public static final String JANSI_ERR_MODE = "jansi.err.mode";
+
+ /**
+ * Jansi mode value to strip all ansi sequences.
+ */
+ public static final String JANSI_MODE_STRIP = "strip";
+ /**
+ * Jansi mode value to force ansi sequences to the stream even if it's not a terminal.
+ */
+ public static final String JANSI_MODE_FORCE = "force";
+ /**
+ * Jansi mode value that output sequences if on a terminal, else strip them.
+ */
+ public static final String JANSI_MODE_DEFAULT = "default";
+
+ /**
+ * The default color support that Jansi will use, can be either 16,
+ * 256 or truecolor. If not set, JANSI will try to
+ * autodetect the number of colors supported by the terminal by checking the
+ * COLORTERM and TERM variables.
+ */
+ public static final String JANSI_COLORS = "jansi.colors";
+ /**
+ * Jansi colors specific to the standard output stream.
+ */
+ public static final String JANSI_OUT_COLORS = "jansi.out.colors";
+ /**
+ * Jansi colors specific to the standard error stream.
+ */
+ public static final String JANSI_ERR_COLORS = "jansi.err.colors";
+
+ /**
+ * Force the use of 16 colors. When using a 256-indexed color, or an RGB
+ * color, the color will be rounded to the nearest one from the 16 palette.
+ */
+ public static final String JANSI_COLORS_16 = "16";
+ /**
+ * Force the use of 256 colors. When using an RGB color, the color will be
+ * rounded to the nearest one from the standard 256 palette.
+ */
+ public static final String JANSI_COLORS_256 = "256";
+ /**
+ * Force the use of 24-bit colors.
+ */
+ public static final String JANSI_COLORS_TRUECOLOR = "truecolor";
+
+ /**
+ * If the jansi.noreset system property is set to true, the attributes won't be
+ * reset when the streams are uninstalled.
+ */
+ public static final String JANSI_NORESET = "jansi.noreset";
+ /**
+ * If the jansi.graceful system property is set to false, any exception that occurs
+ * during the initialization will cause the library to report this exception and fail. The default
+ * behavior is to behave gracefully and fall back to pure emulation on posix systems.
+ */
+ public static final String JANSI_GRACEFUL = "jansi.graceful";
+
+ /**
+ * The {@code jansi.providers} system property can be set to control which internal provider
+ * will be used. If this property is not set, the {@code ffm} provider will be used if available,
+ * else the {@code jni} one will be used. If set, this property is interpreted as a comma
+ * separated list of provider names to try in order.
+ */
+ public static final String JANSI_PROVIDERS = "jansi.providers";
+ /**
+ * The name of the {@code jni} provider.
+ */
+ public static final String JANSI_PROVIDER_JNI = "jni";
+ /**
+ * The name of the {@code ffm} provider.
+ */
+ public static final String JANSI_PROVIDER_FFM = "ffm";
+ /**
+ * The name of the {@code native-image} provider.
+ *
This provider uses the
+ * Native Image C API
+ * to call native functions, so it is only available when building to native image.
+ * Additionally, this provider currently does not support Windows.
+ *
Note: This is not the only provider available on Native Image,
+ * and it is usually recommended to use ffm or jni provider.
+ * This provider is mainly used when building static native images linked to musl libc.
+ */
+ public static final String JANSI_PROVIDER_NATIVE_IMAGE = "native-image";
+
+ private static final PrintStream system_out = System.out;
+ private static PrintStream out;
+ private static final PrintStream system_err = System.err;
+ private static PrintStream err;
+
+ /**
+ * Try to find the width of the console for this process.
+ * Both output and error streams will be checked to determine the width.
+ * A value of 0 is returned if the width can not be determined.
+ * @since 2.2
+ */
+ public static int getTerminalWidth() {
+ int w = out().getTerminalWidth();
+ if (w <= 0) {
+ w = err().getTerminalWidth();
+ }
+ return w;
+ }
+
+ static final boolean IS_WINDOWS = OSUtils.IS_WINDOWS;
+
+ static final boolean IS_CYGWIN =
+ IS_WINDOWS && System.getenv("PWD") != null && System.getenv("PWD").startsWith("/");
+
+ static final boolean IS_MSYSTEM = IS_WINDOWS
+ && System.getenv("MSYSTEM") != null
+ && (System.getenv("MSYSTEM").startsWith("MINGW")
+ || System.getenv("MSYSTEM").equals("MSYS"));
+
+ static final boolean IS_CONEMU = IS_WINDOWS && System.getenv("ConEmuPID") != null;
+
+ static final int ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
+
+ static int STDOUT_FILENO = 1;
+
+ static int STDERR_FILENO = 2;
+
+ private static int installed;
+ static Terminal terminal;
+
+ private AnsiConsole() {}
+
+ public static Terminal getTerminal() {
+ return terminal;
+ }
+
+ public static void setTerminal(Terminal terminal) {
+ AnsiConsole.terminal = terminal;
+ }
+
+ /**
+ * Initialize the out/err ansi-enabled streams
+ */
+ static synchronized void doInstall() {
+ try {
+ if (terminal == null) {
+ TerminalBuilder builder = TerminalBuilder.builder()
+ .system(true)
+ .name("jansi")
+ .providers(System.getProperty(JANSI_PROVIDERS));
+ String graceful = System.getProperty(JANSI_GRACEFUL);
+ if (graceful != null) {
+ builder.dumb(Boolean.parseBoolean(graceful));
+ }
+ terminal = builder.build();
+ }
+ if (out == null) {
+ out = ansiStream(true);
+ err = ansiStream(false);
+ }
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+
+ static synchronized void doUninstall() {
+ try {
+ if (terminal != null) {
+ terminal.close();
+ }
+ } catch (IOException e) {
+ throw new IOError(e);
+ } finally {
+ terminal = null;
+ out = null;
+ err = null;
+ }
+ }
+
+ private static AnsiPrintStream ansiStream(boolean stdout) throws IOException {
+ final OutputStream out;
+ final AnsiOutputStream.WidthSupplier width;
+ final AnsiProcessor processor = null;
+ final AnsiType type;
+ final AnsiOutputStream.IoRunnable installer = null;
+ final AnsiOutputStream.IoRunnable uninstaller = null;
+
+ final TerminalProvider provider = ((TerminalExt) terminal).getProvider();
+ final boolean isatty =
+ provider != null && provider.isSystemStream(stdout ? SystemStream.Output : SystemStream.Error);
+ if (isatty) {
+ out = terminal.output();
+ width = terminal::getWidth;
+ type = terminal instanceof DumbTerminal ? AnsiType.Unsupported : AnsiType.Native;
+ } else {
+ out = new FastBufferedOutputStream(new FileOutputStream(stdout ? FileDescriptor.out : FileDescriptor.err));
+ width = new AnsiOutputStream.ZeroWidthSupplier();
+ type = ((TerminalExt) terminal).getSystemStream() != null ? AnsiType.Redirected : AnsiType.Unsupported;
+ }
+ String enc = System.getProperty(stdout ? "stdout.encoding" : "stderr.encoding");
+ if (enc == null) {
+ enc = System.getProperty(stdout ? "sun.stdout.encoding" : "sun.stderr.encoding");
+ }
+
+ AnsiMode mode;
+
+ // If the jansi.mode property is set, use it
+ String jansiMode = System.getProperty(stdout ? JANSI_OUT_MODE : JANSI_ERR_MODE, System.getProperty(JANSI_MODE));
+ if (JANSI_MODE_FORCE.equals(jansiMode)) {
+ mode = AnsiMode.Force;
+ } else if (JANSI_MODE_STRIP.equals(jansiMode)) {
+ mode = AnsiMode.Strip;
+ } else {
+ mode = isatty ? AnsiMode.Default : AnsiMode.Strip;
+ }
+
+ AnsiColors colors;
+
+ String colorterm, term;
+ // If the jansi.colors property is set, use it
+ String jansiColors =
+ System.getProperty(stdout ? JANSI_OUT_COLORS : JANSI_ERR_COLORS, System.getProperty(JANSI_COLORS));
+ if (JANSI_COLORS_TRUECOLOR.equals(jansiColors)) {
+ colors = AnsiColors.TrueColor;
+ } else if (JANSI_COLORS_256.equals(jansiColors)) {
+ colors = AnsiColors.Colors256;
+ } else if (jansiColors != null) {
+ colors = AnsiColors.Colors16;
+ }
+
+ // If the COLORTERM env variable contains "truecolor" or "24bit", assume true color support
+ // see https://gist.github.com/XVilka/8346728#true-color-detection
+ else if ((colorterm = System.getenv("COLORTERM")) != null
+ && (colorterm.contains("truecolor") || colorterm.contains("24bit"))) {
+ colors = AnsiColors.TrueColor;
+ }
+
+ // check the if TERM contains -direct
+ else if ((term = System.getenv("TERM")) != null && term.contains("-direct")) {
+ colors = AnsiColors.TrueColor;
+ }
+
+ // check the if TERM contains -256color
+ else if (term != null && term.contains("-256color")) {
+ colors = AnsiColors.Colors256;
+ }
+
+ // else defaults to 16 colors
+ else {
+ colors = AnsiColors.Colors16;
+ }
+
+ // If the jansi.noreset property is not set, reset the attributes
+ // when the stream is closed
+ boolean resetAtUninstall = type != AnsiType.Unsupported && !getBoolean(JANSI_NORESET);
+
+ return newPrintStream(
+ new AnsiOutputStream(
+ out,
+ width,
+ mode,
+ processor,
+ type,
+ colors,
+ terminal.encoding(),
+ installer,
+ uninstaller,
+ resetAtUninstall),
+ terminal.encoding().name());
+ }
+
+ private static AnsiPrintStream newPrintStream(AnsiOutputStream out, String enc) {
+ if (enc != null) {
+ try {
+ return new AnsiPrintStream(out, true, enc);
+ } catch (UnsupportedEncodingException e) {
+ }
+ }
+ return new AnsiPrintStream(out, true);
+ }
+
+ static boolean getBoolean(String name) {
+ boolean result = false;
+ try {
+ String val = System.getProperty(name);
+ result = val.isEmpty() || Boolean.parseBoolean(val);
+ } catch (IllegalArgumentException | NullPointerException ignored) {
+ }
+ return result;
+ }
+
+ /**
+ * If the standard out natively supports ANSI escape codes, then this just
+ * returns System.out, otherwise it will provide an ANSI aware PrintStream
+ * which strips out the ANSI escape sequences or which implement the escape
+ * sequences.
+ *
+ * @return a PrintStream which is ANSI aware.
+ */
+ public static AnsiPrintStream out() {
+ doInstall();
+ return (AnsiPrintStream) out;
+ }
+
+ /**
+ * Access to the original System.out stream before ansi streams were installed.
+ *
+ * @return the originial System.out print stream
+ */
+ public static PrintStream sysOut() {
+ return system_out;
+ }
+
+ /**
+ * If the standard out natively supports ANSI escape codes, then this just
+ * returns System.err, otherwise it will provide an ANSI aware PrintStream
+ * which strips out the ANSI escape sequences or which implement the escape
+ * sequences.
+ *
+ * @return a PrintStream which is ANSI aware.
+ */
+ public static AnsiPrintStream err() {
+ doInstall();
+ return (AnsiPrintStream) err;
+ }
+
+ /**
+ * Access to the original System.err stream before ansi streams were installed.
+ *
+ * @return the originial System.err print stream
+ */
+ public static PrintStream sysErr() {
+ return system_err;
+ }
+
+ /**
+ * Install AnsiConsole.out() to System.out and
+ * AnsiConsole.err() to System.err.
+ * @see #systemUninstall()
+ */
+ public static synchronized void systemInstall() {
+ if (installed == 0) {
+ doInstall();
+ System.setOut(out);
+ System.setErr(err);
+ }
+ installed++;
+ }
+
+ /**
+ * check if the streams have been installed or not
+ */
+ public static synchronized boolean isInstalled() {
+ return installed > 0;
+ }
+
+ /**
+ * undo a previous {@link #systemInstall()}. If {@link #systemInstall()} was called
+ * multiple times, {@link #systemUninstall()} must be called the same number of times before
+ * it is actually uninstalled.
+ */
+ public static synchronized void systemUninstall() {
+ installed--;
+ if (installed == 0) {
+ doUninstall();
+ System.setOut(system_out);
+ System.setErr(system_err);
+ }
+ }
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/AnsiMain.java b/jansi-core/src/main/java/org/jline/jansi/AnsiMain.java
new file mode 100644
index 000000000..f4cf8636c
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/AnsiMain.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.Properties;
+
+import org.jline.terminal.impl.Diag;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.jline.jansi.Ansi.ansi;
+
+/**
+ * Main class for the library, providing executable jar to diagnose Jansi setup.
+ *
If no system property is set and output is sent to a terminal (no redirect to a file):
+ *
+ *
any terminal on any Unix should get RESET_ANSI_AT_CLOSE mode,
+ *
on Windows, Git-bash or Cygwin terminals should get RESET_ANSI_AT_CLOSE mode also, since they
+ * support natively ANSI escape sequences like any Unix terminal,
+ *
on Windows, cmd.exe, PowerShell or Git-cmd terminals should get WINDOWS mode.
+ *
+ * If stdout is redirected to a file (> out.txt), System.out should switch to STRIP_ANSI.
+ * Same for stderr redirection (2> err.txt) which should affect System.err mode.
+ *
The results will vary if you play with jansi.passthrough, jansi.strip or
+ * jansi.force system property, or if you redirect output to a file.
+ *
If you have a specific situation that is not covered, please report precise conditions to reproduce
+ * the issue and ideas on how to detect precisely the affected situation.
+ * @see AnsiConsole
+ */
+@SuppressWarnings("deprecation")
+public class AnsiMain {
+ public static void main(String... args) throws IOException {
+ Diag.diag(System.out);
+
+ System.out.println("Jansi " + getJansiVersion());
+
+ System.out.println();
+
+ System.out.println("jansi.providers= " + System.getProperty(AnsiConsole.JANSI_PROVIDERS, ""));
+ // String provider = ((TerminalExt) AnsiConsole.terminal).getProvider().name();
+ // System.out.println("Selected provider: " + provider);
+ //
+ // if (AnsiConsole.JANSI_PROVIDER_JNI.equals(provider)) {
+ // // info on native library
+ // System.out.println("library.jansi.path= " + System.getProperty("library.jansi.path", ""));
+ // System.out.println("library.jansi.version= " + System.getProperty("library.jansi.version", ""));
+ // boolean loaded = JansiLoader.initialize();
+ // if (loaded) {
+ // System.out.println("Jansi native library loaded from " + JansiLoader.getNativeLibraryPath());
+ // if (JansiLoader.getNativeLibrarySourceUrl() != null) {
+ // System.out.println(" which was auto-extracted from " +
+ // JansiLoader.getNativeLibrarySourceUrl());
+ // }
+ // } else {
+ // String prev = System.getProperty(AnsiConsole.JANSI_GRACEFUL);
+ // try {
+ // System.setProperty(AnsiConsole.JANSI_GRACEFUL, "false");
+ // JansiLoader.initialize();
+ // } catch (Throwable e) {
+ // e.printStackTrace(System.out);
+ // } finally {
+ // if (prev != null) {
+ // System.setProperty(AnsiConsole.JANSI_GRACEFUL, prev);
+ // } else {
+ // System.clearProperty(AnsiConsole.JANSI_GRACEFUL);
+ // }
+ // }
+ // }
+ // }
+
+ System.out.println();
+
+ System.out.println("os.name= " + System.getProperty("os.name") + ", "
+ + "os.version= " + System.getProperty("os.version") + ", "
+ + "os.arch= " + System.getProperty("os.arch"));
+ System.out.println("file.encoding= " + System.getProperty("file.encoding"));
+ System.out.println("sun.stdout.encoding= " + System.getProperty("sun.stdout.encoding") + ", "
+ + "sun.stderr.encoding= " + System.getProperty("sun.stderr.encoding"));
+ System.out.println("stdout.encoding= " + System.getProperty("stdout.encoding") + ", " + "stderr.encoding= "
+ + System.getProperty("stderr.encoding"));
+ System.out.println("java.version= " + System.getProperty("java.version") + ", "
+ + "java.vendor= " + System.getProperty("java.vendor") + ","
+ + " java.home= " + System.getProperty("java.home"));
+ System.out.println("Console: " + System.console());
+
+ System.out.println();
+
+ System.out.println(AnsiConsole.JANSI_GRACEFUL + "= " + System.getProperty(AnsiConsole.JANSI_GRACEFUL, ""));
+ System.out.println(AnsiConsole.JANSI_MODE + "= " + System.getProperty(AnsiConsole.JANSI_MODE, ""));
+ System.out.println(AnsiConsole.JANSI_OUT_MODE + "= " + System.getProperty(AnsiConsole.JANSI_OUT_MODE, ""));
+ System.out.println(AnsiConsole.JANSI_ERR_MODE + "= " + System.getProperty(AnsiConsole.JANSI_ERR_MODE, ""));
+ System.out.println(AnsiConsole.JANSI_COLORS + "= " + System.getProperty(AnsiConsole.JANSI_COLORS, ""));
+ System.out.println(AnsiConsole.JANSI_OUT_COLORS + "= " + System.getProperty(AnsiConsole.JANSI_OUT_COLORS, ""));
+ System.out.println(AnsiConsole.JANSI_ERR_COLORS + "= " + System.getProperty(AnsiConsole.JANSI_ERR_COLORS, ""));
+ System.out.println(AnsiConsole.JANSI_NORESET + "= " + AnsiConsole.getBoolean(AnsiConsole.JANSI_NORESET));
+ System.out.println(Ansi.DISABLE + "= " + AnsiConsole.getBoolean(Ansi.DISABLE));
+
+ System.out.println();
+
+ System.out.println("IS_WINDOWS: " + AnsiConsole.IS_WINDOWS);
+ if (AnsiConsole.IS_WINDOWS) {
+ System.out.println("IS_CONEMU: " + AnsiConsole.IS_CONEMU);
+ System.out.println("IS_CYGWIN: " + AnsiConsole.IS_CYGWIN);
+ System.out.println("IS_MSYSTEM: " + AnsiConsole.IS_MSYSTEM);
+ }
+
+ System.out.println();
+
+ diagnoseTty(false); // System.out
+ diagnoseTty(true); // System.err
+
+ AnsiConsole.systemInstall();
+
+ System.out.println();
+
+ System.out.println("Resulting Jansi modes for stout/stderr streams:");
+ System.out.println(" - System.out: " + AnsiConsole.out().toString());
+ System.out.println(" - System.err: " + AnsiConsole.err().toString());
+ System.out.println("Processor types description:");
+ for (AnsiType type : AnsiType.values()) {
+ System.out.println(" - " + type + ": " + type.getDescription());
+ }
+ System.out.println("Colors support description:");
+ for (AnsiColors colors : AnsiColors.values()) {
+ System.out.println(" - " + colors + ": " + colors.getDescription());
+ }
+ System.out.println("Modes description:");
+ for (AnsiMode mode : AnsiMode.values()) {
+ System.out.println(" - " + mode + ": " + mode.getDescription());
+ }
+
+ try {
+ System.out.println();
+
+ testAnsi(false);
+ testAnsi(true);
+
+ if (args.length == 0) {
+ printJansiLogoDemo();
+ return;
+ }
+
+ System.out.println();
+
+ if (args.length == 1) {
+ File f = new File(args[0]);
+ if (f.exists()) {
+ // write file content
+ System.out.println(
+ Ansi.ansi().bold().a("\"" + args[0] + "\" content:").reset());
+ writeFileContent(f);
+ return;
+ }
+ }
+
+ // write args without Jansi then with Jansi AnsiConsole
+ System.out.println(Ansi.ansi().bold().a("original args:").reset());
+ int i = 1;
+ for (String arg : args) {
+ AnsiConsole.sysOut().print(i++ + ": ");
+ AnsiConsole.sysOut().println(arg);
+ }
+
+ System.out.println(Ansi.ansi().bold().a("Jansi filtered args:").reset());
+ i = 1;
+ for (String arg : args) {
+ System.out.print(i++ + ": ");
+ System.out.println(arg);
+ }
+ } finally {
+ AnsiConsole.systemUninstall();
+ }
+ }
+
+ private static String getJansiVersion() {
+ Package p = AnsiMain.class.getPackage();
+ return (p == null) ? null : p.getImplementationVersion();
+ }
+
+ private static void diagnoseTty(boolean stderr) {
+ // int isatty;
+ // int width;
+ // if (AnsiConsole.IS_WINDOWS) {
+ // long console = AnsiConsoleSupportHolder.getKernel32().getStdHandle(!stderr);
+ // isatty = AnsiConsoleSupportHolder.getKernel32().isTty(console);
+ // if ((AnsiConsole.IS_CONEMU || AnsiConsole.IS_CYGWIN || AnsiConsole.IS_MSYSTEM) && isatty == 0) {
+ // MingwSupport mingw = new MingwSupport();
+ // String name = mingw.getConsoleName(!stderr);
+ // if (name != null && !name.isEmpty()) {
+ // isatty = 1;
+ // width = mingw.getTerminalWidth(name);
+ // } else {
+ // isatty = 0;
+ // width = 0;
+ // }
+ // } else {
+ // width = AnsiConsoleSupportHolder.getKernel32().getTerminalWidth(console);
+ // }
+ // } else {
+ // int fd = stderr ? AnsiConsoleSupport.CLibrary.STDERR_FILENO :
+ // AnsiConsoleSupport.CLibrary.STDOUT_FILENO;
+ // isatty = AnsiConsoleSupportHolder.getCLibrary().isTty(fd);
+ // width = AnsiConsoleSupportHolder.getCLibrary().getTerminalWidth(fd);
+ // }
+ //
+ // System.out.println("isatty(STD" + (stderr ? "ERR" : "OUT") + "_FILENO): " + isatty + ", System."
+ // + (stderr ? "err" : "out") + " " + ((isatty == 0) ? "is *NOT*" : "is") + " a terminal");
+ // System.out.println("width(STD" + (stderr ? "ERR" : "OUT") + "_FILENO): " + width);
+ }
+
+ private static void testAnsi(boolean stderr) {
+ @SuppressWarnings("resource")
+ PrintStream s = stderr ? System.err : System.out;
+ s.print("test on System." + (stderr ? "err" : "out") + ":");
+ for (Ansi.Color c : Ansi.Color.values()) {
+ s.print(" " + Ansi.ansi().fg(c) + c + Ansi.ansi().reset());
+ }
+ s.println();
+ s.print(" bright:");
+ for (Ansi.Color c : Ansi.Color.values()) {
+ s.print(" " + Ansi.ansi().fgBright(c) + c + Ansi.ansi().reset());
+ }
+ s.println();
+ s.print(" bold:");
+ for (Ansi.Color c : Ansi.Color.values()) {
+ s.print(" " + Ansi.ansi().bold().fg(c) + c + Ansi.ansi().reset());
+ }
+ s.println();
+ s.print(" faint:");
+ for (Ansi.Color c : Ansi.Color.values()) {
+ s.print(" " + Ansi.ansi().a(Ansi.Attribute.INTENSITY_FAINT).fg(c) + c
+ + Ansi.ansi().reset());
+ }
+ s.println();
+ s.print(" bold+faint:");
+ for (Ansi.Color c : Ansi.Color.values()) {
+ s.print(" " + Ansi.ansi().bold().a(Ansi.Attribute.INTENSITY_FAINT).fg(c) + c
+ + Ansi.ansi().reset());
+ }
+ s.println();
+ Ansi ansi = Ansi.ansi();
+ ansi.a(" 256 colors: ");
+ for (int i = 0; i < 6 * 6 * 6; i++) {
+ if (i > 0 && i % 36 == 0) {
+ ansi.reset();
+ ansi.newline();
+ ansi.a(" ");
+ } else if (i > 0 && i % 6 == 0) {
+ ansi.reset();
+ ansi.a(" ");
+ }
+ int a0 = i % 6;
+ int a1 = (i / 6) % 6;
+ int a2 = i / 36;
+ ansi.bg(16 + a0 + a2 * 6 + a1 * 36).a(' ');
+ }
+ ansi.reset();
+ s.println(ansi);
+ ansi = Ansi.ansi();
+ ansi.a(" truecolor: ");
+ for (int i = 0; i < 256; i++) {
+ if (i > 0 && i % 48 == 0) {
+ ansi.reset();
+ ansi.newline();
+ ansi.a(" ");
+ }
+ int r = 255 - i;
+ int g = i * 2 > 255 ? 255 - 2 * i : 2 * i;
+ int b = i;
+ ansi.bgRgb(r, g, b).fgRgb(255 - r, 255 - g, 255 - b).a(i % 2 == 0 ? '/' : '\\');
+ }
+ ansi.reset();
+ s.println(ansi);
+ }
+
+ private static String getPomPropertiesVersion(String path) throws IOException {
+ InputStream in = AnsiMain.class.getResourceAsStream("/META-INF/maven/" + path + "/pom.properties");
+ if (in == null) {
+ return null;
+ }
+ try {
+ Properties p = new Properties();
+ p.load(in);
+ return p.getProperty("version");
+ } finally {
+ closeQuietly(in);
+ }
+ }
+
+ private static void printJansiLogoDemo() throws IOException {
+ BufferedReader in =
+ new BufferedReader(new InputStreamReader(AnsiMain.class.getResourceAsStream("jansi.txt"), UTF_8));
+ try {
+ String l;
+ while ((l = in.readLine()) != null) {
+ System.out.println(l);
+ }
+ } finally {
+ closeQuietly(in);
+ }
+ }
+
+ private static void writeFileContent(File f) throws IOException {
+ InputStream in = new FileInputStream(f);
+ try {
+ byte[] buf = new byte[1024];
+ int l = 0;
+ while ((l = in.read(buf)) >= 0) {
+ System.out.write(buf, 0, l);
+ }
+ } finally {
+ closeQuietly(in);
+ }
+ }
+
+ private static void closeQuietly(Closeable c) {
+ try {
+ c.close();
+ } catch (IOException ioe) {
+ ioe.printStackTrace(System.err);
+ }
+ }
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/AnsiMode.java b/jansi-core/src/main/java/org/jline/jansi/AnsiMode.java
new file mode 100644
index 000000000..f16c1c25f
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/AnsiMode.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+/**
+ * Ansi mode.
+ *
+ * @since 2.1
+ */
+public enum AnsiMode {
+ Strip("Strip all ansi sequences"),
+ Default("Print ansi sequences if the stream is a terminal"),
+ Force("Always print ansi sequences, even if the stream is redirected");
+
+ private final String description;
+
+ AnsiMode(String description) {
+ this.description = description;
+ }
+
+ String getDescription() {
+ return description;
+ }
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/AnsiPrintStream.java b/jansi-core/src/main/java/org/jline/jansi/AnsiPrintStream.java
new file mode 100644
index 000000000..bd36168a5
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/AnsiPrintStream.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+
+import org.jline.jansi.io.AnsiOutputStream;
+
+/**
+ * Simple PrintStream holding an AnsiOutputStream.
+ * This allows changing the mode in which the underlying AnsiOutputStream operates.
+ */
+public class AnsiPrintStream extends PrintStream {
+
+ public AnsiPrintStream(AnsiOutputStream out, boolean autoFlush) {
+ super(out, autoFlush);
+ }
+
+ public AnsiPrintStream(AnsiOutputStream out, boolean autoFlush, String encoding)
+ throws UnsupportedEncodingException {
+ super(out, autoFlush, encoding);
+ }
+
+ protected AnsiOutputStream getOut() {
+ return (AnsiOutputStream) out;
+ }
+
+ public AnsiType getType() {
+ return getOut().getType();
+ }
+
+ public AnsiColors getColors() {
+ return getOut().getColors();
+ }
+
+ public AnsiMode getMode() {
+ return getOut().getMode();
+ }
+
+ public void setMode(AnsiMode ansiMode) {
+ getOut().setMode(ansiMode);
+ }
+
+ public boolean isResetAtUninstall() {
+ return getOut().isResetAtUninstall();
+ }
+
+ public void setResetAtUninstall(boolean resetAtClose) {
+ getOut().setResetAtUninstall(resetAtClose);
+ }
+
+ /**
+ * Returns the width of the terminal associated with this stream or 0.
+ * @since 2.2
+ */
+ public int getTerminalWidth() {
+ return getOut().getTerminalWidth();
+ }
+
+ public void install() throws IOException {
+ getOut().install();
+ }
+
+ public void uninstall() throws IOException {
+ // If the system output stream has been closed, out should be null, so avoid a NPE
+ AnsiOutputStream out = getOut();
+ if (out != null) {
+ out.uninstall();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "AnsiPrintStream{"
+ + "type=" + getType()
+ + ", colors=" + getColors()
+ + ", mode=" + getMode()
+ + ", resetAtUninstall=" + isResetAtUninstall()
+ + "}";
+ }
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/AnsiRenderer.java b/jansi-core/src/main/java/org/jline/jansi/AnsiRenderer.java
new file mode 100644
index 000000000..646adf897
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/AnsiRenderer.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * Renders ANSI color escape-codes in strings by parsing out some special syntax to pick up the correct fluff to use.
+ *
+ * The syntax for embedded ANSI codes is:
+ *
+ *
+ * @|code(,code)* text|@
+ *
+ *
+ * Examples:
+ *
+ *
+ * @|bold Hello|@
+ *
+ *
+ *
+ * @|bold,red Warning!|@
+ *
+ *
+ * @since 2.2
+ */
+public class AnsiRenderer {
+
+ public static final String BEGIN_TOKEN = "@|";
+
+ public static final String END_TOKEN = "|@";
+
+ public static final String CODE_TEXT_SEPARATOR = " ";
+
+ public static final String CODE_LIST_SEPARATOR = ",";
+
+ private static final int BEGIN_TOKEN_LEN = 2;
+
+ private static final int END_TOKEN_LEN = 2;
+
+ public static String render(final String input) throws IllegalArgumentException {
+ try {
+ return render(input, new StringBuilder()).toString();
+ } catch (IOException e) {
+ // Cannot happen because StringBuilder does not throw IOException
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Renders the given input to the target Appendable.
+ *
+ * @param input
+ * source to render
+ * @param target
+ * render onto this target Appendable.
+ * @return the given Appendable
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static Appendable render(final String input, Appendable target) throws IOException {
+
+ int i = 0;
+ int j, k;
+
+ while (true) {
+ j = input.indexOf(BEGIN_TOKEN, i);
+ if (j == -1) {
+ if (i == 0) {
+ target.append(input);
+ return target;
+ }
+ target.append(input.substring(i));
+ return target;
+ }
+ target.append(input.substring(i, j));
+ k = input.indexOf(END_TOKEN, j);
+
+ if (k == -1) {
+ target.append(input);
+ return target;
+ }
+ j += BEGIN_TOKEN_LEN;
+
+ // Check for invalid string with END_TOKEN before BEGIN_TOKEN
+ if (k < j) {
+ throw new IllegalArgumentException("Invalid input string found.");
+ }
+ String spec = input.substring(j, k);
+
+ String[] items = spec.split(CODE_TEXT_SEPARATOR, 2);
+ if (items.length == 1) {
+ target.append(input);
+ return target;
+ }
+ String replacement = render(items[1], items[0].split(CODE_LIST_SEPARATOR));
+
+ target.append(replacement);
+
+ i = k + END_TOKEN_LEN;
+ }
+ }
+
+ public static String render(final String text, final String... codes) {
+ return render(Ansi.ansi(), codes).a(text).reset().toString();
+ }
+
+ /**
+ * Renders {@link Code} names as an ANSI escape string.
+ * @param codes The code names to render
+ * @return an ANSI escape string.
+ */
+ public static String renderCodes(final String... codes) {
+ return render(Ansi.ansi(), codes).toString();
+ }
+
+ /**
+ * Renders {@link Code} names as an ANSI escape string.
+ * @param codes A space separated list of code names to render
+ * @return an ANSI escape string.
+ */
+ public static String renderCodes(final String codes) {
+ return renderCodes(codes.split("\\s"));
+ }
+
+ private static Ansi render(Ansi ansi, String... names) {
+ for (String name : names) {
+ Code code = Code.valueOf(name.toUpperCase(Locale.ENGLISH));
+ if (code.isColor()) {
+ if (code.isBackground()) {
+ ansi.bg(code.getColor());
+ } else {
+ ansi.fg(code.getColor());
+ }
+ } else if (code.isAttribute()) {
+ ansi.a(code.getAttribute());
+ }
+ }
+ return ansi;
+ }
+
+ public static boolean test(final String text) {
+ return text != null && text.contains(BEGIN_TOKEN);
+ }
+
+ @SuppressWarnings("unused")
+ public enum Code {
+ //
+ // TODO: Find a better way to keep Code in sync with Color/Attribute/Erase
+ //
+
+ // Colors
+ BLACK(Ansi.Color.BLACK),
+ RED(Ansi.Color.RED),
+ GREEN(Ansi.Color.GREEN),
+ YELLOW(Ansi.Color.YELLOW),
+ BLUE(Ansi.Color.BLUE),
+ MAGENTA(Ansi.Color.MAGENTA),
+ CYAN(Ansi.Color.CYAN),
+ WHITE(Ansi.Color.WHITE),
+ DEFAULT(Ansi.Color.DEFAULT),
+
+ // Foreground Colors
+ FG_BLACK(Ansi.Color.BLACK, false),
+ FG_RED(Ansi.Color.RED, false),
+ FG_GREEN(Ansi.Color.GREEN, false),
+ FG_YELLOW(Ansi.Color.YELLOW, false),
+ FG_BLUE(Ansi.Color.BLUE, false),
+ FG_MAGENTA(Ansi.Color.MAGENTA, false),
+ FG_CYAN(Ansi.Color.CYAN, false),
+ FG_WHITE(Ansi.Color.WHITE, false),
+ FG_DEFAULT(Ansi.Color.DEFAULT, false),
+
+ // Background Colors
+ BG_BLACK(Ansi.Color.BLACK, true),
+ BG_RED(Ansi.Color.RED, true),
+ BG_GREEN(Ansi.Color.GREEN, true),
+ BG_YELLOW(Ansi.Color.YELLOW, true),
+ BG_BLUE(Ansi.Color.BLUE, true),
+ BG_MAGENTA(Ansi.Color.MAGENTA, true),
+ BG_CYAN(Ansi.Color.CYAN, true),
+ BG_WHITE(Ansi.Color.WHITE, true),
+ BG_DEFAULT(Ansi.Color.DEFAULT, true),
+
+ // Attributes
+ RESET(Ansi.Attribute.RESET),
+ INTENSITY_BOLD(Ansi.Attribute.INTENSITY_BOLD),
+ INTENSITY_FAINT(Ansi.Attribute.INTENSITY_FAINT),
+ ITALIC(Ansi.Attribute.ITALIC),
+ UNDERLINE(Ansi.Attribute.UNDERLINE),
+ BLINK_SLOW(Ansi.Attribute.BLINK_SLOW),
+ BLINK_FAST(Ansi.Attribute.BLINK_FAST),
+ BLINK_OFF(Ansi.Attribute.BLINK_OFF),
+ NEGATIVE_ON(Ansi.Attribute.NEGATIVE_ON),
+ NEGATIVE_OFF(Ansi.Attribute.NEGATIVE_OFF),
+ CONCEAL_ON(Ansi.Attribute.CONCEAL_ON),
+ CONCEAL_OFF(Ansi.Attribute.CONCEAL_OFF),
+ UNDERLINE_DOUBLE(Ansi.Attribute.UNDERLINE_DOUBLE),
+ UNDERLINE_OFF(Ansi.Attribute.UNDERLINE_OFF),
+
+ // Aliases
+ BOLD(Ansi.Attribute.INTENSITY_BOLD),
+ FAINT(Ansi.Attribute.INTENSITY_FAINT);
+
+ private final Enum> n;
+
+ private final boolean background;
+
+ Code(final Enum> n, boolean background) {
+ this.n = n;
+ this.background = background;
+ }
+
+ Code(final Enum> n) {
+ this(n, false);
+ }
+
+ public boolean isColor() {
+ return n instanceof Ansi.Color;
+ }
+
+ public Ansi.Color getColor() {
+ return (Ansi.Color) n;
+ }
+
+ public boolean isAttribute() {
+ return n instanceof Ansi.Attribute;
+ }
+
+ public Ansi.Attribute getAttribute() {
+ return (Ansi.Attribute) n;
+ }
+
+ public boolean isBackground() {
+ return background;
+ }
+ }
+
+ private AnsiRenderer() {}
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/AnsiType.java b/jansi-core/src/main/java/org/jline/jansi/AnsiType.java
new file mode 100644
index 000000000..4a40b52f2
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/AnsiType.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+/**
+ * Processor type.
+ *
+ * @since 2.1
+ */
+public enum AnsiType {
+ Native("Supports ansi sequences natively"),
+ Unsupported("Ansi sequences are stripped out"),
+ VirtualTerminal("Supported through windows virtual terminal"),
+ Emulation("Emulated through using windows API console commands"),
+ Redirected("The stream is redirected to a file or a pipe");
+
+ private final String description;
+
+ AnsiType(String description) {
+ this.description = description;
+ }
+
+ String getDescription() {
+ return description;
+ }
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/WindowsSupport.java b/jansi-core/src/main/java/org/jline/jansi/WindowsSupport.java
new file mode 100644
index 000000000..9f30922c3
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/WindowsSupport.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi;
+
+@Deprecated
+public class WindowsSupport {
+
+ @Deprecated
+ public static String getLastErrorMessage() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Deprecated
+ public static String getErrorMessage(int errorCode) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/jansi-core/src/main/java/org/jline/jansi/io/AnsiOutputStream.java b/jansi-core/src/main/java/org/jline/jansi/io/AnsiOutputStream.java
new file mode 100644
index 000000000..b02103b7b
--- /dev/null
+++ b/jansi-core/src/main/java/org/jline/jansi/io/AnsiOutputStream.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2009-2023, the original author(s).
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+package org.jline.jansi.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+
+import org.jline.jansi.AnsiColors;
+import org.jline.jansi.AnsiMode;
+import org.jline.jansi.AnsiType;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+/**
+ * A ANSI print stream extracts ANSI escape codes written to
+ * an output stream and calls corresponding AnsiProcessor.process* methods.
+ * This particular class is not synchronized for improved performances.
+ *
+ *
For more information about ANSI escape codes, see
+ * Wikipedia article
+ *
+ * @since 1.0
+ * @see AnsiProcessor
+ */
+public class AnsiOutputStream extends FilterOutputStream {
+
+ public static final byte[] RESET_CODE = "\033[0m".getBytes(US_ASCII);
+
+ @FunctionalInterface
+ public interface IoRunnable {
+ void run() throws IOException;
+ }
+
+ @FunctionalInterface
+ public interface WidthSupplier {
+ int getTerminalWidth();
+ }
+
+ public static class ZeroWidthSupplier implements WidthSupplier {
+ @Override
+ public int getTerminalWidth() {
+ return 0;
+ }
+ }
+
+ private static final int LOOKING_FOR_FIRST_ESC_CHAR = 0;
+ private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1;
+ private static final int LOOKING_FOR_NEXT_ARG = 2;
+ private static final int LOOKING_FOR_STR_ARG_END = 3;
+ private static final int LOOKING_FOR_INT_ARG_END = 4;
+ private static final int LOOKING_FOR_OSC_COMMAND = 5;
+ private static final int LOOKING_FOR_OSC_COMMAND_END = 6;
+ private static final int LOOKING_FOR_OSC_PARAM = 7;
+ private static final int LOOKING_FOR_ST = 8;
+ private static final int LOOKING_FOR_CHARSET = 9;
+
+ private static final int FIRST_ESC_CHAR = 27;
+ private static final int SECOND_ESC_CHAR = '[';
+ private static final int SECOND_OSC_CHAR = ']';
+ private static final int BEL = 7;
+ private static final int SECOND_ST_CHAR = '\\';
+ private static final int SECOND_CHARSET0_CHAR = '(';
+ private static final int SECOND_CHARSET1_CHAR = ')';
+
+ private AnsiProcessor ap;
+ private static final int MAX_ESCAPE_SEQUENCE_LENGTH = 100;
+ private final byte[] buffer = new byte[MAX_ESCAPE_SEQUENCE_LENGTH];
+ private int pos = 0;
+ private int startOfValue;
+ private final ArrayList