8297587: Upgrade JLine to 3.22.0

Reviewed-by: vromero
This commit is contained in:
Jan Lahoda 2023-03-02 08:27:44 +00:00
parent 99f5687eb1
commit 4619e8bae8
54 changed files with 1320 additions and 717 deletions

View File

@ -14,7 +14,7 @@ package jdk.internal.org.jline.reader;
* @see Macro
* @see Reference
* @see Widget
* @see org.jline.keymap.KeyMap
* @see jdk.internal.org.jline.keymap.KeyMap
*
* @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
*/

View File

@ -24,6 +24,7 @@ public class Candidate implements Comparable<Candidate> {
private final String suffix;
private final String key;
private final boolean complete;
private final int sort;
/**
* Simple constructor with only a single String as an argument.
@ -31,7 +32,30 @@ public class Candidate implements Comparable<Candidate> {
* @param value the candidate
*/
public Candidate(String value) {
this(value, value, null, null, null, null, true);
this(value, value, null, null, null, null, true, 0);
}
/**
* Constructs a new Candidate.
*
* @param value the value
* @param displ the display string
* @param group the group
* @param descr the description
* @param suffix the suffix
* @param key the key
* @param complete the complete flag
* @param sort the sort flag
*/
public Candidate(String value, String displ, String group, String descr, String suffix, String key, boolean complete, int sort) {
this.value = Objects.requireNonNull(value);
this.displ = Objects.requireNonNull(displ);
this.group = group;
this.descr = descr;
this.suffix = suffix;
this.key = key;
this.complete = complete;
this.sort = sort;
}
/**
@ -46,13 +70,7 @@ public class Candidate implements Comparable<Candidate> {
* @param complete the complete flag
*/
public Candidate(String value, String displ, String group, String descr, String suffix, String key, boolean complete) {
this.value = Objects.requireNonNull(value);
this.displ = Objects.requireNonNull(displ);
this.group = group;
this.descr = descr;
this.suffix = suffix;
this.key = key;
this.complete = complete;
this(value, displ, group, descr, suffix, key, complete, 0);
}
/**
@ -133,9 +151,36 @@ public class Candidate implements Comparable<Candidate> {
return complete;
}
/**
* Integer used to override default sort logic.
* @return the sort int
*/
public int sort() {
return sort;
}
@Override
public int compareTo(Candidate o) {
return value.compareTo(o.value);
// If both candidates have same sort, use default behavior
if( sort == o.sort() ) {
return value.compareTo(o.value);
} else {
return Integer.compare(sort, o.sort());
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Candidate candidate = (Candidate) o;
return Objects.equals(value, candidate.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override

View File

@ -10,7 +10,7 @@ package jdk.internal.org.jline.reader;
/**
* An extension of {@link ParsedLine} that, being aware of the quoting and escaping rules
* of the {@link org.jline.reader.Parser} that produced it, knows if and how a completion candidate
* of the {@link jdk.internal.org.jline.reader.Parser} that produced it, knows if and how a completion candidate
* should be escaped/quoted.
*
* @author Eric Bottard

View File

@ -29,7 +29,7 @@ public interface CompletionMatcher {
/**
*
* @param candidates list of candidates
* @return a map of candidates that completion matcher matches
* @return a list of candidates that completion matcher matches
*/
List<Candidate> matches(List<Candidate> candidates);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2019, the original author or authors.
* Copyright (c) 2002-2021, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
@ -14,7 +14,28 @@ import jdk.internal.org.jline.utils.AttributedString;
public interface Highlighter {
/**
* Highlight buffer
* @param reader LineReader
* @param buffer the buffer to be highlighted
* @return highlighted buffer
*/
AttributedString highlight(LineReader reader, String buffer);
public void setErrorPattern(Pattern errorPattern);
public void setErrorIndex(int errorIndex);
/**
* Refresh highlight configuration
*/
default void refresh(LineReader reader) {}
/**
* Set error pattern to be highlighted
* @param errorPattern error pattern to be highlighted
*/
void setErrorPattern(Pattern errorPattern);
/**
* Set error index to be highlighted
* @param errorIndex error index to be highlighted
*/
void setErrorIndex(int errorIndex);
}

View File

@ -61,12 +61,13 @@ public interface History extends Iterable<History.Entry>
void append(Path file, boolean incremental) throws IOException;
/**
* Read history from the file. If incremental only the events that are not contained within the internal list are added.
* Read history from the file. If checkDuplicates is <code>true</code> only the events that
* are not contained within the internal list are added.
* @param file History file
* @param incremental If true incremental read operation is performed.
* @param checkDuplicates If <code>true</code>, duplicate history entries will be discarded
* @throws IOException if a problem occurs
*/
void read(Path file, boolean incremental) throws IOException;
void read(Path file, boolean checkDuplicates) throws IOException;
/**
* Purge history.

View File

@ -352,7 +352,7 @@ public interface LineReader {
String AMBIGUOUS_BINDING = "ambiguous-binding";
/**
* Columns separated list of patterns that will not be saved in history.
* Colon separated list of patterns that will not be saved in history.
*/
String HISTORY_IGNORE = "history-ignore";
@ -467,6 +467,9 @@ public interface LineReader {
/** Show command options tab completion candidates for zero length word */
EMPTY_WORD_OPTIONS(true),
/** Disable the undo feature */
DISABLE_UNDO
;
private final boolean def;
@ -699,7 +702,7 @@ public interface LineReader {
void runMacro(String macro);
/**
* Read a mouse event when the {@link org.jline.utils.InfoCmp.Capability#key_mouse} sequence
* Read a mouse event when the {@link jdk.internal.org.jline.utils.InfoCmp.Capability#key_mouse} sequence
* has just been read on the input stream.
* Compared to {@link Terminal#readMouseEvent()}, this method takes into account keys
* that have been pushed back using {@link #runMacro(String)}.

View File

@ -118,6 +118,12 @@ public final class LineReaderBuilder {
throw new IOError(e);
}
}
String appName = this.appName;
if (null == appName) {
appName = terminal.getName();
}
LineReaderImpl reader = new LineReaderImpl(terminal, appName, variables);
if (history != null) {
reader.setHistory(history);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2020, the original author or authors.
* Copyright (c) 2002-2021, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
@ -12,8 +12,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public interface Parser {
static final String REGEX_VARIABLE = "[a-zA-Z_]{1,}[a-zA-Z0-9_-]*";
static final String REGEX_COMMAND = "[:]{0,1}[a-zA-Z]{1,}[a-zA-Z0-9_-]*";
String REGEX_VARIABLE = "[a-zA-Z_]+[a-zA-Z0-9_-]*";
String REGEX_COMMAND = "[:]?[a-zA-Z]+[a-zA-Z0-9_-]*";
ParsedLine parse(String line, int cursor, ParseContext context) throws SyntaxError;
@ -34,7 +34,7 @@ public interface Parser {
}
default String getCommand(final String line) {
String out = "";
String out;
Pattern patternCommand = Pattern.compile("^\\s*" + REGEX_VARIABLE + "=(" + REGEX_COMMAND + ")(\\s+|$)");
Matcher matcher = patternCommand.matcher(line);
if (matcher.find()) {
@ -68,7 +68,7 @@ public interface Parser {
/** Parsed words will have all characters present in input line
* including quotes and escape chars.
* May throw EOFError in which case we have incomplete input.
* We should tolerate and ignore errors.
*/
SPLIT_LINE,

View File

@ -54,7 +54,7 @@ public class CompletionMatcherImpl implements CompletionMatcher {
break;
}
}
return !matching.isEmpty() ? matching.entrySet().stream().flatMap(e -> e.getValue().stream()).collect(Collectors.toList())
return !matching.isEmpty() ? matching.entrySet().stream().flatMap(e -> e.getValue().stream()).distinct().collect(Collectors.toList())
: new ArrayList<>();
}

View File

@ -27,6 +27,27 @@ public class DefaultParser implements Parser {
ANGLE // <>
}
public static class BlockCommentDelims {
private final String start;
private final String end;
public BlockCommentDelims(String start, String end) {
if (start == null || end == null
|| start.isEmpty() || end.isEmpty() || start.equals(end)) {
throw new IllegalArgumentException("Bad block comment delimiter!");
}
this.start = start;
this.end = end;
}
public String getStart() {
return start;
}
public String getEnd() {
return end;
}
}
private char[] quoteChars = {'\'', '"'};
private char[] escapeChars = {'\\'};
@ -39,6 +60,10 @@ public class DefaultParser implements Parser {
private char[] closingBrackets = null;
private String[] lineCommentDelims = null;
private BlockCommentDelims blockCommentDelims = null;
private String regexVariable = "[a-zA-Z_]+[a-zA-Z0-9_-]*((\\.|\\['|\\[\"|\\[)[a-zA-Z0-9_-]*(|']|\"]|]))?";
private String regexCommand = "[:]?[a-zA-Z]+[a-zA-Z0-9_-]*";
private int commandGroup = 4;
@ -47,6 +72,16 @@ public class DefaultParser implements Parser {
// Chainable setters
//
public DefaultParser lineCommentDelims(final String[] lineCommentDelims) {
this.lineCommentDelims = lineCommentDelims;
return this;
}
public DefaultParser blockCommentDelims(final BlockCommentDelims blockCommentDelims) {
this.blockCommentDelims = blockCommentDelims;
return this;
}
public DefaultParser quoteChars(final char[] chars) {
this.quoteChars = chars;
return this;
@ -107,6 +142,22 @@ public class DefaultParser implements Parser {
return this.escapeChars;
}
public void setLineCommentDelims(String[] lineCommentDelims) {
this.lineCommentDelims = lineCommentDelims;
}
public String[] getLineCommentDelims() {
return this.lineCommentDelims;
}
public void setBlockCommentDelims(BlockCommentDelims blockCommentDelims) {
this.blockCommentDelims = blockCommentDelims;
}
public BlockCommentDelims getBlockCommentDelims() {
return blockCommentDelims;
}
public void setEofOnUnclosedQuote(boolean eofOnUnclosedQuote) {
this.eofOnUnclosedQuote = eofOnUnclosedQuote;
}
@ -225,6 +276,11 @@ public class DefaultParser implements Parser {
int rawWordStart = 0;
BracketChecker bracketChecker = new BracketChecker(cursor);
boolean quotedWord = false;
boolean lineCommented = false;
boolean blockCommented = false;
boolean blockCommentInRightOrder = true;
final String blockCommentEnd = blockCommentDelims == null ? null : blockCommentDelims.end;
final String blockCommentStart = blockCommentDelims == null ? null : blockCommentDelims.start;
for (int i = 0; (line != null) && (i < line.length()); i++) {
// once we reach the cursor, set the
@ -237,7 +293,7 @@ public class DefaultParser implements Parser {
rawWordCursor = i - rawWordStart;
}
if (quoteStart < 0 && isQuoteChar(line, i)) {
if (quoteStart < 0 && isQuoteChar(line, i) && !lineCommented && !blockCommented) {
// Start a quote block
quoteStart = i;
if (current.length()==0) {
@ -258,17 +314,40 @@ public class DefaultParser implements Parser {
quoteStart = -1;
quotedWord = false;
} else if (quoteStart < 0 && isDelimiter(line, i)) {
// Delimiter
if (current.length() > 0) {
words.add(current.toString());
current.setLength(0); // reset the arg
if (rawWordCursor >= 0 && rawWordLength < 0) {
rawWordLength = i - rawWordStart;
if (lineCommented) {
if (isCommentDelim(line, i, System.lineSeparator())) {
lineCommented = false;
}
} else if (blockCommented) {
if (isCommentDelim(line, i, blockCommentEnd)) {
blockCommented = false;
}
} else {
// Delimiter
rawWordLength = handleDelimiterAndGetRawWordLength(current, words, rawWordStart, rawWordCursor, rawWordLength, i);
rawWordStart = i + 1;
}
rawWordStart = i + 1;
} else {
if (!isEscapeChar(line, i)) {
if (quoteStart < 0 && !blockCommented && (lineCommented || isLineCommentStarted(line, i))) {
lineCommented = true;
} else if (quoteStart < 0 && !lineCommented
&& (blockCommented || isCommentDelim(line, i, blockCommentStart))) {
if (blockCommented) {
if (blockCommentEnd != null && isCommentDelim(line, i, blockCommentEnd)) {
blockCommented = false;
i += blockCommentEnd.length() - 1;
}
} else {
blockCommented = true;
rawWordLength = handleDelimiterAndGetRawWordLength(current, words, rawWordStart, rawWordCursor, rawWordLength, i);
i += blockCommentStart == null ? 0 : blockCommentStart.length() - 1;
rawWordStart = i + 1;
}
} else if (quoteStart < 0 && !lineCommented
&& isCommentDelim(line, i, blockCommentEnd)) {
current.append(line.charAt(i));
blockCommentInRightOrder = false;
} else if (!isEscapeChar(line, i)) {
current.append(line.charAt(i));
if (quoteStart < 0) {
bracketChecker.check(line, i);
@ -301,6 +380,14 @@ public class DefaultParser implements Parser {
throw new EOFError(-1, -1, "Missing closing quote", line.charAt(quoteStart) == '\''
? "quote" : "dquote");
}
if (blockCommented) {
throw new EOFError(-1, -1, "Missing closing block comment delimiter",
"add: " + blockCommentEnd);
}
if (!blockCommentInRightOrder) {
throw new EOFError(-1, -1, "Missing opening block comment delimiter",
"missing: " + blockCommentStart);
}
if (bracketChecker.isClosingBracketMissing() || bracketChecker.isOpeningBracketMissing()) {
String message = null;
String missing = null;
@ -333,6 +420,17 @@ public class DefaultParser implements Parser {
return !isQuoted(buffer, pos) && !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos);
}
private int handleDelimiterAndGetRawWordLength(StringBuilder current, List<String> words, int rawWordStart, int rawWordCursor, int rawWordLength, int pos) {
if (current.length() > 0) {
words.add(current.toString());
current.setLength(0); // reset the arg
if (rawWordCursor >= 0 && rawWordLength < 0) {
return pos - rawWordStart;
}
}
return rawWordLength;
}
public boolean isQuoted(final CharSequence buffer, final int pos) {
return false;
}
@ -351,6 +449,36 @@ public class DefaultParser implements Parser {
return false;
}
private boolean isCommentDelim(final CharSequence buffer, final int pos, final String pattern) {
if (pos < 0) {
return false;
}
if (pattern != null) {
final int length = pattern.length();
if (length <= buffer.length() - pos) {
for (int i = 0; i < length; i++) {
if (pattern.charAt(i) != buffer.charAt(pos + i)) {
return false;
}
}
return true;
}
}
return false;
}
public boolean isLineCommentStarted(final CharSequence buffer, final int pos) {
if (lineCommentDelims != null) {
for (String comment: lineCommentDelims) {
if (isCommentDelim(buffer, pos, comment)) {
return true;
}
}
}
return false;
}
@Override
public boolean isEscapeChar(char ch) {
if (escapeChars != null) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2020, the original author or authors.
* Copyright (c) 2002-2022, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
@ -281,7 +281,7 @@ public class LineReaderImpl implements LineReader, Flushable
int candidateStartPosition = 0;
public LineReaderImpl(Terminal terminal) throws IOException {
this(terminal, null, null);
this(terminal, terminal.getName(), null);
}
public LineReaderImpl(Terminal terminal, String appName) throws IOException {
@ -633,7 +633,8 @@ public class LineReaderImpl implements LineReader, Flushable
callWidget(CALLBACK_INIT);
undo.newState(buf.copy());
if (!isSet(Option.DISABLE_UNDO))
undo.newState(buf.copy());
// Draw initial prompt
redrawLine();
@ -679,7 +680,7 @@ public class LineReaderImpl implements LineReader, Flushable
if (!w.apply()) {
beep();
}
if (!isUndo && copy != null && buf.length() <= getInt(FEATURES_MAX_BUFFER_SIZE, DEFAULT_FEATURES_MAX_BUFFER_SIZE)
if (!isSet(Option.DISABLE_UNDO) && !isUndo && copy != null && buf.length() <= getInt(FEATURES_MAX_BUFFER_SIZE, DEFAULT_FEATURES_MAX_BUFFER_SIZE)
&& !copy.toString().equals(buf.toString())) {
undo.newState(buf.copy());
}
@ -739,8 +740,8 @@ public class LineReaderImpl implements LineReader, Flushable
}
} finally {
lock.unlock();
startedReading.set(false);
}
startedReading.set(false);
}
}
@ -1082,18 +1083,18 @@ public class LineReaderImpl implements LineReader, Flushable
if (isSet(Option.BRACKETED_PASTE)) {
terminal.writer().write(BRACKETED_PASTE_OFF);
}
Constructor<?> ctor = Class.forName("org.jline.builtins.Nano").getConstructor(Terminal.class, File.class);
Constructor<?> ctor = Class.forName("jdk.internal.org.jline.builtins.Nano").getConstructor(Terminal.class, File.class);
Editor editor = (Editor) ctor.newInstance(terminal, new File(file.getParent()));
editor.setRestricted(true);
editor.open(Collections.singletonList(file.getName()));
editor.run();
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
commandsBuffer.clear();
while ((line = br.readLine()) != null) {
commandsBuffer.add(line);
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
commandsBuffer.clear();
while ((line = br.readLine()) != null) {
commandsBuffer.add(line);
}
}
br.close();
}
//
@ -3595,9 +3596,9 @@ public class LineReaderImpl implements LineReader, Flushable
File file = null;
try {
file = File.createTempFile("jline-execute-", null);
FileWriter writer = new FileWriter(file);
writer.write(buf.toString());
writer.close();
try (FileWriter writer = new FileWriter(file)) {
writer.write(buf.toString());
}
editAndAddInBuffer(file);
} catch (Exception e) {
e.printStackTrace(terminal.writer());
@ -3796,6 +3797,9 @@ public class LineReaderImpl implements LineReader, Flushable
Status status = Status.getStatus(terminal, false);
if (status != null) {
if (terminal.getType().startsWith(AbstractWindowsTerminal.TYPE_WINDOWS)) {
status.resize();
}
status.redraw();
}
@ -3947,7 +3951,8 @@ public class LineReaderImpl implements LineReader, Flushable
StringBuilder sb = new StringBuilder();
for (char c: buffer.replace("\\", "\\\\").toCharArray()) {
if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '*'
|| c == '$' || c == '.' || c == '?' || c == '+') {
|| c == '$' || c == '.' || c == '?' || c == '+' || c == '|' || c == '<' || c == '>' || c == '!'
|| c == '-') {
sb.append('\\');
}
sb.append(c);
@ -4520,7 +4525,7 @@ public class LineReaderImpl implements LineReader, Flushable
}
}
private CompletingParsedLine wrap(ParsedLine line) {
protected static CompletingParsedLine wrap(ParsedLine line) {
if (line instanceof CompletingParsedLine) {
return (CompletingParsedLine) line;
} else {
@ -4625,6 +4630,11 @@ public class LineReaderImpl implements LineReader, Flushable
return size.getRows() - (status != null ? status.size() : 0);
}
private int visibleDisplayRows() {
Status status = Status.getStatus(terminal, false);
return terminal.getSize().getRows() - (status != null ? status.size() : 0);
}
private int promptLines() {
AttributedString text = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>());
return text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size();
@ -5070,18 +5080,19 @@ public class LineReaderImpl implements LineReader, Flushable
protected PostResult computePost(List<Candidate> possible, Candidate selection, List<Candidate> ordered, String completed, Function<String, Integer> wcwidth, int width, boolean autoGroup, boolean groupName, boolean rowsFirst) {
List<Object> strings = new ArrayList<>();
boolean customOrder = possible.stream().anyMatch(c -> c.sort() != 0);
if (groupName) {
Comparator<String> groupComparator = getGroupComparator();
Map<String, Map<String, Candidate>> sorted;
Map<String, Map<Object, Candidate>> sorted;
sorted = groupComparator != null
? new TreeMap<>(groupComparator)
: new LinkedHashMap<>();
for (Candidate cand : possible) {
String group = cand.group();
sorted.computeIfAbsent(group != null ? group : "", s -> new LinkedHashMap<>())
.put(cand.value(), cand);
.put((customOrder ? cand.sort() : cand.value()), cand);
}
for (Map.Entry<String, Map<String, Candidate>> entry : sorted.entrySet()) {
for (Map.Entry<String, Map<Object, Candidate>> entry : sorted.entrySet()) {
String group = entry.getKey();
if (group.isEmpty() && sorted.size() > 1) {
group = getOthersGroupName();
@ -5096,13 +5107,13 @@ public class LineReaderImpl implements LineReader, Flushable
}
} else {
Set<String> groups = new LinkedHashSet<>();
TreeMap<String, Candidate> sorted = new TreeMap<>();
TreeMap<Object, Candidate> sorted = new TreeMap<>();
for (Candidate cand : possible) {
String group = cand.group();
if (group != null) {
groups.add(group);
}
sorted.put(cand.value(), cand);
sorted.put((customOrder ? cand.sort() : cand.value()), cand);
}
if (autoGroup) {
strings.addAll(groups);
@ -5129,7 +5140,7 @@ public class LineReaderImpl implements LineReader, Flushable
this.startPos = startPos;
endLine = line.substring(line.lastIndexOf('\n') + 1);
boolean first = true;
while (endLine.length() + (first ? startPos : 0) > width) {
while (endLine.length() + (first ? startPos : 0) > width && width > 0) {
if (first) {
endLine = endLine.substring(width - startPos);
} else {
@ -5207,7 +5218,7 @@ public class LineReaderImpl implements LineReader, Flushable
AttributedStringBuilder sb = new AttributedStringBuilder();
if (listSize > 0) {
if (isSet(Option.AUTO_MENU_LIST)
&& listSize < Math.min(getInt(MENU_LIST_MAX, DEFAULT_MENU_LIST_MAX), displayRows() - promptLines())) {
&& listSize < Math.min(getInt(MENU_LIST_MAX, DEFAULT_MENU_LIST_MAX), visibleDisplayRows() - promptLines())) {
maxWidth = Math.max(maxWidth, MENU_LIST_WIDTH);
sb.tabs(Math.max(Math.min(candidateStartPosition, width - maxWidth - 1), 1));
width = maxWidth + 2;

View File

@ -41,7 +41,7 @@ import jdk.internal.org.jline.utils.AttributedStyle;
* @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @since 2.3
* @deprecated use <code>org.jline.builtins.Completers$FileNameCompleter</code> instead
* @deprecated use <code>jdk.internal.org.jline.builtins.Completers$FileNameCompleter</code> instead
*/
@Deprecated
public class FileNameCompleter implements Completer

View File

@ -67,7 +67,7 @@ public class SystemCompleter implements Completer {
if (cmd != null) {
if (completers.containsKey(cmd)) {
out = cmd;
} else if (aliasCommand.containsKey(cmd)) {
} else {
out = aliasCommand.get(cmd);
}
}

View File

@ -97,14 +97,14 @@ public class DefaultHistory implements History {
}
@Override
public void read(Path file, boolean incremental) throws IOException {
public void read(Path file, boolean checkDuplicates) throws IOException {
Path path = file != null ? file : getPath();
if (path != null) {
try {
if (Files.exists(path)) {
Log.trace("Reading history from: ", path);
try (BufferedReader reader = Files.newBufferedReader(path)) {
reader.lines().forEach(line -> addHistoryLine(path, line, incremental));
reader.lines().forEach(line -> addHistoryLine(path, line, checkDuplicates));
setHistoryFileData(path, new HistoryFileData(items.size(), offset + items.size()));
maybeResize();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2020, the original author or authors.
* Copyright (c) 2002-2021, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
@ -16,22 +16,24 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.org.jline.terminal.impl.AbstractPosixTerminal;
import jdk.internal.org.jline.terminal.impl.AbstractTerminal;
import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal;
import jdk.internal.org.jline.terminal.impl.DumbTerminal;
import jdk.internal.org.jline.terminal.impl.ExecPty;
import jdk.internal.org.jline.terminal.impl.ExternalTerminal;
import jdk.internal.org.jline.terminal.impl.PosixPtyTerminal;
import jdk.internal.org.jline.terminal.impl.PosixSysTerminal;
import jdk.internal.org.jline.terminal.spi.JansiSupport;
import jdk.internal.org.jline.terminal.spi.JnaSupport;
import jdk.internal.org.jline.terminal.spi.Pty;
import jdk.internal.org.jline.terminal.spi.TerminalProvider;
import jdk.internal.org.jline.utils.Log;
import jdk.internal.org.jline.utils.OSUtils;
@ -52,6 +54,11 @@ public final class TerminalBuilder {
public static final String PROP_EXEC = "org.jline.terminal.exec";
public static final String PROP_DUMB = "org.jline.terminal.dumb";
public static final String PROP_DUMB_COLOR = "org.jline.terminal.dumb.color";
public static final String PROP_OUTPUT = "org.jline.terminal.output";
public static final String PROP_OUTPUT_OUT = "out";
public static final String PROP_OUTPUT_ERR = "err";
public static final String PROP_OUTPUT_OUT_ERR = "out-err";
public static final String PROP_OUTPUT_ERR_OUT = "err-out";
//
// Other system properties controlling various jline parts
@ -61,6 +68,16 @@ public final class TerminalBuilder {
public static final String PROP_COLOR_DISTANCE = "org.jline.utils.colorDistance";
public static final String PROP_DISABLE_ALTERNATE_CHARSET = "org.jline.utils.disableAlternateCharset";
//
// Terminal output control
//
public enum SystemOutput {
SysOut,
SysErr,
SysOutOrSysErr,
SysErrOrSysOut
}
/**
* Returns the default system terminal.
* Terminals should be closed properly using the {@link Terminal#close()}
@ -97,6 +114,7 @@ public final class TerminalBuilder {
private Charset encoding;
private int codepage;
private Boolean system;
private SystemOutput systemOutput;
private Boolean jna;
private Boolean jansi;
private Boolean exec;
@ -128,6 +146,20 @@ public final class TerminalBuilder {
return this;
}
/**
* Indicates which standard stream should be used when displaying to the terminal.
* The default is to use the system output stream.
* Building a system terminal will fail if one of the stream specified is not linked
* to the controlling terminal.
*
* @param systemOutput The mode to choose the output stream.
* @return The builder.
*/
public TerminalBuilder systemOutput(SystemOutput systemOutput) {
this.systemOutput = systemOutput;
return this;
}
public TerminalBuilder jna(boolean jna) {
this.jna = jna;
return this;
@ -298,11 +330,18 @@ public final class TerminalBuilder {
encoding = Charset.forName(charsetName);
}
}
int codepage = this.codepage;
if (codepage <= 0) {
String str = System.getProperty(PROP_CODEPAGE);
if (str != null) {
codepage = Integer.parseInt(str);
if (encoding == null) {
int codepage = this.codepage;
if (codepage <= 0) {
String str = System.getProperty(PROP_CODEPAGE);
if (str != null) {
codepage = Integer.parseInt(str);
}
}
if (codepage >= 0) {
encoding = getCodepageCharset(codepage);
} else {
encoding = StandardCharsets.UTF_8;
}
}
String type = this.type;
@ -328,102 +367,112 @@ public final class TerminalBuilder {
if (dumb == null) {
dumb = getBoolean(PROP_DUMB, null);
}
IllegalStateException exception = new IllegalStateException("Unable to create a terminal");
List<TerminalProvider> providers = new ArrayList<>();
if (jna) {
try {
TerminalProvider provider = TerminalProvider.load("jna");
providers.add(provider);
} catch (Throwable t) {
Log.debug("Unable to load JNA support: ", t);
exception.addSuppressed(t);
}
}
if (jansi) {
try {
TerminalProvider provider = TerminalProvider.load("jansi");
providers.add(provider);
} catch (Throwable t) {
Log.debug("Unable to load JANSI support: ", t);
exception.addSuppressed(t);
}
}
if (exec)
{
try {
TerminalProvider provider = TerminalProvider.load("exec");
providers.add(provider);
} catch (Throwable t) {
Log.debug("Unable to load EXEC support: ", t);
exception.addSuppressed(t);
}
}
Terminal terminal = null;
if ((system != null && system) || (system == null && in == null && out == null)) {
if (system != null && ((in != null && !in.equals(System.in)) || (out != null && !out.equals(System.out)))) {
if (system != null && ((in != null && !in.equals(System.in)) ||
(out != null && !out.equals(System.out) && !out.equals(System.err)))) {
throw new IllegalArgumentException("Cannot create a system terminal using non System streams");
}
Terminal terminal = null;
IllegalStateException exception = new IllegalStateException("Unable to create a system terminal");
TerminalBuilderSupport tbs = new TerminalBuilderSupport(jna, jansi);
if (tbs.isConsoleInput() && tbs.isConsoleOutput()) {
if (attributes != null || size != null) {
Log.warn("Attributes and size fields are ignored when creating a system terminal");
}
if (out != null) {
if (out.equals(System.out)) {
systemOutput = SystemOutput.SysOut;
} else if (out.equals(System.err)) {
systemOutput = SystemOutput.SysErr;
}
}
if (systemOutput == null) {
String str = System.getProperty(PROP_OUTPUT);
if (str != null) {
switch (str.trim().toLowerCase(Locale.ROOT)) {
case PROP_OUTPUT_OUT: systemOutput = SystemOutput.SysOut; break;
case PROP_OUTPUT_ERR: systemOutput = SystemOutput.SysErr; break;
case PROP_OUTPUT_OUT_ERR: systemOutput = SystemOutput.SysOutOrSysErr; break;
case PROP_OUTPUT_ERR_OUT: systemOutput = SystemOutput.SysErrOrSysOut; break;
default:
Log.debug("Unsupported value for " + PROP_OUTPUT + ": " + str + ". Supported values are: "
+ String.join(", ", PROP_OUTPUT_OUT, PROP_OUTPUT_ERR, PROP_OUTPUT_OUT_ERR,PROP_OUTPUT_ERR_OUT)
+ ".");
}
}
}
if (systemOutput == null) {
systemOutput = SystemOutput.SysOutOrSysErr;
}
Map<TerminalProvider.Stream, Boolean> system = Stream.of(TerminalProvider.Stream.values())
.collect(Collectors.toMap(stream -> stream, stream -> providers.stream().anyMatch(p -> p.isSystemStream(stream))));
TerminalProvider.Stream console = select(system, systemOutput);
if (system.get(TerminalProvider.Stream.Input) && console != null) {
if (attributes != null || size != null) {
Log.warn("Attributes and size fields are ignored when creating a system terminal");
}
if (OSUtils.IS_WINDOWS) {
if (!OSUtils.IS_CYGWIN && !OSUtils.IS_MSYSTEM) {
boolean ansiPassThrough = OSUtils.IS_CONEMU;
if (tbs.hasJnaSupport()) {
try {
terminal = tbs.getJnaSupport().winSysTerminal(name, type, ansiPassThrough, encoding, codepage
, nativeSignals, signalHandler, paused, inputStreamWrapper);
} catch (Throwable t) {
Log.debug("Error creating JNA based terminal: ", t.getMessage(), t);
exception.addSuppressed(t);
}
}
if (terminal == null && tbs.hasJansiSupport()) {
try {
terminal = tbs.getJansiSupport().winSysTerminal(name, type, ansiPassThrough, encoding, codepage
, nativeSignals, signalHandler, paused);
} catch (Throwable t) {
Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t);
exception.addSuppressed(t);
}
}
} else if (exec) {
//
// Cygwin support
//
boolean ansiPassThrough = OSUtils.IS_CONEMU;
// Cygwin defaults to XTERM, but actually supports 256 colors,
// so if the value comes from the environment, change it to xterm-256color
if ((OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) && "xterm".equals(type)
&& this.type == null && System.getProperty(PROP_TYPE) == null) {
type = "xterm-256color";
}
for ( TerminalProvider provider : providers) {
if (terminal == null) {
try {
// Cygwin defaults to XTERM, but actually supports 256 colors,
// so if the value comes from the environment, change it to xterm-256color
if ("xterm".equals(type) && this.type == null && System.getProperty(PROP_TYPE) == null) {
type = "xterm-256color";
}
Pty pty = tbs.getExecPty();
terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler);
} catch (IOException e) {
// Ignore if not a tty
Log.debug("Error creating EXEC based terminal: ", e.getMessage(), e);
exception.addSuppressed(e);
}
}
if (terminal == null && !jna && !jansi && (dumb == null || !dumb)) {
throw new IllegalStateException("Unable to create a system terminal. On windows, either "
+ "JNA or JANSI library is required. Make sure to add one of those in the classpath.");
}
} else {
if (tbs.hasJnaSupport()) {
try {
Pty pty = tbs.getJnaSupport().current();
terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler);
terminal = provider.sysTerminal(name, type, ansiPassThrough, encoding,
nativeSignals, signalHandler, paused, console, inputStreamWrapper);
} catch (Throwable t) {
// ignore
Log.debug("Error creating JNA based terminal: ", t.getMessage(), t);
exception.addSuppressed(t);
}
}
if (terminal == null && tbs.hasJansiSupport()) {
try {
Pty pty = tbs.getJansiSupport().current();
terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler);
} catch (Throwable t) {
Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t);
exception.addSuppressed(t);
}
}
if (terminal == null && exec) {
try {
Pty pty = tbs.getExecPty();
terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler);
} catch (Throwable t) {
// Ignore if not a tty
Log.debug("Error creating EXEC based terminal: ", t.getMessage(), t);
Log.debug("Error creating " + provider.name() + " based terminal: ", t.getMessage(), t);
exception.addSuppressed(t);
}
}
}
if (terminal instanceof AbstractTerminal) {
AbstractTerminal t = (AbstractTerminal) terminal;
if (SYSTEM_TERMINAL.compareAndSet(null, t)) {
t.setOnClose(() -> SYSTEM_TERMINAL.compareAndSet(t, null));
} else {
exception.addSuppressed(new IllegalStateException("A system terminal is already running. " +
"Make sure to use the created system Terminal on the LineReaderBuilder if you're using one " +
"or that previously created system Terminals have been correctly closed."));
terminal.close();
terminal = null;
}
if (terminal == null && OSUtils.IS_WINDOWS && !jna && !jansi && (dumb == null || !dumb)) {
throw new IllegalStateException("Unable to create a system terminal. On windows, either "
+ "JNA or JANSI library is required. Make sure to add one of those in the classpath.");
}
}
if (terminal instanceof AbstractTerminal) {
AbstractTerminal t = (AbstractTerminal) terminal;
if (SYSTEM_TERMINAL.compareAndSet(null, t)) {
t.setOnClose(() -> SYSTEM_TERMINAL.compareAndSet(t, null));
} else {
exception.addSuppressed(new IllegalStateException("A system terminal is already running. " +
"Make sure to use the created system Terminal on the LineReaderBuilder if you're using one " +
"or that previously created system Terminals have been correctly closed."));
terminal.close();
terminal = null;
}
}
if (terminal == null && (dumb == null || dumb)) {
@ -433,7 +482,8 @@ public final class TerminalBuilder {
color = getBoolean(PROP_DUMB_COLOR, false);
// detect emacs using the env variable
if (!color) {
color = System.getenv("INSIDE_EMACS") != null;
String emacs = System.getenv("INSIDE_EMACS");
color = emacs != null && emacs.contains("comint");
}
// detect Intellij Idea
if (!color) {
@ -441,12 +491,13 @@ public final class TerminalBuilder {
color = command != null && command.contains("idea");
}
if (!color) {
color = tbs.isConsoleOutput() && System.getenv("TERM") != null;
color = system.get(TerminalProvider.Stream.Output) && System.getenv("TERM") != null;
}
if (!color && dumb == null) {
if (Log.isDebugEnabled()) {
Log.warn("input is tty: {}", tbs.isConsoleInput());
Log.warn("output is tty: {}", tbs.isConsoleOutput());
Log.warn("input is tty: {}", system.get(TerminalProvider.Stream.Input));
Log.warn("output is tty: {}", system.get(TerminalProvider.Stream.Output));
Log.warn("error is tty: {}", system.get(TerminalProvider.Stream.Error));
Log.warn("Creating a dumb terminal", exception);
} else {
Log.warn("Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)");
@ -454,33 +505,49 @@ public final class TerminalBuilder {
}
}
terminal = new DumbTerminal(name, color ? Terminal.TYPE_DUMB_COLOR : Terminal.TYPE_DUMB,
inputStreamWrapper.apply(new FileInputStream(FileDescriptor.in)),
new FileOutputStream(FileDescriptor.out),
new FileInputStream(FileDescriptor.in),
new FileOutputStream(console == TerminalProvider.Stream.Output ? FileDescriptor.out : FileDescriptor.err),
encoding, signalHandler);
}
if (terminal == null) {
throw exception;
}
return terminal;
} else {
if (jna) {
try {
Pty pty = load(JnaSupport.class).open(attributes, size);
return new PosixPtyTerminal(name, type, pty, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused);
} catch (Throwable t) {
Log.debug("Error creating JNA based terminal: ", t.getMessage(), t);
for ( TerminalProvider provider : providers) {
if (terminal == null) {
try {
terminal = provider.newTerminal(name, type, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused, attributes, size);
} catch (Throwable t) {
Log.debug("Error creating " + provider.name() + " based terminal: ", t.getMessage(), t);
exception.addSuppressed(t);
}
}
}
if (jansi) {
try {
Pty pty = load(JansiSupport.class).open(attributes, size);
return new PosixPtyTerminal(name, type, pty, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused);
} catch (Throwable t) {
Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t);
}
}
return new ExternalTerminal(name, type, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused, attributes, size);
}
if (terminal == null) {
throw exception;
}
return terminal;
}
private TerminalProvider.Stream select(Map<TerminalProvider.Stream, Boolean> system, SystemOutput systemOutput) {
switch (systemOutput) {
case SysOut:
return select(system, TerminalProvider.Stream.Output);
case SysErr:
return select(system, TerminalProvider.Stream.Error);
case SysOutOrSysErr:
return select(system, TerminalProvider.Stream.Output, TerminalProvider.Stream.Error);
case SysErrOrSysOut:
return select(system, TerminalProvider.Stream.Error, TerminalProvider.Stream.Output);
}
return null;
}
private static TerminalProvider.Stream select(Map<TerminalProvider.Stream, Boolean> system, TerminalProvider.Stream... streams) {
for (TerminalProvider.Stream s : streams) {
if (system.get(s)) {
return s;
}
}
return null;
}
private static String getParentProcessCommand() {
@ -512,6 +579,24 @@ public final class TerminalBuilder {
return ServiceLoader.load(clazz, clazz.getClassLoader()).iterator().next();
}
private static final int UTF8_CODE_PAGE = 65001;
private static Charset getCodepageCharset(int codepage) {
//http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
if (codepage == UTF8_CODE_PAGE) {
return StandardCharsets.UTF_8;
}
String charsetMS = "ms" + codepage;
if (Charset.isSupported(charsetMS)) {
return Charset.forName(charsetMS);
}
String charsetCP = "cp" + codepage;
if (Charset.isSupported(charsetCP)) {
return Charset.forName(charsetCP);
}
return Charset.defaultCharset();
}
/**
* Allows an application to override the result of {@link #build()}. The
* intended use case is to allow a container or server application to control
@ -521,7 +606,7 @@ public final class TerminalBuilder {
* build tool uses a <code>LineReader</code> to implement an interactive shell.
* One of its supported commands is <code>console</code> which invokes
* the scala REPL. The scala REPL also uses a <code>LineReader</code> and it
* is necessary to override the {@link Terminal} used by the REPL to
* is necessary to override the {@link Terminal} used by the the REPL to
* share the same {@link Terminal} instance used by sbt.
*
* <p>
@ -545,79 +630,4 @@ public final class TerminalBuilder {
TERMINAL_OVERRIDE.set(terminal);
}
private static class TerminalBuilderSupport {
private JansiSupport jansiSupport = null;
private JnaSupport jnaSupport = null;
private Pty pty = null;
private boolean consoleOutput;
TerminalBuilderSupport(boolean jna, boolean jansi) {
if (jna) {
try {
jnaSupport = load(JnaSupport.class);
consoleOutput = jnaSupport.isConsoleOutput();
} catch (Throwable e) {
jnaSupport = null;
Log.debug("jnaSupport.isConsoleOutput(): ", e);
}
}
if (jansi) {
try {
jansiSupport = load(JansiSupport.class);
consoleOutput = jansiSupport.isConsoleOutput();
} catch (Throwable e) {
jansiSupport = null;
Log.debug("jansiSupport.isConsoleOutput(): ", e);
}
}
if (jnaSupport == null && jansiSupport == null) {
try {
pty = ExecPty.current();
consoleOutput = true;
} catch (Exception e) {
Log.debug("ExecPty.current(): ", e);
}
}
}
public boolean isConsoleOutput() {
return consoleOutput;
}
public boolean isConsoleInput() {
if (pty != null) {
return true;
} else if (hasJnaSupport()) {
return jnaSupport.isConsoleInput();
} else if (hasJansiSupport()) {
return jansiSupport.isConsoleInput();
} else {
return false;
}
}
public boolean hasJnaSupport() {
return jnaSupport != null;
}
public boolean hasJansiSupport() {
return jansiSupport != null;
}
public JnaSupport getJnaSupport() {
return jnaSupport;
}
public JansiSupport getJansiSupport() {
return jansiSupport;
}
public Pty getExecPty() throws IOException {
if (pty == null) {
pty = ExecPty.current();
}
return pty;
}
}
}

View File

@ -86,11 +86,6 @@ public abstract class AbstractPty implements Pty {
}
}
@Override
public int readBuffered(byte[] b) throws IOException {
return in.read(b);
}
private void setNonBlocking() {
if (current == null
|| current.getControlChar(Attributes.ControlChar.VMIN) != 0

View File

@ -81,8 +81,8 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal {
protected boolean focusTracking = false;
private volatile boolean closing;
public AbstractWindowsTerminal(Writer writer, String name, String type, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
super(name, type, selectCharset(encoding, codepage), signalHandler);
public AbstractWindowsTerminal(Writer writer, String name, String type, Charset encoding, boolean nativeSignals, SignalHandler signalHandler, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
super(name, type, encoding, signalHandler);
NonBlockingPumpReader reader = NonBlocking.nonBlockingPumpReader();
this.slaveInputPipe = reader.getWriter();
this.input = inputStreamWrapper.apply(NonBlocking.nonBlockingStream(reader, encoding()));
@ -116,35 +116,6 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal {
}
}
private static Charset selectCharset(Charset encoding, int codepage) {
if (encoding != null) {
return encoding;
}
if (codepage >= 0) {
return getCodepageCharset(codepage);
}
// Use UTF-8 as default
return StandardCharsets.UTF_8;
}
private static Charset getCodepageCharset(int codepage) {
//http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
if (codepage == UTF8_CODE_PAGE) {
return StandardCharsets.UTF_8;
}
String charsetMS = "ms" + codepage;
if (Charset.isSupported(charsetMS)) {
return Charset.forName(charsetMS);
}
String charsetCP = "cp" + codepage;
if (Charset.isSupported(charsetCP)) {
return Charset.forName(charsetCP);
}
return Charset.defaultCharset();
}
@Override
public SignalHandler handle(Signal signal, SignalHandler handler) {
SignalHandler prev = super.handle(signal, handler);

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2022, the original author or authors.
*
* 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 jdk.internal.org.jline.terminal.impl;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.ServiceLoader;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.TimeUnit;
import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Terminal;
import jdk.internal.org.jline.terminal.spi.TerminalProvider;
import jdk.internal.org.jline.utils.OSUtils;
public class Diag {
public static void main(String[] args) {
diag(System.out);
}
static void diag(PrintStream out) {
out.println("System properties");
out.println("=================");
out.println("os.name = " + System.getProperty("os.name"));
out.println("OSTYPE = " + System.getenv("OSTYPE"));
out.println("MSYSTEM = " + System.getenv("MSYSTEM"));
out.println("PWD = " + System.getenv("PWD"));
out.println("ConEmuPID = " + System.getenv("ConEmuPID"));
out.println("WSL_DISTRO_NAME = " + System.getenv("WSL_DISTRO_NAME"));
out.println("WSL_INTEROP = " + System.getenv("WSL_INTEROP"));
out.println();
out.println("OSUtils");
out.println("=================");
out.println("IS_WINDOWS = " + OSUtils.IS_WINDOWS);
out.println("IS_CYGWIN = " + OSUtils.IS_CYGWIN);
out.println("IS_MSYSTEM = " + OSUtils.IS_MSYSTEM);
out.println("IS_WSL = " + OSUtils.IS_WSL);
out.println("IS_WSL1 = " + OSUtils.IS_WSL1);
out.println("IS_WSL2 = " + OSUtils.IS_WSL2);
out.println("IS_CONEMU = " + OSUtils.IS_CONEMU);
out.println("IS_OSX = " + OSUtils.IS_OSX);
out.println();
out.println("JnaSupport");
out.println("=================");
try {
TerminalProvider provider = TerminalProvider.load("jna");
testProvider(out, provider);
} catch (Throwable t) {
out.println("JNA support not available: " + t);
}
out.println();
out.println("JansiSupport");
out.println("=================");
try {
TerminalProvider provider = TerminalProvider.load("jansi");
testProvider(out, provider);
} catch (Throwable t) {
out.println("Jansi support not available: " + t);
}
out.println();
// Exec
out.println("Exec Support");
out.println("=================");
try {
TerminalProvider provider = TerminalProvider.load("exec");
testProvider(out, provider);
} catch (Throwable t) {
out.println("Exec support not available: " + t);
}
}
private static void testProvider(PrintStream out, TerminalProvider provider) {
try {
out.println("StdIn stream = " + provider.isSystemStream(TerminalProvider.Stream.Input));
out.println("StdOut stream = " + provider.isSystemStream(TerminalProvider.Stream.Output));
out.println("StdErr stream = " + provider.isSystemStream(TerminalProvider.Stream.Error));
} catch (Throwable t2) {
out.println("Unable to check stream: " + t2);
}
try {
out.println("StdIn stream name = " + provider.systemStreamName(TerminalProvider.Stream.Input));
out.println("StdOut stream name = " + provider.systemStreamName(TerminalProvider.Stream.Output));
out.println("StdErr stream name = " + provider.systemStreamName(TerminalProvider.Stream.Error));
} catch (Throwable t2) {
out.println("Unable to check stream names: " + t2);
}
try (Terminal terminal = provider.sysTerminal("diag", "xterm", false, StandardCharsets.UTF_8,
false, Terminal.SignalHandler.SIG_DFL, false, TerminalProvider.Stream.Output, input -> input) ) {
if (terminal != null) {
Attributes attr = terminal.enterRawMode();
try {
out.println("Terminal size: " + terminal.getSize());
ForkJoinTask<Integer> t = new ForkJoinPool(1).submit(() -> terminal.reader().read(1) );
int r = t.get(1000, TimeUnit.MILLISECONDS);
StringBuilder sb = new StringBuilder();
sb.append("The terminal seems to work: ");
sb.append("terminal ").append(terminal.getClass().getName());
if (terminal instanceof AbstractPosixTerminal) {
sb.append(" with pty ").append(((AbstractPosixTerminal) terminal).getPty().getClass().getName());
}
out.println(sb);
} catch (Throwable t3) {
out.println("Unable to read from terminal: " + t3);
t3.printStackTrace();
} finally {
terminal.setAttributes(attr);
}
} else {
out.println("Not supported by provider");
}
} catch (Throwable t2) {
out.println("Unable to open terminal: " + t2);
t2.printStackTrace();
}
}
static <S> S load(Class<S> clazz) {
return ServiceLoader.load(clazz, clazz.getClassLoader()).iterator().next();
}
}

View File

@ -26,6 +26,7 @@ import jdk.internal.org.jline.terminal.Attributes.InputFlag;
import jdk.internal.org.jline.terminal.Attributes.LocalFlag;
import jdk.internal.org.jline.terminal.Attributes.OutputFlag;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.spi.TerminalProvider;
import jdk.internal.org.jline.terminal.spi.Pty;
import jdk.internal.org.jline.utils.OSUtils;
@ -34,20 +35,23 @@ import static jdk.internal.org.jline.utils.ExecHelper.exec;
public class ExecPty extends AbstractPty implements Pty {
private final String name;
private final boolean system;
private final TerminalProvider.Stream console;
public static Pty current() throws IOException {
public static Pty current(TerminalProvider.Stream console) throws IOException {
try {
String result = exec(true, OSUtils.TTY_COMMAND);
return new ExecPty(result.trim(), true);
if (console != TerminalProvider.Stream.Output && console != TerminalProvider.Stream.Error) {
throw new IllegalArgumentException("console should be Output or Error: " + console);
}
return new ExecPty(result.trim(), console);
} catch (IOException e) {
throw new IOException("Not a tty", e);
}
}
protected ExecPty(String name, boolean system) {
protected ExecPty(String name, TerminalProvider.Stream console) {
this.name = name;
this.system = system;
this.console = console;
}
@Override
@ -70,16 +74,18 @@ public class ExecPty extends AbstractPty implements Pty {
@Override
protected InputStream doGetSlaveInput() throws IOException {
return system
return console != null
? new FileInputStream(FileDescriptor.in)
: new FileInputStream(getName());
}
@Override
public OutputStream getSlaveOutput() throws IOException {
return system
return console == TerminalProvider.Stream.Output
? new FileOutputStream(FileDescriptor.out)
: new FileOutputStream(getName());
: console == TerminalProvider.Stream.Error
? new FileOutputStream(FileDescriptor.err)
: new FileOutputStream(getName());
}
@Override
@ -93,23 +99,11 @@ public class ExecPty extends AbstractPty implements Pty {
List<String> commands = getFlagsToSet(attr, getAttr());
if (!commands.isEmpty()) {
commands.add(0, OSUtils.STTY_COMMAND);
if (!system) {
if (console == null) {
commands.add(1, OSUtils.STTY_F_OPTION);
commands.add(2, getName());
}
try {
exec(system, commands.toArray(new String[commands.size()]));
} catch (IOException e) {
// Handle partial failures with GNU stty, see #97
if (e.toString().contains("unable to perform all requested operations")) {
commands = getFlagsToSet(attr, getAttr());
if (!commands.isEmpty()) {
throw new IOException("Could not set the following flags: " + String.join(", ", commands), e);
}
} else {
throw e;
}
}
exec(console != null, commands.toArray(new String[0]));
}
}
@ -171,7 +165,7 @@ public class ExecPty extends AbstractPty implements Pty {
}
protected String doGetConfig() throws IOException {
return system
return console != null
? exec(true, OSUtils.STTY_COMMAND, "-a")
: exec(false, OSUtils.STTY_COMMAND, OSUtils.STTY_F_OPTION, getName(), "-a");
}
@ -280,7 +274,7 @@ public class ExecPty extends AbstractPty implements Pty {
@Override
public void setSize(Size size) throws IOException {
if (system) {
if (console != null) {
exec(true,
OSUtils.STTY_COMMAND,
"columns", Integer.toString(size.getColumns()),
@ -296,7 +290,7 @@ public class ExecPty extends AbstractPty implements Pty {
@Override
public String toString() {
return "ExecPty[" + getName() + (system ? ", system]" : "]");
return "ExecPty[" + getName() + (console != null ? ", system]" : "]");
}
}

View File

@ -15,7 +15,6 @@ import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.internal.org.jline.terminal.spi.Pty;
import jdk.internal.org.jline.utils.ClosedException;
@ -143,10 +142,10 @@ public class PosixPtyTerminal extends AbstractPosixTerminal {
}
}
private class InputStreamWrapper extends NonBlockingInputStream {
private static class InputStreamWrapper extends NonBlockingInputStream {
private final NonBlockingInputStream in;
private final AtomicBoolean closed = new AtomicBoolean();
private volatile boolean closed;
protected InputStreamWrapper(NonBlockingInputStream in) {
this.in = in;
@ -154,7 +153,7 @@ public class PosixPtyTerminal extends AbstractPosixTerminal {
@Override
public int read(long timeout, boolean isPeek) throws IOException {
if (closed.get()) {
if (closed) {
throw new ClosedException();
}
return in.read(timeout, isPeek);
@ -162,7 +161,7 @@ public class PosixPtyTerminal extends AbstractPosixTerminal {
@Override
public void close() throws IOException {
closed.set(true);
closed = true;
}
}

View File

@ -16,6 +16,7 @@ import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import jdk.internal.org.jline.utils.NonBlocking;
import jdk.internal.org.jline.terminal.spi.Pty;
@ -34,11 +35,12 @@ public class PosixSysTerminal extends AbstractPosixTerminal {
protected final Map<Signal, Object> nativeHandlers = new HashMap<>();
protected final Task closer;
public PosixSysTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, Charset encoding,
boolean nativeSignals, SignalHandler signalHandler) throws IOException {
public PosixSysTerminal(String name, String type, Pty pty, Charset encoding,
boolean nativeSignals, SignalHandler signalHandler,
Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
super(name, type, pty, encoding, signalHandler);
this.input = NonBlocking.nonBlocking(getName(), in);
this.output = out;
this.input = NonBlocking.nonBlocking(getName(), inputStreamWrapper.apply(pty.getSlaveInput()));
this.output = pty.getSlaveOutput();
this.reader = NonBlocking.nonBlocking(getName(), input, encoding());
this.writer = new PrintWriter(new OutputStreamWriter(output, encoding()));
parseInfoCmp();

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2022, the original author or authors.
*
* 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 jdk.internal.org.jline.terminal.impl.exec;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.function.Function;
import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.Terminal;
import jdk.internal.org.jline.terminal.impl.ExecPty;
import jdk.internal.org.jline.terminal.impl.ExternalTerminal;
import jdk.internal.org.jline.terminal.impl.PosixSysTerminal;
import jdk.internal.org.jline.terminal.spi.Pty;
import jdk.internal.org.jline.terminal.spi.TerminalProvider;
import jdk.internal.org.jline.utils.ExecHelper;
import jdk.internal.org.jline.utils.OSUtils;
public class ExecTerminalProvider implements TerminalProvider
{
public String name() {
return "exec";
}
public Pty current(Stream consoleStream) throws IOException {
return ExecPty.current(consoleStream);
}
@Override
public Terminal sysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
Stream consoleStream, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
if (OSUtils.IS_WINDOWS) {
return winSysTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, consoleStream, inputStreamWrapper );
} else {
return posixSysTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, consoleStream, inputStreamWrapper );
}
}
public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
Stream consoleStream, Function<InputStream, InputStream> inputStreamWrapper ) throws IOException {
if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) {
Pty pty = current(consoleStream);
return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler, inputStreamWrapper);
} else {
return null;
}
}
public Terminal posixSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
Stream consoleStream, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
Pty pty = current(consoleStream);
return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler, inputStreamWrapper);
}
@Override
public Terminal newTerminal(String name, String type, InputStream in, OutputStream out,
Charset encoding, Terminal.SignalHandler signalHandler, boolean paused,
Attributes attributes, Size size) throws IOException
{
return new ExternalTerminal(name, type, in, out, encoding, signalHandler, paused, attributes, size);
}
@Override
public boolean isSystemStream(Stream stream) {
try {
return isWindowsSystemStream(stream) || isPosixSystemStream(stream);
} catch (Throwable t) {
return false;
}
}
public boolean isWindowsSystemStream(Stream stream) {
return systemStreamName( stream ) != null;
}
public boolean isPosixSystemStream(Stream stream) {
try {
Process p = new ProcessBuilder(OSUtils.TEST_COMMAND, "-t", Integer.toString(stream.ordinal()))
.inheritIO().start();
return p.waitFor() == 0;
} catch (Throwable t) {
// ignore
}
return false;
}
@Override
public String systemStreamName(Stream stream) {
try {
ProcessBuilder.Redirect input = stream == Stream.Input
? ProcessBuilder.Redirect.INHERIT
: getRedirect(stream == Stream.Output ? FileDescriptor.out : FileDescriptor.err);
Process p = new ProcessBuilder(OSUtils.TTY_COMMAND).redirectInput(input).start();
String result = ExecHelper.waitAndCapture(p);
if (p.exitValue() == 0) {
return result.trim();
}
} catch (Throwable t) {
// ignore
}
return null;
}
private ProcessBuilder.Redirect getRedirect(FileDescriptor fd) throws ReflectiveOperationException {
// This is not really allowed, but this is the only way to redirect the output or error stream
// to the input. This is definitely not something you'd usually want to do, but in the case of
// the `tty` utility, it provides a way to get
Class<?> rpi = Class.forName("java.lang.ProcessBuilder$RedirectPipeImpl");
Constructor<?> cns = rpi.getDeclaredConstructor();
cns.setAccessible(true);
ProcessBuilder.Redirect input = (ProcessBuilder.Redirect) cns.newInstance();
Field f = rpi.getDeclaredField("fd");
f.setAccessible(true);
f.set(input, fd);
return input;
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2002-2020, the original author or authors.
*
* 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 jdk.internal.org.jline.terminal.spi;
import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.Terminal;
import java.io.IOException;
import java.nio.charset.Charset;
public interface JansiSupport {
Pty current() throws IOException;
Pty open(Attributes attributes, Size size) throws IOException;
Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler) throws IOException;
Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused) throws IOException;
boolean isWindowsConsole();
boolean isConsoleOutput();
boolean isConsoleInput();
}

View File

@ -1,37 +0,0 @@
/*
* Copyright (c) 2002-2020, the original author or authors.
*
* 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 jdk.internal.org.jline.terminal.spi;
import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.Terminal;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.function.Function;
public interface JnaSupport {
Pty current() throws IOException;
Pty open(Attributes attributes, Size size) throws IOException;
Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler) throws IOException;
Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused) throws IOException;
Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused, Function<InputStream, InputStream> inputStreamWrapper) throws IOException;
boolean isWindowsConsole();
boolean isConsoleOutput();
boolean isConsoleInput();
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2022, the original author or authors.
*
* 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 jdk.internal.org.jline.terminal.spi;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.function.Function;
import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.Terminal;
import jdk.internal.org.jline.terminal.impl.exec.ExecTerminalProvider;
public interface TerminalProvider
{
enum Stream {
Input,
Output,
Error
}
String name();
Terminal sysTerminal(String name, String type, boolean ansiPassThrough,
Charset encoding, boolean nativeSignals,
Terminal.SignalHandler signalHandler, boolean paused,
Stream consoleStream, Function<InputStream, InputStream> inputStreamWrapper) throws IOException;
Terminal newTerminal(String name, String type,
InputStream masterInput, OutputStream masterOutput,
Charset encoding, Terminal.SignalHandler signalHandler,
boolean paused, Attributes attributes, Size size) throws IOException;
boolean isSystemStream(Stream stream);
String systemStreamName(Stream stream);
static TerminalProvider load(String name) throws IOException {
switch (name) {
case "exec": return new ExecTerminalProvider();
case "jna": {
try {
return (TerminalProvider) Class.forName("jdk.internal.org.jline.terminal.impl.jna.JnaTerminalProvider").getConstructor().newInstance();
} catch (ReflectiveOperationException t) {
throw new IOException(t);
}
}
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
}
InputStream is = cl.getResourceAsStream( "META-INF/services/org/jline/terminal/provider/" + name);
if (is != null) {
Properties props = new Properties();
try {
props.load(is);
String className = props.getProperty("class");
if (className == null) {
throw new IOException("No class defined in terminal provider file " + name);
}
Class<?> clazz = cl.loadClass( className );
return (TerminalProvider) clazz.getConstructor().newInstance();
} catch ( Exception e ) {
throw new IOException("Unable to load terminal provider " + name, e);
}
} else {
throw new IOException("Unable to find terminal provider " + name);
}
}
}

View File

@ -534,7 +534,7 @@ public class Colors {
H = 0;
return H;
} else {
throw new IllegalArgumentException("h outside assumed range 0..360: " + Double.toString(h));
throw new IllegalArgumentException("h outside assumed range 0..360: " + h);
}
}

View File

@ -12,7 +12,7 @@ import java.io.Flushable;
import java.io.IOError;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Stack;
import java.util.ArrayDeque;
/**
* Curses helper methods.
@ -21,8 +21,8 @@ import java.util.Stack;
*/
public final class Curses {
private static Object[] sv = new Object[26];
private static Object[] dv = new Object[26];
private static final Object[] sv = new Object[26];
private static final Object[] dv = new Object[26];
private static final int IFTE_NONE = 0;
private static final int IFTE_IF = 1;
@ -68,7 +68,7 @@ public final class Curses {
int length = str.length();
int ifte = IFTE_NONE;
boolean exec = true;
Stack<Object> stack = new Stack<>();
ArrayDeque<Object> stack = new ArrayDeque<>();
while (index < length) {
char ch = str.charAt(index++);
switch (ch) {
@ -197,7 +197,7 @@ public final class Curses {
int start = index;
while (str.charAt(index++) != '}') ;
if (exec) {
int v = Integer.valueOf(str.substring(start, index - 1));
int v = Integer.parseInt(str.substring(start, index - 1));
stack.push(v);
}
break;
@ -470,7 +470,7 @@ public final class Curses {
} else if (pop instanceof Boolean) {
return (Boolean) pop ? 1 : 0;
} else {
return Integer.valueOf(pop.toString());
return Integer.parseInt(pop.toString());
}
}

View File

@ -187,7 +187,7 @@ public class Display {
int lineIndex = 0;
int currentPos = 0;
int numLines = Math.max(oldLines.size(), newLines.size());
int numLines = Math.min(rows, Math.max(oldLines.size(), newLines.size()));
boolean wrapNeeded = false;
while (lineIndex < numLines) {
AttributedString oldLine =

View File

@ -503,7 +503,7 @@ public final class InfoCmp {
public String[] getNames() {
return getCapabilitiesByName().entrySet().stream()
.filter(e -> e.getValue() == this)
.map(Map.Entry::getValue)
.map(Map.Entry::getKey)
.toArray(String[]::new);
}

View File

@ -95,13 +95,9 @@ public class NonBlocking {
@Override
public int read(long timeout, boolean isPeek) throws IOException {
boolean isInfinite = (timeout <= 0L);
while (!bytes.hasRemaining() && (isInfinite || timeout > 0L)) {
long start = 0;
if (!isInfinite) {
start = System.currentTimeMillis();
}
int c = reader.read(timeout);
Timeout t = new Timeout(timeout);
while (!bytes.hasRemaining() && !t.elapsed()) {
int c = reader.read(t.timeout());
if (c == EOF) {
return EOF;
}
@ -117,9 +113,6 @@ public class NonBlocking {
encoder.encode(chars, bytes, false);
bytes.flip();
}
if (!isInfinite) {
timeout -= System.currentTimeMillis() - start;
}
}
if (bytes.hasRemaining()) {
if (isPeek) {
@ -151,21 +144,17 @@ public class NonBlocking {
public NonBlockingInputStreamReader(NonBlockingInputStream input, CharsetDecoder decoder) {
this.input = input;
this.decoder = decoder;
this.bytes = ByteBuffer.allocate(4);
this.chars = CharBuffer.allocate(2);
this.bytes = ByteBuffer.allocate(2048);
this.chars = CharBuffer.allocate(1024);
this.bytes.limit(0);
this.chars.limit(0);
}
@Override
protected int read(long timeout, boolean isPeek) throws IOException {
boolean isInfinite = (timeout <= 0L);
while (!chars.hasRemaining() && (isInfinite || timeout > 0L)) {
long start = 0;
if (!isInfinite) {
start = System.currentTimeMillis();
}
int b = input.read(timeout);
Timeout t = new Timeout(timeout);
while (!chars.hasRemaining() && !t.elapsed()) {
int b = input.read(t.timeout());
if (b == EOF) {
return EOF;
}
@ -181,10 +170,6 @@ public class NonBlocking {
decoder.decode(bytes, chars, false);
chars.flip();
}
if (!isInfinite) {
timeout -= System.currentTimeMillis() - start;
}
}
if (chars.hasRemaining()) {
if (isPeek) {
@ -198,46 +183,37 @@ public class NonBlocking {
}
@Override
public int readBuffered(char[] b) throws IOException {
public int readBuffered(char[] b, int off, int len, long timeout) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (b.length == 0) {
} else if (off < 0 || len < 0 || off + len < b.length) {
throw new IllegalArgumentException();
} else if (len == 0) {
return 0;
} else if (chars.hasRemaining()) {
int r = Math.min(len, chars.remaining());
chars.get(b, off, r);
return r;
} else {
if (chars.hasRemaining()) {
int r = Math.min(b.length, chars.remaining());
chars.get(b);
return r;
} else {
byte[] buf = new byte[b.length];
int l = input.readBuffered(buf);
if (l < 0) {
return l;
} else {
ByteBuffer currentBytes;
if (bytes.hasRemaining()) {
int transfer = bytes.remaining();
byte[] newBuf = new byte[l + transfer];
bytes.get(newBuf, 0, transfer);
System.arraycopy(buf, 0, newBuf, transfer, l);
currentBytes = ByteBuffer.wrap(newBuf);
bytes.position(0);
bytes.limit(0);
} else {
currentBytes = ByteBuffer.wrap(buf, 0, l);
}
CharBuffer chars = CharBuffer.wrap(b);
decoder.decode(currentBytes, chars, false);
chars.flip();
if (currentBytes.hasRemaining()) {
int pos = bytes.position();
bytes.limit(bytes.limit() + currentBytes.remaining());
bytes.put(currentBytes);
bytes.position(pos);
}
return chars.remaining();
Timeout t = new Timeout(timeout);
while (!chars.hasRemaining() && !t.elapsed()) {
if (!bytes.hasRemaining()) {
bytes.position(0);
bytes.limit(0);
}
int nb = input.readBuffered(bytes.array(), bytes.limit(),
bytes.capacity() - bytes.limit(), t.timeout());
if (nb < 0) {
return nb;
}
bytes.limit(bytes.limit() + nb);
chars.clear();
decoder.decode(bytes, chars, false);
chars.flip();
}
int nb = Math.min(len, chars.remaining());
chars.get(b, off, nb);
return nb;
}
}

View File

@ -79,12 +79,34 @@ public abstract class NonBlockingInputStream extends InputStream {
}
public int readBuffered(byte[] b) throws IOException {
return readBuffered(b, 0L);
}
public int readBuffered(byte[] b, long timeout) throws IOException {
return readBuffered(b, 0, b.length, timeout);
}
public int readBuffered(byte[] b, int off, int len, long timeout) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (b.length == 0) {
} else if (off < 0 || len < 0 || off + len < b.length) {
throw new IllegalArgumentException();
} else if (len == 0) {
return 0;
} else {
return super.read(b, 0, b.length);
Timeout t = new Timeout(timeout);
int nb = 0;
while (!t.elapsed()) {
int r = read(nb > 0 ? 1 : t.timeout());
if (r < 0) {
return nb > 0 ? nb : r;
}
b[off + nb++] = (byte) r;
if (nb >= len || t.isInfinite()) {
break;
}
}
return nb;
}
}

View File

@ -123,20 +123,17 @@ public class NonBlockingInputStreamImpl
notifyAll();
}
boolean isInfinite = (timeout <= 0L);
/*
* So the thread is currently doing the reading for us. So
* now we play the waiting game.
*/
while (isInfinite || timeout > 0L) {
long start = System.currentTimeMillis ();
Timeout t = new Timeout(timeout);
while (!t.elapsed()) {
try {
if (Thread.interrupted()) {
throw new InterruptedException();
}
wait(timeout);
wait(t.timeout());
}
catch (InterruptedException e) {
exception = (IOException) new InterruptedIOException().initCause(e);
@ -155,10 +152,6 @@ public class NonBlockingInputStreamImpl
assert exception == null;
break;
}
if (!isInfinite) {
timeout -= System.currentTimeMillis() - start;
}
}
}

View File

@ -45,24 +45,17 @@ public class NonBlockingPumpInputStream extends NonBlockingInputStream {
}
private int wait(ByteBuffer buffer, long timeout) throws IOException {
boolean isInfinite = (timeout <= 0L);
long end = 0;
if (!isInfinite) {
end = System.currentTimeMillis() + timeout;
}
while (!closed && !buffer.hasRemaining() && (isInfinite || timeout > 0L)) {
Timeout t = new Timeout(timeout);
while (!closed && !buffer.hasRemaining() && !t.elapsed()) {
// Wake up waiting readers/writers
notifyAll();
try {
wait(timeout);
wait(t.timeout());
checkIoException();
} catch (InterruptedException e) {
checkIoException();
throw new InterruptedIOException();
}
if (!isInfinite) {
timeout = end - System.currentTimeMillis();
}
}
return buffer.hasRemaining()
? 0
@ -107,17 +100,25 @@ public class NonBlockingPumpInputStream extends NonBlockingInputStream {
}
@Override
public synchronized int readBuffered(byte[] b) throws IOException {
checkIoException();
int res = wait(readBuffer, 0L);
if (res >= 0) {
res = 0;
while (res < b.length && readBuffer.hasRemaining()) {
b[res++] = (byte) (readBuffer.get() & 0x00FF);
public synchronized int readBuffered(byte[] b, int off, int len, long timeout) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || off + len < b.length) {
throw new IllegalArgumentException();
} else if (len == 0) {
return 0;
} else {
checkIoException();
int res = wait(readBuffer, timeout);
if (res >= 0) {
res = 0;
while (res < len && readBuffer.hasRemaining()) {
b[off + res++] = (byte) (readBuffer.get() & 0x00FF);
}
}
rewind(readBuffer, writeBuffer);
return res;
}
rewind(readBuffer, writeBuffer);
return res;
}
public synchronized void setIoException(IOException exception) {

View File

@ -106,10 +106,12 @@ public class NonBlockingPumpReader extends NonBlockingReader {
}
@Override
public int readBuffered(char[] b) throws IOException {
public int readBuffered(char[] b, int off, int len, long timeout) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (b.length == 0) {
} else if (off < 0 || len < 0 || off + len < b.length) {
throw new IllegalArgumentException();
} else if (len == 0) {
return 0;
} else {
final ReentrantLock lock = this.lock;
@ -117,7 +119,13 @@ public class NonBlockingPumpReader extends NonBlockingReader {
try {
if (!closed && count == 0) {
try {
notEmpty.await();
if (timeout > 0) {
if (!notEmpty.await(timeout, TimeUnit.MILLISECONDS)) {
throw new IOException( "Timeout reading" );
}
} else {
notEmpty.await();
}
} catch (InterruptedException e) {
throw (IOException) new InterruptedIOException().initCause(e);
}
@ -127,9 +135,9 @@ public class NonBlockingPumpReader extends NonBlockingReader {
} else if (count == 0) {
return READ_EXPIRED;
} else {
int r = Math.min(b.length, count);
int r = Math.min(len, count);
for (int i = 0; i < r; i++) {
b[i] = buffer[read++];
b[off + i] = buffer[read++];
if (read == buffer.length) {
read = 0;
}

View File

@ -85,7 +85,15 @@ public abstract class NonBlockingReader extends Reader {
return 1;
}
public abstract int readBuffered(char[] b) throws IOException;
public int readBuffered(char[] b) throws IOException {
return readBuffered(b, 0L);
}
public int readBuffered(char[] b, long timeout) throws IOException {
return readBuffered(b, 0, b.length, timeout);
}
public abstract int readBuffered(char[] b, int off, int len, long timeout) throws IOException;
public int available() {
return 0;

View File

@ -91,10 +91,12 @@ public class NonBlockingReaderImpl
}
@Override
public int readBuffered(char[] b) throws IOException {
public int readBuffered(char[] b, int off, int len, long timeout) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (b.length == 0) {
} else if (off < 0 || len < 0 || off + len < b.length) {
throw new IllegalArgumentException();
} else if (len == 0) {
return 0;
} else if (exception != null) {
assert ch == READ_EXPIRED;
@ -105,15 +107,16 @@ public class NonBlockingReaderImpl
b[0] = (char) ch;
ch = READ_EXPIRED;
return 1;
} else if (!threadIsReading) {
return in.read(b);
} else if (!threadIsReading && timeout <= 0) {
return in.read(b, off, len);
} else {
int c = read(-1, false);
// TODO: rework implementation to read as much as possible
int c = read(timeout, false);
if (c >= 0) {
b[0] = (char) c;
b[off] = (char) c;
return 1;
} else {
return -1;
return c;
}
}
}
@ -158,20 +161,17 @@ public class NonBlockingReaderImpl
notifyAll();
}
boolean isInfinite = (timeout <= 0L);
/*
* So the thread is currently doing the reading for us. So
* now we play the waiting game.
*/
while (isInfinite || timeout > 0L) {
long start = System.currentTimeMillis ();
Timeout t = new Timeout(timeout);
while (!t.elapsed()) {
try {
if (Thread.interrupted()) {
throw new InterruptedException();
}
wait(timeout);
wait(t.timeout());
}
catch (InterruptedException e) {
exception = (IOException) new InterruptedIOException().initCause(e);
@ -190,10 +190,6 @@ public class NonBlockingReaderImpl
assert exception == null;
break;
}
if (!isInfinite) {
timeout -= System.currentTimeMillis() - start;
}
}
}

View File

@ -28,6 +28,12 @@ public class OSUtils {
&& (System.getenv("MSYSTEM").startsWith("MINGW")
|| System.getenv("MSYSTEM").equals("MSYS"));
public static final boolean IS_WSL = System.getenv("WSL_DISTRO_NAME") != null;
public static final boolean IS_WSL1 = IS_WSL && System.getenv("WSL_INTEROP") == null;
public static final boolean IS_WSL2 = IS_WSL && !IS_WSL1;
public static final boolean IS_CONEMU = IS_WINDOWS
&& System.getenv("ConEmuPID") != null;
@ -38,17 +44,20 @@ public class OSUtils {
public static String STTY_COMMAND;
public static String STTY_F_OPTION;
public static String INFOCMP_COMMAND;
public static String TEST_COMMAND;
static {
String tty;
String stty;
String sttyfopt;
String infocmp;
String test;
if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) {
tty = "tty.exe";
stty = "stty.exe";
tty = null;
stty = null;
sttyfopt = null;
infocmp = "infocmp.exe";
infocmp = null;
test = null;
String path = System.getenv("PATH");
if (path != null) {
String[] paths = path.split(";");
@ -62,23 +71,35 @@ public class OSUtils {
if (infocmp == null && new File(p, "infocmp.exe").exists()) {
infocmp = new File(p, "infocmp.exe").getAbsolutePath();
}
if (test == null && new File(p, "test.exe").exists()) {
test = new File(p, "test.exe").getAbsolutePath();
}
}
}
if (tty == null) {
tty = "tty.exe";
}
if (stty == null) {
stty = "stty.exe";
}
if (infocmp == null) {
infocmp = "infocmp.exe";
}
if (test == null) {
test = "test.exe";
}
} else {
tty = "tty";
stty = "stty";
stty = IS_OSX ? "/bin/stty" : "stty";
sttyfopt = IS_OSX ? "-f" : "-F";
infocmp = "infocmp";
if (IS_OSX) {
sttyfopt = "-f";
}
else {
sttyfopt = "-F";
}
test = "/bin/test";
}
TTY_COMMAND = tty;
STTY_COMMAND = stty;
STTY_F_OPTION = sttyfopt;
INFOCMP_COMMAND = infocmp;
TEST_COMMAND = test;
}
}

View File

@ -36,7 +36,7 @@ public class PumpReader extends Reader {
}
public PumpReader(int bufferSize) {
char[] buf = new char[bufferSize];
char[] buf = new char[Math.max(bufferSize, 2)];
this.readBuffer = CharBuffer.wrap(buf);
this.writeBuffer = CharBuffer.wrap(buf);
this.writer = new Writer(this);
@ -53,12 +53,52 @@ public class PumpReader extends Reader {
return new InputStream(this, charset);
}
private boolean wait(CharBuffer buffer) throws InterruptedIOException {
if (closed) {
return false;
/**
* Blocks until more input is available, even if {@link #readBuffer} already
* contains some chars; or until the reader is closed.
*
* @return true if more input is available, false if no additional input is
* available and the reader is closed
* @throws InterruptedIOException If {@link #wait()} is interrupted
*/
private boolean waitForMoreInput() throws InterruptedIOException {
if (!writeBuffer.hasRemaining()) {
throw new AssertionError("No space in write buffer");
}
int oldRemaining = readBuffer.remaining();
do {
if (closed) {
return false;
}
// Wake up waiting writers
notifyAll();
try {
wait();
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
} while (readBuffer.remaining() <= oldRemaining);
return true;
}
/**
* Waits until {@code buffer.hasRemaining() == true}, or it is false and
* the reader is {@link #closed}.
*
* @return true if {@code buffer.hasRemaining() == true}; false otherwise
* when reader is closed
*/
private boolean wait(CharBuffer buffer) throws InterruptedIOException {
while (!buffer.hasRemaining()) {
if (closed) {
return false;
}
// Wake up waiting readers/writers
notifyAll();
@ -67,19 +107,15 @@ public class PumpReader extends Reader {
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
if (closed) {
return false;
}
}
return true;
}
/**
* Blocks until more input is available or the reader is closed.
* Blocks until input is available or the reader is closed.
*
* @return true if more input is available, false if the reader is closed
* @return true if input is available, false if no input is available and the reader is closed
* @throws InterruptedIOException If {@link #wait()} is interrupted
*/
private boolean waitForInput() throws InterruptedIOException {
@ -94,7 +130,8 @@ public class PumpReader extends Reader {
* @throws ClosedException If the reader was closed
*/
private void waitForBufferSpace() throws InterruptedIOException, ClosedException {
if (!wait(writeBuffer)) {
// Check `closed` to throw even if writer buffer has space available
if (!wait(writeBuffer) || closed) {
throw new ClosedException();
}
}
@ -122,7 +159,9 @@ public class PumpReader extends Reader {
* @return If more input is available
*/
private boolean rewindReadBuffer() {
return rewind(readBuffer, writeBuffer) && readBuffer.hasRemaining();
boolean rw = rewind(readBuffer, writeBuffer) && readBuffer.hasRemaining();
notifyAll();
return rw;
}
/**
@ -131,6 +170,7 @@ public class PumpReader extends Reader {
*/
private void rewindWriteBuffer() {
rewind(writeBuffer, readBuffer);
notifyAll();
}
@Override
@ -202,10 +242,33 @@ public class PumpReader extends Reader {
}
private void encodeBytes(CharsetEncoder encoder, ByteBuffer output) throws IOException {
int oldPos = output.position();
CoderResult result = encoder.encode(readBuffer, output, false);
if (rewindReadBuffer() && result.isUnderflow()) {
encoder.encode(readBuffer, output, false);
int encodedCount = output.position() - oldPos;
if (result.isUnderflow()) {
boolean hasMoreInput = rewindReadBuffer();
boolean reachedEndOfInput = false;
// If encoding did not make any progress must block for more input
if (encodedCount == 0 && !hasMoreInput) {
reachedEndOfInput = !waitForMoreInput();
}
result = encoder.encode(readBuffer, output, reachedEndOfInput);
if (result.isError()) {
result.throwException();
}
if (!reachedEndOfInput && output.position() - oldPos == 0) {
throw new AssertionError("Failed to encode any chars");
}
rewindReadBuffer();
} else if (result.isOverflow()) {
if (encodedCount == 0) {
throw new AssertionError("Output buffer has not enough space");
}
} else {
result.throwException();
}
}
@ -334,7 +397,7 @@ public class PumpReader extends Reader {
this.encoder = charset.newEncoder()
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.onMalformedInput(CodingErrorAction.REPLACE);
this.buffer = ByteBuffer.allocate((int) Math.ceil(encoder.maxBytesPerChar()));
this.buffer = ByteBuffer.allocate((int) Math.ceil(encoder.maxBytesPerChar() * 2));
// No input available after initialization
buffer.limit(0);

View File

@ -241,7 +241,7 @@ public class StyleResolver {
if (spec.length() == 1) {
// log.warning("Invalid style-reference; missing discriminator: " + spec);
} else {
String name = spec.substring(1, spec.length());
String name = spec.substring(1);
String resolvedSpec = source.apply(name);
if (resolvedSpec != null) {
return apply(style, resolvedSpec);

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2002-2018, the original author or authors.
*
* 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 jdk.internal.org.jline.utils;
/**
* Helper class ti use during I/O operations with an eventual timeout.
*/
public class Timeout {
private final long timeout;
private long cur = 0;
private long end = Long.MAX_VALUE;
public Timeout(long timeout) {
this.timeout = timeout;
}
public boolean isInfinite() {
return timeout <= 0;
}
public boolean isFinite() {
return timeout > 0;
}
public boolean elapsed() {
if (timeout > 0) {
cur = System.currentTimeMillis();
if (end == Long.MAX_VALUE) {
end = cur + timeout;
}
return cur >= end;
} else {
return false;
}
}
public long timeout() {
return timeout > 0 ? Math.max(1, end - cur) : timeout;
}
}

View File

@ -70,6 +70,7 @@ public final class WCWidth {
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
(ucs >= 0x1f000 && ucs <= 0x1feee) ||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
(ucs >= 0x30000 && ucs <= 0x3fffd))) ? 1 : 0);
}
@ -123,8 +124,8 @@ public final class WCWidth {
new Interval( 0x10A01, 0x10A03 ), new Interval( 0x10A05, 0x10A06 ), new Interval( 0x10A0C, 0x10A0F ),
new Interval( 0x10A38, 0x10A3A ), new Interval( 0x10A3F, 0x10A3F ), new Interval( 0x1D167, 0x1D169 ),
new Interval( 0x1D173, 0x1D182 ), new Interval( 0x1D185, 0x1D18B ), new Interval( 0x1D1AA, 0x1D1AD ),
new Interval( 0x1D242, 0x1D244 ), new Interval( 0xE0001, 0xE0001 ), new Interval( 0xE0020, 0xE007F ),
new Interval( 0xE0100, 0xE01EF )
new Interval( 0x1D242, 0x1D244 ), new Interval( 0x1F3FB, 0x1F3FF ), new Interval( 0xE0001, 0xE0001 ),
new Interval( 0xE0020, 0xE007F ), new Interval( 0xE0100, 0xE01EF )
};
private static class Interval {

View File

@ -2,7 +2,7 @@ windows-vtp|windows with virtual terminal processing,
am, mc5i, mir, msgr,
colors#256, cols#80, it#8, lines#24, ncv#3, pairs#64,
bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, clear=\E[H\E[J,
cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\E[B,
cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\n,
cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH,
cuu=\E[%p1%dA, cuu1=\E[A,
il=\E[%p1%dL, il1=\E[L,

View File

@ -48,8 +48,6 @@ module jdk.internal.le {
exports jdk.internal.org.jline.terminal.spi to
jdk.jshell;
uses jdk.internal.org.jline.terminal.spi.JnaSupport;
// Console
provides jdk.internal.io.JdkConsoleProvider with
jdk.internal.org.jline.JdkConsoleProviderImpl;

View File

@ -1,4 +1,4 @@
## JLine v3.20.0
## JLine v3.22.0
### JLine License
<pre>
@ -41,10 +41,10 @@ OF THE POSSIBILITY OF SUCH DAMAGE.
4th Party Dependency
=============
org.fusesource.jansi version 1.17.1
org.apache.sshd 2.1 to 3
org.apache.felix.gogo.runtime 1.1.2
org.apache.felix.gogo.jline 1.1.4
org.fusesource.jansi version 2.4.0
org.apache.sshd 2.9.2
org.apache.felix.gogo.runtime 1.1.6
org.apache.felix.gogo.jline 1.1.8
=============
Apache License
Version 2.0, January 2004
@ -262,7 +262,7 @@ slf4j
SLF4J source code and binaries are distributed under the MIT license.
Copyright (c) 2004-2017 QOS.ch
Copyright (c) 2004-2023 QOS.ch
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining

View File

@ -1,77 +0,0 @@
/*
* Copyright (c) 2002-2019, the original author or authors.
*
* 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 jdk.internal.org.jline.terminal.impl.jna;
import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.Terminal;
import jdk.internal.org.jline.terminal.impl.jna.win.JnaWinSysTerminal;
import jdk.internal.org.jline.terminal.spi.JnaSupport;
import jdk.internal.org.jline.terminal.spi.Pty;
import jdk.internal.org.jline.utils.OSUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.function.Function;
public class JnaSupportImpl implements JnaSupport {
@Override
public Pty current() throws IOException {
// return JnaNativePty.current();
throw new UnsupportedOperationException();
}
@Override
public Pty open(Attributes attributes, Size size) throws IOException {
// return JnaNativePty.open(attributes, size);
throw new UnsupportedOperationException();
}
@Override
public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler) throws IOException {
return winSysTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, false);
}
@Override
public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused) throws IOException {
return winSysTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, paused, input -> input);
}
@Override
public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
return JnaWinSysTerminal.createTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, paused, inputStreamWrapper);
}
@Override
public boolean isWindowsConsole() {
return JnaWinSysTerminal.isWindowsConsole();
}
@Override
public boolean isConsoleOutput() {
if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) {
throw new UnsupportedOperationException();
} else if (OSUtils.IS_WINDOWS) {
return JnaWinSysTerminal.isConsoleOutput();
}
throw new UnsupportedOperationException();
}
@Override
public boolean isConsoleInput() {
if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) {
throw new UnsupportedOperationException();
} else if (OSUtils.IS_WINDOWS) {
return JnaWinSysTerminal.isConsoleInput();
}
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2002-2020, the original author or authors.
*
* 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 jdk.internal.org.jline.terminal.impl.jna;
import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.Terminal;
import jdk.internal.org.jline.terminal.impl.PosixPtyTerminal;
import jdk.internal.org.jline.terminal.impl.PosixSysTerminal;
import jdk.internal.org.jline.terminal.impl.jna.win.JnaWinSysTerminal;
import jdk.internal.org.jline.terminal.spi.TerminalProvider;
import jdk.internal.org.jline.terminal.spi.Pty;
import jdk.internal.org.jline.utils.OSUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.function.Function;
public class JnaTerminalProvider implements TerminalProvider
{
@Override
public String name() {
return "jna";
}
// public Pty current(TerminalProvider.Stream console) throws IOException {
// return JnaNativePty.current(console);
// }
//
// public Pty open(Attributes attributes, Size size) throws IOException {
// return JnaNativePty.open(attributes, size);
// }
@Override
public Terminal sysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
Stream consoleStream, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
if (OSUtils.IS_WINDOWS) {
return winSysTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, consoleStream, inputStreamWrapper );
} else {
return null;
}
}
public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
Stream console, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
return JnaWinSysTerminal.createTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, console, inputStreamWrapper);
}
// public Terminal posixSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
// boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
// Stream consoleStream) throws IOException {
// Pty pty = current(consoleStream);
// return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler);
// }
@Override
public Terminal newTerminal(String name, String type, InputStream in, OutputStream out,
Charset encoding, Terminal.SignalHandler signalHandler, boolean paused,
Attributes attributes, Size size) throws IOException
{
// Pty pty = open(attributes, size);
// return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler, paused);
return null;
}
@Override
public boolean isSystemStream(Stream stream) {
try {
if (OSUtils.IS_WINDOWS) {
return isWindowsSystemStream(stream);
} else {
// return isPosixSystemStream(stream);
return false;
}
} catch (Throwable t) {
return false;
}
}
public boolean isWindowsSystemStream(Stream stream) {
return JnaWinSysTerminal.isWindowsSystemStream(stream);
}
// public boolean isPosixSystemStream(Stream stream) {
// return JnaNativePty.isPosixSystemStream(stream);
// }
@Override
public String systemStreamName(Stream stream) {
// if (OSUtils.IS_WINDOWS) {
return null;
// } else {
// return JnaNativePty.posixSystemStreamName(stream);
// }
}
}

View File

@ -17,17 +17,17 @@ import java.io.IOException;
class JnaWinConsoleWriter extends AbstractWindowsConsoleWriter {
private final Pointer consoleHandle;
private final Pointer console;
private final IntByReference writtenChars = new IntByReference();
JnaWinConsoleWriter(Pointer consoleHandle) {
this.consoleHandle = consoleHandle;
JnaWinConsoleWriter(Pointer console) {
this.console = console;
}
@Override
protected void writeConsole(char[] text, int len) throws IOException {
try {
Kernel32.INSTANCE.WriteConsoleW(this.consoleHandle, text, len, this.writtenChars, null);
Kernel32.INSTANCE.WriteConsoleW(this.console, text, len, this.writtenChars, null);
} catch (LastErrorException e) {
throw new IOException("Failed to write to console", e);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2019, the original author or authors.
* Copyright (c) 2002-2020, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
@ -19,11 +19,10 @@ import java.util.function.IntConsumer;
//import com.sun.jna.LastErrorException;
//import com.sun.jna.Pointer;
//import com.sun.jna.ptr.IntByReference;
import jdk.internal.org.jline.terminal.Cursor;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.Terminal;
import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal;
import jdk.internal.org.jline.terminal.spi.TerminalProvider;
import jdk.internal.org.jline.utils.InfoCmp;
import jdk.internal.org.jline.utils.OSUtils;
@ -31,38 +30,50 @@ public class JnaWinSysTerminal extends AbstractWindowsTerminal {
private static final Pointer consoleIn = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_INPUT_HANDLE);
private static final Pointer consoleOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE);
private static final Pointer consoleErr = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_ERROR_HANDLE);
public static JnaWinSysTerminal createTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, boolean paused, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
public static JnaWinSysTerminal createTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, boolean nativeSignals, SignalHandler signalHandler, boolean paused, TerminalProvider.Stream consoleStream, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
Pointer console;
switch (consoleStream) {
case Output:
console = JnaWinSysTerminal.consoleOut;
break;
case Error:
console = JnaWinSysTerminal.consoleErr;
break;
default:
throw new IllegalArgumentException("Unsupport stream for console: " + consoleStream);
}
Writer writer;
if (ansiPassThrough) {
if (type == null) {
type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS;
}
writer = new JnaWinConsoleWriter(consoleOut);
writer = new JnaWinConsoleWriter(console);
} else {
IntByReference mode = new IntByReference();
Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode);
Kernel32.INSTANCE.GetConsoleMode(console, mode);
try {
Kernel32.INSTANCE.SetConsoleMode(consoleOut, mode.getValue() | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING);
Kernel32.INSTANCE.SetConsoleMode(console, mode.getValue() | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING);
if (type == null) {
type = TYPE_WINDOWS_VTP;
}
writer = new JnaWinConsoleWriter(consoleOut);
writer = new JnaWinConsoleWriter(console);
} catch (LastErrorException e) {
if (OSUtils.IS_CONEMU) {
if (type == null) {
type = TYPE_WINDOWS_CONEMU;
}
writer = new JnaWinConsoleWriter(consoleOut);
writer = new JnaWinConsoleWriter(console);
} else {
if (type == null) {
type = TYPE_WINDOWS;
}
writer = new WindowsAnsiWriter(new BufferedWriter(new JnaWinConsoleWriter(consoleOut)), consoleOut);
writer = new WindowsAnsiWriter(new BufferedWriter(new JnaWinConsoleWriter(console)), console);
}
}
}
JnaWinSysTerminal terminal = new JnaWinSysTerminal(writer, name, type, encoding, codepage, nativeSignals, signalHandler, inputStreamWrapper);
JnaWinSysTerminal terminal = new JnaWinSysTerminal(writer, name, type, encoding, nativeSignals, signalHandler, inputStreamWrapper);
// Start input pump thread
if (!paused) {
terminal.resume();
@ -70,39 +81,26 @@ public class JnaWinSysTerminal extends AbstractWindowsTerminal {
return terminal;
}
public static boolean isWindowsConsole() {
public static boolean isWindowsSystemStream(TerminalProvider.Stream stream) {
try {
IntByReference mode = new IntByReference();
Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode);
Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode);
Pointer console;
switch (stream) {
case Input: console = consoleIn; break;
case Output: console = consoleOut; break;
case Error: console = consoleErr; break;
default: return false;
}
Kernel32.INSTANCE.GetConsoleMode(console, mode);
return true;
} catch (LastErrorException e) {
return false;
}
}
public static boolean isConsoleOutput() {
try {
IntByReference mode = new IntByReference();
Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode);
return true;
} catch (LastErrorException e) {
return false;
}
}
public static boolean isConsoleInput() {
try {
IntByReference mode = new IntByReference();
Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode);
return true;
} catch (LastErrorException e) {
return false;
}
}
JnaWinSysTerminal(Writer writer, String name, String type, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
super(writer, name, type, encoding, codepage, nativeSignals, signalHandler, inputStreamWrapper);
JnaWinSysTerminal(Writer writer, String name, String type, Charset encoding, boolean nativeSignals, SignalHandler signalHandler,
Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
super(writer, name, type, encoding, nativeSignals, signalHandler, inputStreamWrapper);
strings.put(InfoCmp.Capability.key_mouse, "\\E[M");
}

View File

@ -70,7 +70,7 @@ public final class WindowsAnsiWriter extends AnsiWriter {
BACKGROUND_WHITE,
};
private static final int MAX_ESCAPE_SEQUENCE_LENGTH = 100;
private final static int MAX_ESCAPE_SEQUENCE_LENGTH = 100;
private final Pointer console;
@ -93,7 +93,7 @@ public final class WindowsAnsiWriter extends AnsiWriter {
private void getConsoleInfo() throws IOException {
out.flush();
Kernel32.INSTANCE.GetConsoleScreenBufferInfo(console, info);
if( negative ) {
if (negative) {
info.wAttributes = invertAttributeColors(info.wAttributes);
}
}
@ -109,14 +109,14 @@ public final class WindowsAnsiWriter extends AnsiWriter {
if (underline) {
attributes |= BACKGROUND_INTENSITY;
}
if( negative ) {
if (negative) {
attributes = invertAttributeColors(attributes);
}
Kernel32.INSTANCE.SetConsoleTextAttribute(console, attributes);
}
private short invertAttributeColors(short attributes) {
// Swap the Foreground and Background bits.
// Swap the the Foreground and Background bits.
int fg = 0x000F & attributes;
fg <<= 4;
int bg = 0X00F0 & attributes;
@ -183,26 +183,26 @@ public final class WindowsAnsiWriter extends AnsiWriter {
protected void processCursorUpLine(int count) throws IOException {
getConsoleInfo();
info.dwCursorPosition.X = 0;
info.dwCursorPosition.Y -= (short)count;
info.dwCursorPosition.Y -= (short) count;
applyCursorPosition();
}
protected void processCursorDownLine(int count) throws IOException {
getConsoleInfo();
info.dwCursorPosition.X = 0;
info.dwCursorPosition.Y += (short)count;
info.dwCursorPosition.Y += (short) count;
applyCursorPosition();
}
protected void processCursorLeft(int count) throws IOException {
getConsoleInfo();
info.dwCursorPosition.X -= (short)count;
info.dwCursorPosition.X -= (short) count;
applyCursorPosition();
}
protected void processCursorRight(int count) throws IOException {
getConsoleInfo();
info.dwCursorPosition.X += (short)count;
info.dwCursorPosition.X += (short) count;
applyCursorPosition();
}
@ -210,7 +210,7 @@ public final class WindowsAnsiWriter extends AnsiWriter {
getConsoleInfo();
int nb = Math.max(0, info.dwCursorPosition.Y + count - info.dwSize.Y + 1);
if (nb != count) {
info.dwCursorPosition.Y += (short)count;
info.dwCursorPosition.Y += (short) count;
applyCursorPosition();
}
if (nb > 0) {
@ -226,7 +226,7 @@ public final class WindowsAnsiWriter extends AnsiWriter {
protected void processCursorUp(int count) throws IOException {
getConsoleInfo();
info.dwCursorPosition.Y -= (short)count;
info.dwCursorPosition.Y -= (short) count;
applyCursorPosition();
}

View File

@ -1,27 +0,0 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
provides jdk.internal.org.jline.terminal.spi.JnaSupport with jdk.internal.org.jline.terminal.impl.jna.JnaSupportImpl;

View File

@ -56,7 +56,7 @@ public class AbstractWindowsTerminalTest {
return is.read();
}
};
var t = new AbstractWindowsTerminal(out, "test", "vt100", null, -1, false, SignalHandler.SIG_DFL, isWrapper) {
var t = new AbstractWindowsTerminal(out, "test", "vt100", null, false, SignalHandler.SIG_DFL, isWrapper) {
@Override
protected int getConsoleMode() {
return -1;

View File

@ -59,7 +59,7 @@ public class KeyConversionTest {
void checkKeyConversion(KeyEvent event, String expected) throws IOException {
StringBuilder result = new StringBuilder();
new AbstractWindowsTerminal(new StringWriter(), "", "windows", Charset.forName("UTF-8"),
0, true, SignalHandler.SIG_DFL, in -> in) {
true, SignalHandler.SIG_DFL, in -> in) {
@Override
protected int getConsoleMode() {
return 0;

View File

@ -27,6 +27,7 @@
* @summary Control Char <UNDEF> check for pty
* @modules jdk.internal.le/jdk.internal.org.jline.terminal
* jdk.internal.le/jdk.internal.org.jline.terminal.impl
* jdk.internal.le/jdk.internal.org.jline.terminal.spi
* @requires (os.family == "linux") | (os.family == "aix")
*/
@ -35,10 +36,11 @@ import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Attributes.ControlChar;
import jdk.internal.org.jline.terminal.Attributes.LocalFlag;
import jdk.internal.org.jline.terminal.impl.ExecPty;
import jdk.internal.org.jline.terminal.spi.TerminalProvider;
public class ExecPtyGetFlagsToSetTest extends ExecPty {
public ExecPtyGetFlagsToSetTest(String name, boolean system) {
super(name, system);
public ExecPtyGetFlagsToSetTest(String name, TerminalProvider.Stream stream) {
super(name, stream);
}
@Override
@ -48,7 +50,7 @@ public class ExecPtyGetFlagsToSetTest extends ExecPty {
public static void main(String[] args) {
ExecPtyGetFlagsToSetTest testPty =
new ExecPtyGetFlagsToSetTest("stty", true);
new ExecPtyGetFlagsToSetTest("stty", TerminalProvider.Stream.Output);
Attributes attr = new Attributes();
Attributes current = new Attributes();