8273682: Upgrade Jline to 3.20.0
Reviewed-by: sundar
This commit is contained in:
parent
dcf428c7a7
commit
b8cb76ad21
@ -137,4 +137,9 @@ public class Candidate implements Comparable<Candidate> {
|
||||
public int compareTo(Candidate o) {
|
||||
return value.compareTo(o.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Candidate{" + value + "}";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.reader;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface CompletionMatcher {
|
||||
|
||||
/**
|
||||
* Compiles completion matcher functions
|
||||
*
|
||||
* @param options LineReader options
|
||||
* @param prefix invoked by complete-prefix or expand-or-complete-prefix widget
|
||||
* @param line The parsed line within which completion has been requested
|
||||
* @param caseInsensitive if completion is case insensitive or not
|
||||
* @param errors number of errors accepted in matching
|
||||
* @param originalGroupName value of JLineReader variable original-group-name
|
||||
*/
|
||||
void compile(Map<LineReader.Option, Boolean> options, boolean prefix, CompletingParsedLine line
|
||||
, boolean caseInsensitive, int errors, String originalGroupName);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param candidates list of candidates
|
||||
* @return a map of candidates that completion matcher matches
|
||||
*/
|
||||
List<Candidate> matches(List<Candidate> candidates);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return a candidate that have exact match, null if no exact match found
|
||||
*/
|
||||
Candidate exactMatch();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return a common prefix of matched candidates
|
||||
*/
|
||||
String getCommonPrefix();
|
||||
|
||||
}
|
@ -1,75 +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.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ConfigurationPath {
|
||||
private Path appConfig;
|
||||
private Path userConfig;
|
||||
|
||||
/**
|
||||
* Configuration class constructor.
|
||||
* @param appConfig Application configuration directory
|
||||
* @param userConfig User private configuration directory
|
||||
*/
|
||||
public ConfigurationPath(Path appConfig, Path userConfig) {
|
||||
this.appConfig = appConfig;
|
||||
this.userConfig = userConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search configuration file first from userConfig and then appConfig directory. Returns null if file is not found.
|
||||
* @param name Configuration file name.
|
||||
* @return Configuration file.
|
||||
*
|
||||
*/
|
||||
public Path getConfig(String name) {
|
||||
Path out = null;
|
||||
if (userConfig != null && userConfig.resolve(name).toFile().exists()) {
|
||||
out = userConfig.resolve(name);
|
||||
} else if (appConfig != null && appConfig.resolve(name).toFile().exists()) {
|
||||
out = appConfig.resolve(name);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search configuration file from userConfig directory. Returns null if file is not found.
|
||||
* @param name Configuration file name.
|
||||
* @return Configuration file.
|
||||
* @throws IOException When we do not have read access to the file or directory.
|
||||
*
|
||||
*/
|
||||
public Path getUserConfig(String name) throws IOException {
|
||||
return getUserConfig(name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search configuration file from userConfig directory. Returns null if file is not found.
|
||||
* @param name Configuration file name
|
||||
* @param create When true configuration file is created if not found.
|
||||
* @return Configuration file.
|
||||
* @throws IOException When we do not have read/write access to the file or directory.
|
||||
*/
|
||||
public Path getUserConfig(String name, boolean create) throws IOException {
|
||||
Path out = null;
|
||||
if (userConfig != null) {
|
||||
if (!userConfig.resolve(name).toFile().exists() && create) {
|
||||
userConfig.resolve(name).toFile().createNewFile();
|
||||
}
|
||||
if (userConfig.resolve(name).toFile().exists()) {
|
||||
out = userConfig.resolve(name);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2016, 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.
|
||||
@ -15,6 +15,7 @@ package jdk.internal.org.jline.reader;
|
||||
public class EndOfFileException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 528485360925144689L;
|
||||
private String partialLine;
|
||||
|
||||
public EndOfFileException() {
|
||||
}
|
||||
@ -34,4 +35,13 @@ public class EndOfFileException extends RuntimeException {
|
||||
public EndOfFileException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
public EndOfFileException partialLine(String partialLine) {
|
||||
this.partialLine = partialLine;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPartialLine() {
|
||||
return partialLine;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
@ -294,7 +294,13 @@ public interface LineReader {
|
||||
String COMMENT_BEGIN = "comment-begin";
|
||||
String BELL_STYLE = "bell-style";
|
||||
String PREFER_VISIBLE_BELL = "prefer-visible-bell";
|
||||
/** tab completion: if candidates are more than list-max a question will be asked before displaying them */
|
||||
String LIST_MAX = "list-max";
|
||||
/**
|
||||
* tab completion: if candidates are less than menu-list-max
|
||||
* they are displayed in a list below the field to be completed
|
||||
*/
|
||||
String MENU_LIST_MAX = "menu-list-max";
|
||||
String DISABLE_HISTORY = "disable-history";
|
||||
String DISABLE_COMPLETION = "disable-completion";
|
||||
String EDITING_MODE = "editing-mode";
|
||||
@ -303,6 +309,7 @@ public interface LineReader {
|
||||
String WORDCHARS = "WORDCHARS";
|
||||
String REMOVE_SUFFIX_CHARS = "REMOVE_SUFFIX_CHARS";
|
||||
String SEARCH_TERMINATORS = "search-terminators";
|
||||
/** Number of matching errors that are accepted by the completion matcher */
|
||||
String ERRORS = "errors";
|
||||
/** Property for the "others" group name */
|
||||
String OTHERS_GROUP_NAME = "OTHERS_GROUP_NAME";
|
||||
@ -310,12 +317,19 @@ public interface LineReader {
|
||||
String ORIGINAL_GROUP_NAME = "ORIGINAL_GROUP_NAME";
|
||||
/** Completion style for displaying groups name */
|
||||
String COMPLETION_STYLE_GROUP = "COMPLETION_STYLE_GROUP";
|
||||
String COMPLETION_STYLE_LIST_GROUP = "COMPLETION_STYLE_LIST_GROUP";
|
||||
/** Completion style for displaying the current selected item */
|
||||
String COMPLETION_STYLE_SELECTION = "COMPLETION_STYLE_SELECTION";
|
||||
String COMPLETION_STYLE_LIST_SELECTION = "COMPLETION_STYLE_LIST_SELECTION";
|
||||
/** Completion style for displaying the candidate description */
|
||||
String COMPLETION_STYLE_DESCRIPTION = "COMPLETION_STYLE_DESCRIPTION";
|
||||
String COMPLETION_STYLE_LIST_DESCRIPTION = "COMPLETION_STYLE_LIST_DESCRIPTION";
|
||||
/** Completion style for displaying the matching part of candidates */
|
||||
String COMPLETION_STYLE_STARTING = "COMPLETION_STYLE_STARTING";
|
||||
String COMPLETION_STYLE_LIST_STARTING = "COMPLETION_STYLE_LIST_STARTING";
|
||||
/** Completion style for displaying the list */
|
||||
String COMPLETION_STYLE_BACKGROUND = "COMPLETION_STYLE_BACKGROUND";
|
||||
String COMPLETION_STYLE_LIST_BACKGROUND = "COMPLETION_STYLE_LIST_BACKGROUND";
|
||||
/**
|
||||
* Set the template for prompts for secondary (continuation) lines.
|
||||
* This is a prompt template as described in the class header.
|
||||
@ -370,10 +384,20 @@ public interface LineReader {
|
||||
*/
|
||||
String FEATURES_MAX_BUFFER_SIZE = "features-max-buffer-size";
|
||||
|
||||
/**
|
||||
* Min buffer size for tab auto-suggestions.
|
||||
* For shorter buffer sizes auto-suggestions are not resolved.
|
||||
*/
|
||||
String SUGGESTIONS_MIN_BUFFER_SIZE = "suggestions-min-buffer-size";
|
||||
|
||||
Map<String, KeyMap<Binding>> defaultKeyMaps();
|
||||
|
||||
enum Option {
|
||||
COMPLETE_IN_WORD,
|
||||
/** use camel case completion matcher */
|
||||
COMPLETE_MATCHER_CAMELCASE,
|
||||
/** use type completion matcher */
|
||||
COMPLETE_MATCHER_TYPO(true),
|
||||
DISABLE_EVENT_EXPANSION,
|
||||
HISTORY_VERIFY,
|
||||
HISTORY_IGNORE_SPACE(true),
|
||||
@ -386,9 +410,13 @@ public interface LineReader {
|
||||
AUTO_GROUP(true),
|
||||
AUTO_MENU(true),
|
||||
AUTO_LIST(true),
|
||||
/** list candidates below the field to be completed */
|
||||
AUTO_MENU_LIST,
|
||||
RECOGNIZE_EXACT,
|
||||
/** display group name before each group (else display all group names first) */
|
||||
GROUP(true),
|
||||
/** when double tab to select candidate keep candidates grouped (else loose grouping) */
|
||||
GROUP_PERSIST,
|
||||
/** if completion is case insensitive or not */
|
||||
CASE_INSENSITIVE,
|
||||
LIST_AMBIGUOUS,
|
||||
@ -414,7 +442,8 @@ public interface LineReader {
|
||||
DELAY_LINE_WRAP,
|
||||
AUTO_PARAM_SLASH(true),
|
||||
AUTO_REMOVE_SLASH(true),
|
||||
USE_FORWARD_SLASH(false),
|
||||
/** FileNameCompleter: Use '/' character as a file directory separator */
|
||||
USE_FORWARD_SLASH,
|
||||
/** When hitting the <code><tab></code> key at the beginning of the line, insert a tabulation
|
||||
* instead of completing. This is mainly useful when {@link #BRACKETED_PASTE} is
|
||||
* disabled, so that copy/paste of indented text does not trigger completion.
|
||||
@ -450,6 +479,11 @@ public interface LineReader {
|
||||
this.def = def;
|
||||
}
|
||||
|
||||
public final boolean isSet(Map<Option, Boolean> options) {
|
||||
Boolean b = options.get(this);
|
||||
return b != null ? b : this.isDef();
|
||||
}
|
||||
|
||||
public boolean isDef() {
|
||||
return def;
|
||||
}
|
||||
@ -489,8 +523,9 @@ public interface LineReader {
|
||||
* Equivalent to <code>readLine(null, null, null)</code>.
|
||||
*
|
||||
* @return the line read
|
||||
* @throws UserInterruptException If the call was interrupted by the user.
|
||||
* @throws EndOfFileException If the end of the input stream was reached.
|
||||
* @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
|
||||
* @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
|
||||
* @throws java.io.IOError in case of other i/o errors
|
||||
*/
|
||||
String readLine() throws UserInterruptException, EndOfFileException;
|
||||
|
||||
@ -502,8 +537,9 @@ public interface LineReader {
|
||||
*
|
||||
* @param mask The mask character, <code>null</code> or <code>0</code>.
|
||||
* @return A line that is read from the terminal, can never be null.
|
||||
* @throws UserInterruptException If the call was interrupted by the user.
|
||||
* @throws EndOfFileException If the end of the input stream was reached.
|
||||
* @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
|
||||
* @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
|
||||
* @throws java.io.IOError in case of other i/o errors
|
||||
*/
|
||||
String readLine(Character mask) throws UserInterruptException, EndOfFileException;
|
||||
|
||||
@ -515,8 +551,9 @@ public interface LineReader {
|
||||
*
|
||||
* @param prompt The prompt to issue to the terminal, may be null.
|
||||
* @return A line that is read from the terminal, can never be null.
|
||||
* @throws UserInterruptException If the call was interrupted by the user.
|
||||
* @throws EndOfFileException If the end of the input stream was reached.
|
||||
* @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
|
||||
* @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
|
||||
* @throws java.io.IOError in case of other i/o errors
|
||||
*/
|
||||
String readLine(String prompt) throws UserInterruptException, EndOfFileException;
|
||||
|
||||
@ -529,8 +566,9 @@ public interface LineReader {
|
||||
* @param prompt The prompt to issue to the terminal, may be null.
|
||||
* @param mask The mask character, <code>null</code> or <code>0</code>.
|
||||
* @return A line that is read from the terminal, can never be null.
|
||||
* @throws UserInterruptException If the call was interrupted by the user.
|
||||
* @throws EndOfFileException If the end of the input stream was reached.
|
||||
* @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
|
||||
* @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
|
||||
* @throws java.io.IOError in case of other i/o errors
|
||||
*/
|
||||
String readLine(String prompt, Character mask) throws UserInterruptException, EndOfFileException;
|
||||
|
||||
@ -546,8 +584,9 @@ public interface LineReader {
|
||||
* @param mask The character mask, may be null.
|
||||
* @param buffer The default value presented to the user to edit, may be null.
|
||||
* @return A line that is read from the terminal, can never be null.
|
||||
* @throws UserInterruptException If the call was interrupted by the user.
|
||||
* @throws EndOfFileException If the end of the input stream was reached.
|
||||
* @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
|
||||
* @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
|
||||
* @throws java.io.IOError in case of other i/o errors
|
||||
*/
|
||||
String readLine(String prompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException;
|
||||
|
||||
@ -568,8 +607,6 @@ public interface LineReader {
|
||||
* @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
|
||||
* @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
|
||||
* @throws java.io.IOError in case of other i/o errors
|
||||
* @throws UserInterruptException If the call was interrupted by the user.
|
||||
* @throws EndOfFileException If the end of the input stream was reached.
|
||||
*/
|
||||
String readLine(String prompt, String rightPrompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException;
|
||||
|
||||
@ -590,8 +627,6 @@ public interface LineReader {
|
||||
* @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
|
||||
* @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
|
||||
* @throws java.io.IOError in case of other i/o errors
|
||||
* @throws UserInterruptException If the call was interrupted by the user.
|
||||
* @throws EndOfFileException If the end of the input stream was reached.
|
||||
*/
|
||||
String readLine(String prompt, String rightPrompt, MaskingCallback maskingCallback, String buffer) throws UserInterruptException, EndOfFileException;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2018, 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.
|
||||
@ -36,6 +36,7 @@ public final class LineReaderBuilder {
|
||||
Highlighter highlighter;
|
||||
Parser parser;
|
||||
Expander expander;
|
||||
CompletionMatcher completionMatcher;
|
||||
|
||||
private LineReaderBuilder() {
|
||||
}
|
||||
@ -103,6 +104,11 @@ public final class LineReaderBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LineReaderBuilder completionMatcher(CompletionMatcher completionMatcher) {
|
||||
this.completionMatcher = completionMatcher;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LineReader build() {
|
||||
Terminal terminal = this.terminal;
|
||||
if (terminal == null) {
|
||||
@ -133,6 +139,9 @@ public final class LineReaderBuilder {
|
||||
if (expander != null) {
|
||||
reader.setExpander(expander);
|
||||
}
|
||||
if (completionMatcher != null) {
|
||||
reader.setCompletionMatcher(completionMatcher);
|
||||
}
|
||||
for (Map.Entry<LineReader.Option, Boolean> e : options.entrySet()) {
|
||||
reader.option(e.getKey(), e.getValue());
|
||||
}
|
||||
|
@ -35,16 +35,12 @@ public interface Parser {
|
||||
|
||||
default String getCommand(final String line) {
|
||||
String out = "";
|
||||
Pattern patternCommand = Pattern.compile("^\\s*" + REGEX_VARIABLE + "=(" + REGEX_COMMAND + ")(\\s+.*|$)");
|
||||
Pattern patternCommand = Pattern.compile("^\\s*" + REGEX_VARIABLE + "=(" + REGEX_COMMAND + ")(\\s+|$)");
|
||||
Matcher matcher = patternCommand.matcher(line);
|
||||
if (matcher.find()) {
|
||||
out = matcher.group(1);
|
||||
} else {
|
||||
out = line.trim().split("\\s+")[0];
|
||||
int idx = out.indexOf("=");
|
||||
if (idx > -1) {
|
||||
out = out.substring(idx + 1);
|
||||
}
|
||||
if (!out.matches(REGEX_COMMAND)) {
|
||||
out = "";
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
package jdk.internal.org.jline.reader;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Redirects a {@link Writer} to a {@link LineReader}'s {@link LineReader#printAbove(String)} method,
|
||||
* which draws output above the current prompt / input line.
|
||||
*
|
||||
* <p>Example:</p>
|
||||
* <pre>
|
||||
* LineReader reader = LineReaderBuilder.builder().terminal(terminal).parser(parser).build();
|
||||
* PrintAboveWriter printAbove = new PrintAboveWriter(reader);
|
||||
* printAbove.write(new char[] { 'h', 'i', '!', '\n'});
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public class PrintAboveWriter extends StringWriter {
|
||||
private final LineReader reader;
|
||||
|
||||
public PrintAboveWriter(LineReader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
StringBuffer buffer = getBuffer();
|
||||
int lastNewline = buffer.lastIndexOf("\n");
|
||||
if (lastNewline >= 0) {
|
||||
reader.printAbove(buffer.substring(0, lastNewline + 1));
|
||||
buffer.delete(0, lastNewline + 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,153 +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.reader;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Manage scriptEngine variables, statements and script execution.
|
||||
*
|
||||
* @author <a href="mailto:matti.rintanikkola@gmail.com">Matti Rinta-Nikkola</a>
|
||||
*/
|
||||
public interface ScriptEngine {
|
||||
|
||||
/**
|
||||
*
|
||||
* @return scriptEngine name
|
||||
*/
|
||||
String getEngineName();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return script file name extensions
|
||||
*/
|
||||
Collection<String> getExtensions();
|
||||
|
||||
/**
|
||||
* Tests if console variable exists
|
||||
* @param name
|
||||
* @return true if variable exists
|
||||
*/
|
||||
boolean hasVariable(String name);
|
||||
|
||||
/**
|
||||
* Creates variable
|
||||
* @param name of the variable
|
||||
* @param value of the variable
|
||||
*/
|
||||
void put(String name, Object value);
|
||||
|
||||
/**
|
||||
* Gets variable value
|
||||
* @param name of the variable
|
||||
* @return value of the variable
|
||||
*/
|
||||
Object get(String name);
|
||||
|
||||
/**
|
||||
* Gets all variables with values
|
||||
* @return map of the variables
|
||||
*/
|
||||
default Map<String,Object> find() {
|
||||
return find(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the variables that match the name. Name can contain * wild cards.
|
||||
* @param name of the variable
|
||||
* @return map of the variables
|
||||
*/
|
||||
Map<String,Object> find(String name);
|
||||
|
||||
/**
|
||||
* Deletes variables. Variable name cab contain * wild cards.
|
||||
* @param vars
|
||||
*/
|
||||
void del(String... vars);
|
||||
|
||||
/**
|
||||
* Converts object to JSON string.
|
||||
* @param object object to convert to JSON
|
||||
* @return formatted JSON string
|
||||
*/
|
||||
String toJson(Object object);
|
||||
|
||||
/**
|
||||
* Converts object to string.
|
||||
* @param object object to convert to string
|
||||
* @return object string value
|
||||
*/
|
||||
String toString(Object object);
|
||||
|
||||
/**
|
||||
* Substitute variable reference with its value.
|
||||
* @param variable
|
||||
* @return Substituted variable
|
||||
* @throws Exception
|
||||
*/
|
||||
default Object expandParameter(String variable) {
|
||||
return expandParameter(variable, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute variable reference with its value.
|
||||
* @param variable
|
||||
* @param format serialization format
|
||||
* @return Substituted variable
|
||||
* @throws Exception
|
||||
*/
|
||||
Object expandParameter(String variable, String format);
|
||||
|
||||
/**
|
||||
* Persists object value to file.
|
||||
* @param file
|
||||
* @param object
|
||||
*/
|
||||
default void persist(Path file, Object object) {
|
||||
persist(file, object, "JSON");
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists object value to file.
|
||||
* @param file
|
||||
* @param object
|
||||
* @param format
|
||||
*/
|
||||
void persist(Path file, Object object, String format);
|
||||
|
||||
/**
|
||||
* Executes scriptEngine statement
|
||||
* @param statement
|
||||
* @return result
|
||||
* @throws Exception
|
||||
*/
|
||||
Object execute(String statement) throws Exception;
|
||||
|
||||
/**
|
||||
* Executes scriptEngine script
|
||||
* @param script
|
||||
* @return result
|
||||
* @throws Exception
|
||||
*/
|
||||
default Object execute(File script) throws Exception {
|
||||
return execute(script, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes scriptEngine script
|
||||
* @param script
|
||||
* @param args
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
Object execute(File script, Object[] args) throws Exception;
|
||||
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
package jdk.internal.org.jline.reader.impl;
|
||||
|
||||
import jdk.internal.org.jline.reader.Candidate;
|
||||
import jdk.internal.org.jline.reader.CompletingParsedLine;
|
||||
import jdk.internal.org.jline.reader.CompletionMatcher;
|
||||
import jdk.internal.org.jline.reader.LineReader;
|
||||
import jdk.internal.org.jline.utils.AttributedString;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CompletionMatcherImpl implements CompletionMatcher {
|
||||
protected Predicate<String> exact;
|
||||
protected List<Function<Map<String, List<Candidate>>, Map<String, List<Candidate>>>> matchers;
|
||||
private Map<String, List<Candidate>> matching;
|
||||
private boolean caseInsensitive;
|
||||
|
||||
public CompletionMatcherImpl() {
|
||||
}
|
||||
|
||||
protected void reset(boolean caseInsensitive) {
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
exact = s -> false;
|
||||
matchers = new ArrayList<>();
|
||||
matching = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(Map<LineReader.Option, Boolean> options, boolean prefix, CompletingParsedLine line
|
||||
, boolean caseInsensitive, int errors, String originalGroupName) {
|
||||
reset(caseInsensitive);
|
||||
defaultMatchers(options, prefix, line, caseInsensitive, errors, originalGroupName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Candidate> matches(List<Candidate> candidates) {
|
||||
matching = Collections.emptyMap();
|
||||
Map<String, List<Candidate>> sortedCandidates = sort(candidates);
|
||||
for (Function<Map<String, List<Candidate>>,
|
||||
Map<String, List<Candidate>>> matcher : matchers) {
|
||||
matching = matcher.apply(sortedCandidates);
|
||||
if (!matching.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return !matching.isEmpty() ? matching.entrySet().stream().flatMap(e -> e.getValue().stream()).collect(Collectors.toList())
|
||||
: new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Candidate exactMatch() {
|
||||
if (matching == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return matching.values().stream().flatMap(Collection::stream)
|
||||
.filter(Candidate::complete)
|
||||
.filter(c -> exact.test(c.value()))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommonPrefix() {
|
||||
if (matching == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
String commonPrefix = null;
|
||||
for (String key : matching.keySet()) {
|
||||
commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive);
|
||||
}
|
||||
return commonPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default JLine matchers
|
||||
*/
|
||||
protected void defaultMatchers(Map<LineReader.Option, Boolean> options, boolean prefix, CompletingParsedLine line
|
||||
, boolean caseInsensitive, int errors, String originalGroupName) {
|
||||
// Find matchers
|
||||
// TODO: glob completion
|
||||
String wd = line.word();
|
||||
String wdi = caseInsensitive ? wd.toLowerCase() : wd;
|
||||
String wp = wdi.substring(0, line.wordCursor());
|
||||
if (prefix) {
|
||||
matchers = new ArrayList<>(Arrays.asList(
|
||||
simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wp)),
|
||||
simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wp))
|
||||
));
|
||||
if (LineReader.Option.COMPLETE_MATCHER_TYPO.isSet(options)) {
|
||||
matchers.add(typoMatcher(wp, errors, caseInsensitive, originalGroupName));
|
||||
}
|
||||
exact = s -> caseInsensitive ? s.equalsIgnoreCase(wp) : s.equals(wp);
|
||||
} else if (!LineReader.Option.EMPTY_WORD_OPTIONS.isSet(options) && wd.length() == 0) {
|
||||
matchers = new ArrayList<>(Collections.singletonList(simpleMatcher(s -> !s.startsWith("-"))));
|
||||
exact = s -> caseInsensitive ? s.equalsIgnoreCase(wd) : s.equals(wd);
|
||||
} else {
|
||||
if (LineReader.Option.COMPLETE_IN_WORD.isSet(options)) {
|
||||
String ws = wdi.substring(line.wordCursor());
|
||||
Pattern p1 = Pattern.compile(Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*");
|
||||
Pattern p2 = Pattern.compile(".*" + Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*");
|
||||
matchers = new ArrayList<>(Arrays.asList(
|
||||
simpleMatcher(s -> p1.matcher(caseInsensitive ? s.toLowerCase() : s).matches()),
|
||||
simpleMatcher(s -> p2.matcher(caseInsensitive ? s.toLowerCase() : s).matches())
|
||||
));
|
||||
} else {
|
||||
matchers = new ArrayList<>(Arrays.asList(
|
||||
simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wdi)),
|
||||
simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wdi))
|
||||
));
|
||||
}
|
||||
if (LineReader.Option.COMPLETE_MATCHER_CAMELCASE.isSet(options)) {
|
||||
matchers.add(simpleMatcher(s -> camelMatch(wd, 0, s, 0)));
|
||||
}
|
||||
if (LineReader.Option.COMPLETE_MATCHER_TYPO.isSet(options)) {
|
||||
matchers.add(typoMatcher(wdi, errors, caseInsensitive, originalGroupName));
|
||||
}
|
||||
exact = s -> caseInsensitive ? s.equalsIgnoreCase(wd) : s.equals(wd);
|
||||
}
|
||||
}
|
||||
|
||||
protected Function<Map<String, List<Candidate>>,
|
||||
Map<String, List<Candidate>>> simpleMatcher(Predicate<String> predicate) {
|
||||
return m -> m.entrySet().stream()
|
||||
.filter(e -> predicate.test(e.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
protected Function<Map<String, List<Candidate>>,
|
||||
Map<String, List<Candidate>>> typoMatcher(String word, int errors, boolean caseInsensitive, String originalGroupName) {
|
||||
return m -> {
|
||||
Map<String, List<Candidate>> map = m.entrySet().stream()
|
||||
.filter(e -> ReaderUtils.distance(word, caseInsensitive ? e.getKey().toLowerCase() : e.getKey()) < errors)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
if (map.size() > 1) {
|
||||
map.computeIfAbsent(word, w -> new ArrayList<>())
|
||||
.add(new Candidate(word, word, originalGroupName, null, null, null, false));
|
||||
}
|
||||
return map;
|
||||
};
|
||||
}
|
||||
|
||||
protected boolean camelMatch(String word, int i, String candidate, int j) {
|
||||
if (word.length() <= i) {
|
||||
return true;
|
||||
} else if (candidate.length() <= j) {
|
||||
return false;
|
||||
} else {
|
||||
char c = word.charAt(i);
|
||||
if (c == candidate.charAt(j)) {
|
||||
return camelMatch(word, i + 1, candidate, j + 1);
|
||||
} else {
|
||||
for (int j1 = j; j1 < candidate.length(); j1++) {
|
||||
if (Character.isUpperCase(candidate.charAt(j1))) {
|
||||
if (Character.toUpperCase(c) == candidate.charAt(j1)) {
|
||||
if (camelMatch(word, i + 1, candidate, j1 + 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, List<Candidate>> sort(List<Candidate> candidates) {
|
||||
// Build a list of sorted candidates
|
||||
Map<String, List<Candidate>> sortedCandidates = new HashMap<>();
|
||||
for (Candidate candidate : candidates) {
|
||||
sortedCandidates
|
||||
.computeIfAbsent(AttributedString.fromAnsi(candidate.value()).toString(), s -> new ArrayList<>())
|
||||
.add(candidate);
|
||||
}
|
||||
return sortedCandidates;
|
||||
}
|
||||
|
||||
private String getCommonStart(String str1, String str2, boolean caseInsensitive) {
|
||||
int[] s1 = str1.codePoints().toArray();
|
||||
int[] s2 = str2.codePoints().toArray();
|
||||
int len = 0;
|
||||
while (len < Math.min(s1.length, s2.length)) {
|
||||
int ch1 = s1[len];
|
||||
int ch2 = s2[len];
|
||||
if (ch1 != ch2 && caseInsensitive) {
|
||||
ch1 = Character.toUpperCase(ch1);
|
||||
ch2 = Character.toUpperCase(ch2);
|
||||
if (ch1 != ch2) {
|
||||
ch1 = Character.toLowerCase(ch1);
|
||||
ch2 = Character.toLowerCase(ch2);
|
||||
}
|
||||
}
|
||||
if (ch1 != ch2) {
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
return new String(s1, 0, len);
|
||||
}
|
||||
|
||||
}
|
@ -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.
|
||||
@ -19,8 +19,8 @@ import jdk.internal.org.jline.utils.AttributedStyle;
|
||||
import jdk.internal.org.jline.utils.WCWidth;
|
||||
|
||||
public class DefaultHighlighter implements Highlighter {
|
||||
private Pattern errorPattern;
|
||||
private int errorIndex = -1;
|
||||
protected Pattern errorPattern;
|
||||
protected int errorIndex = -1;
|
||||
|
||||
@Override
|
||||
public void setErrorPattern(Pattern errorPattern) {
|
||||
|
@ -24,7 +24,7 @@ public class DefaultParser implements Parser {
|
||||
ROUND, // ()
|
||||
CURLY, // {}
|
||||
SQUARE, // []
|
||||
ANGLE; // <>
|
||||
ANGLE // <>
|
||||
}
|
||||
|
||||
private char[] quoteChars = {'\'', '"'};
|
||||
@ -39,8 +39,8 @@ public class DefaultParser implements Parser {
|
||||
|
||||
private char[] closingBrackets = null;
|
||||
|
||||
private String regexVariable = "[a-zA-Z_]{1,}[a-zA-Z0-9_-]*((.|\\['|\\[\\\"|\\[)[a-zA-Z0-9_-]*(|'\\]|\\\"\\]|\\])){0,1}";
|
||||
private String regexCommand = "[:]{0,1}[a-zA-Z]{1,}[a-zA-Z0-9_-]*";
|
||||
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;
|
||||
|
||||
//
|
||||
@ -175,23 +175,25 @@ public class DefaultParser implements Parser {
|
||||
|
||||
@Override
|
||||
public boolean validVariableName(String name) {
|
||||
return name != null && name.matches(regexVariable);
|
||||
return name != null && regexVariable != null && name.matches(regexVariable);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getCommand(final String line) {
|
||||
String out = "";
|
||||
Pattern patternCommand = Pattern.compile("^\\s*" + regexVariable + "=(" + regexCommand + ")(\\s+.*|$)");
|
||||
Matcher matcher = patternCommand.matcher(line);
|
||||
if (matcher.find()) {
|
||||
out = matcher.group(commandGroup);
|
||||
} else {
|
||||
out = line.trim().split("\\s+")[0];
|
||||
int idx = out.indexOf("=");
|
||||
if (idx > -1) {
|
||||
out = out.substring(idx + 1);
|
||||
boolean checkCommandOnly = regexVariable == null;
|
||||
if (!checkCommandOnly) {
|
||||
Pattern patternCommand = Pattern.compile("^\\s*" + regexVariable + "=(" + regexCommand + ")(\\s+|$)");
|
||||
Matcher matcher = patternCommand.matcher(line);
|
||||
if (matcher.find()) {
|
||||
out = matcher.group(commandGroup);
|
||||
} else {
|
||||
checkCommandOnly = true;
|
||||
}
|
||||
}
|
||||
if (checkCommandOnly) {
|
||||
out = line.trim().split("\\s+")[0];
|
||||
if (!out.matches(regexCommand)) {
|
||||
out = "";
|
||||
}
|
||||
@ -202,10 +204,12 @@ public class DefaultParser implements Parser {
|
||||
@Override
|
||||
public String getVariable(final String line) {
|
||||
String out = null;
|
||||
Pattern patternCommand = Pattern.compile("^\\s*(" + regexVariable + ")\\s*=[^=~].*");
|
||||
Matcher matcher = patternCommand.matcher(line);
|
||||
if (matcher.find()) {
|
||||
out = matcher.group(1);
|
||||
if (regexVariable != null) {
|
||||
Pattern patternCommand = Pattern.compile("^\\s*(" + regexVariable + ")\\s*=[^=~].*");
|
||||
Matcher matcher = patternCommand.matcher(line);
|
||||
if (matcher.find()) {
|
||||
out = matcher.group(1);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -289,7 +293,7 @@ public class DefaultParser implements Parser {
|
||||
rawWordLength = rawWordCursor;
|
||||
}
|
||||
|
||||
if (context != ParseContext.COMPLETE) {
|
||||
if (context != ParseContext.COMPLETE && context != ParseContext.SPLIT_LINE) {
|
||||
if (eofOnEscapedNewLine && isEscapeChar(line, line.length() - 1)) {
|
||||
throw new EOFError(-1, -1, "Escaped new line", "newline");
|
||||
}
|
||||
@ -609,25 +613,28 @@ public class DefaultParser implements Parser {
|
||||
}
|
||||
}
|
||||
if (escapeChars != null) {
|
||||
// Completion is protected by an opening quote:
|
||||
// Delimiters (spaces) don't need to be escaped, nor do other quotes, but everything else does.
|
||||
// Also, close the quote at the end
|
||||
if (openingQuote != null) {
|
||||
needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)) || String.valueOf(sb.charAt(i)).equals(openingQuote);
|
||||
}
|
||||
// Completion is protected by middle quotes:
|
||||
// Delimiters (spaces) don't need to be escaped, nor do quotes, but everything else does.
|
||||
else if (middleQuotes) {
|
||||
needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i));
|
||||
}
|
||||
// No quote protection, need to escape everything: delimiter chars (spaces), quote chars
|
||||
// and escapes themselves
|
||||
else {
|
||||
needToBeEscaped = i -> isDelimiterChar(sb, i) || isRawEscapeChar(sb.charAt(i)) || isRawQuoteChar(sb.charAt(i));
|
||||
}
|
||||
for (int i = 0; i < sb.length(); i++) {
|
||||
if (needToBeEscaped.test(i)) {
|
||||
sb.insert(i++, escapeChars[0]);
|
||||
if (escapeChars.length > 0) {
|
||||
// Completion is protected by an opening quote:
|
||||
// Delimiters (spaces) don't need to be escaped, nor do other quotes, but everything else does.
|
||||
// Also, close the quote at the end
|
||||
if (openingQuote != null) {
|
||||
needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)) || String.valueOf(sb.charAt(i)).equals(openingQuote);
|
||||
}
|
||||
// Completion is protected by middle quotes:
|
||||
// Delimiters (spaces) don't need to be escaped, nor do quotes, but everything else does.
|
||||
else if (middleQuotes) {
|
||||
needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i));
|
||||
}
|
||||
// No quote protection, need to escape everything: delimiter chars (spaces), quote chars
|
||||
// and escapes themselves
|
||||
else {
|
||||
needToBeEscaped = i -> isDelimiterChar(sb, i) || isRawEscapeChar(sb.charAt(i))
|
||||
|| isRawQuoteChar(sb.charAt(i));
|
||||
}
|
||||
for (int i = 0; i < sb.length(); i++) {
|
||||
if (needToBeEscaped.test(i)) {
|
||||
sb.insert(i++, escapeChars[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (openingQuote == null && !middleQuotes) {
|
||||
|
@ -20,7 +20,6 @@ import java.io.InterruptedIOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.*;
|
||||
@ -46,9 +45,9 @@ import jdk.internal.org.jline.utils.AttributedStyle;
|
||||
import jdk.internal.org.jline.utils.Curses;
|
||||
import jdk.internal.org.jline.utils.Display;
|
||||
import jdk.internal.org.jline.utils.InfoCmp.Capability;
|
||||
import jdk.internal.org.jline.utils.Levenshtein;
|
||||
import jdk.internal.org.jline.utils.Log;
|
||||
import jdk.internal.org.jline.utils.Status;
|
||||
import jdk.internal.org.jline.utils.StyleResolver;
|
||||
import jdk.internal.org.jline.utils.WCWidth;
|
||||
|
||||
import static jdk.internal.org.jline.keymap.KeyMap.alt;
|
||||
@ -80,18 +79,26 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
public static final String DEFAULT_SEARCH_TERMINATORS = "\033\012";
|
||||
public static final String DEFAULT_BELL_STYLE = "";
|
||||
public static final int DEFAULT_LIST_MAX = 100;
|
||||
public static final int DEFAULT_MENU_LIST_MAX = Integer.MAX_VALUE;
|
||||
public static final int DEFAULT_ERRORS = 2;
|
||||
public static final long DEFAULT_BLINK_MATCHING_PAREN = 500L;
|
||||
public static final long DEFAULT_AMBIGUOUS_BINDING = 1000L;
|
||||
public static final String DEFAULT_SECONDARY_PROMPT_PATTERN = "%M> ";
|
||||
public static final String DEFAULT_OTHERS_GROUP_NAME = "others";
|
||||
public static final String DEFAULT_ORIGINAL_GROUP_NAME = "original";
|
||||
public static final String DEFAULT_COMPLETION_STYLE_STARTING = "36"; // cyan
|
||||
public static final String DEFAULT_COMPLETION_STYLE_DESCRIPTION = "90"; // dark gray
|
||||
public static final String DEFAULT_COMPLETION_STYLE_GROUP = "35;1"; // magenta
|
||||
public static final String DEFAULT_COMPLETION_STYLE_SELECTION = "7"; // inverted
|
||||
public static final String DEFAULT_COMPLETION_STYLE_STARTING = "fg:cyan";
|
||||
public static final String DEFAULT_COMPLETION_STYLE_DESCRIPTION = "fg:bright-black";
|
||||
public static final String DEFAULT_COMPLETION_STYLE_GROUP = "fg:bright-magenta,bold";
|
||||
public static final String DEFAULT_COMPLETION_STYLE_SELECTION = "inverse";
|
||||
public static final String DEFAULT_COMPLETION_STYLE_BACKGROUND = "bg:default";
|
||||
public static final String DEFAULT_COMPLETION_STYLE_LIST_STARTING = DEFAULT_COMPLETION_STYLE_STARTING;
|
||||
public static final String DEFAULT_COMPLETION_STYLE_LIST_DESCRIPTION = DEFAULT_COMPLETION_STYLE_DESCRIPTION;
|
||||
public static final String DEFAULT_COMPLETION_STYLE_LIST_GROUP = "fg:black,bold";
|
||||
public static final String DEFAULT_COMPLETION_STYLE_LIST_SELECTION = DEFAULT_COMPLETION_STYLE_SELECTION;
|
||||
public static final String DEFAULT_COMPLETION_STYLE_LIST_BACKGROUND = "bg:bright-magenta";
|
||||
public static final int DEFAULT_INDENTATION = 0;
|
||||
public static final int DEFAULT_FEATURES_MAX_BUFFER_SIZE = 1000;
|
||||
public static final int DEFAULT_SUGGESTIONS_MIN_BUFFER_SIZE = 1;
|
||||
|
||||
private static final int MIN_ROWS = 3;
|
||||
|
||||
@ -162,6 +169,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
protected Highlighter highlighter = new DefaultHighlighter();
|
||||
protected Parser parser = new DefaultParser();
|
||||
protected Expander expander = new DefaultExpander();
|
||||
protected CompletionMatcher completionMatcher = new CompletionMatcherImpl();
|
||||
|
||||
//
|
||||
// State variables
|
||||
@ -270,6 +278,8 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
*/
|
||||
protected List<String> commandsBuffer = new ArrayList<>();
|
||||
|
||||
int candidateStartPosition = 0;
|
||||
|
||||
public LineReaderImpl(Terminal terminal) throws IOException {
|
||||
this(terminal, null, null);
|
||||
}
|
||||
@ -419,6 +429,10 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
this.expander = expander;
|
||||
}
|
||||
|
||||
public void setCompletionMatcher(CompletionMatcher completionMatcher) {
|
||||
this.completionMatcher = completionMatcher;
|
||||
}
|
||||
|
||||
//
|
||||
// Line Reading
|
||||
//
|
||||
@ -636,7 +650,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
Binding o = readBinding(getKeys(), local);
|
||||
if (o == null) {
|
||||
throw new EndOfFileException();
|
||||
throw new EndOfFileException().partialLine(buf.length() > 0 ? buf.toString() : null);
|
||||
}
|
||||
Log.trace("Binding: ", o);
|
||||
if (buf.length() == 0 && getLastBinding().charAt(0) == originalAttributes.getControlChar(ControlChar.VEOF)) {
|
||||
@ -741,11 +755,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
size.copy(terminal.getBufferSize());
|
||||
|
||||
display = new Display(terminal, false);
|
||||
if (size.getRows() == 0 || size.getColumns() == 0) {
|
||||
display.resize(1, Integer.MAX_VALUE);
|
||||
} else {
|
||||
display.resize(size.getRows(), size.getColumns());
|
||||
}
|
||||
display.resize(size.getRows(), size.getColumns());
|
||||
if (isSet(Option.DELAY_LINE_WRAP))
|
||||
display.setDelayLineWrap(true);
|
||||
}
|
||||
@ -1049,8 +1059,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
|
||||
@Override
|
||||
public boolean isSet(Option option) {
|
||||
Boolean b = options.get(option);
|
||||
return b != null ? b : option.isDef();
|
||||
return option.isSet(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1070,10 +1079,13 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
|
||||
@Override
|
||||
public void editAndAddInBuffer(File file) throws Exception {
|
||||
if (isSet(Option.BRACKETED_PASTE)) {
|
||||
terminal.writer().write(BRACKETED_PASTE_OFF);
|
||||
}
|
||||
Constructor<?> ctor = Class.forName("org.jline.builtins.Nano").getConstructor(Terminal.class, File.class);
|
||||
Editor editor = (Editor) ctor.newInstance(terminal, new File(file.getParent()));
|
||||
editor.setRestricted(true);
|
||||
editor.open(Arrays.asList(file.getName()));
|
||||
editor.open(Collections.singletonList(file.getName()));
|
||||
editor.run();
|
||||
BufferedReader br = new BufferedReader(new FileReader(file));
|
||||
String line;
|
||||
@ -1344,7 +1356,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
|
||||
protected boolean viForwardWord() {
|
||||
if (count < 0) {
|
||||
return callNeg(this::backwardWord);
|
||||
return callNeg(this::viBackwardWord);
|
||||
}
|
||||
while (count-- > 0) {
|
||||
if (isViAlphaNum(buf.currChar())) {
|
||||
@ -1395,21 +1407,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
|
||||
protected boolean emacsForwardWord() {
|
||||
if (count < 0) {
|
||||
return callNeg(this::emacsBackwardWord);
|
||||
}
|
||||
while (count-- > 0) {
|
||||
while (buf.cursor() < buf.length() && !isWord(buf.currChar())) {
|
||||
buf.move(1);
|
||||
}
|
||||
if (isInViChangeOperation() && count == 0) {
|
||||
return true;
|
||||
}
|
||||
while (buf.cursor() < buf.length() && isWord(buf.currChar())) {
|
||||
buf.move(1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return forwardWord();
|
||||
}
|
||||
|
||||
protected boolean viForwardBlankWordEnd() {
|
||||
@ -1481,7 +1479,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
|
||||
protected boolean viBackwardWord() {
|
||||
if (count < 0) {
|
||||
return callNeg(this::backwardWord);
|
||||
return callNeg(this::viForwardWord);
|
||||
}
|
||||
while (count-- > 0) {
|
||||
int nl = 0;
|
||||
@ -1584,24 +1582,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
|
||||
protected boolean emacsBackwardWord() {
|
||||
if (count < 0) {
|
||||
return callNeg(this::emacsForwardWord);
|
||||
}
|
||||
while (count-- > 0) {
|
||||
while (buf.cursor() > 0) {
|
||||
buf.move(-1);
|
||||
if (isWord(buf.currChar())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (buf.cursor() > 0) {
|
||||
buf.move(-1);
|
||||
if (!isWord(buf.currChar())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return backwardWord();
|
||||
}
|
||||
|
||||
protected boolean backwardDeleteWord() {
|
||||
@ -2557,7 +2538,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
terminal.puts(Capability.keypad_local);
|
||||
terminal.trackMouse(Terminal.MouseTracking.Off);
|
||||
if (isSet(Option.BRACKETED_PASTE))
|
||||
if (isSet(Option.BRACKETED_PASTE) && !isTerminalDumb())
|
||||
terminal.writer().write(BRACKETED_PASTE_OFF);
|
||||
flush();
|
||||
}
|
||||
@ -2720,7 +2701,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
starts.add(new Pair<>(index, m.start()));
|
||||
}
|
||||
return starts;
|
||||
}
|
||||
}
|
||||
|
||||
private String doGetSearchPattern() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@ -3908,7 +3889,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
|
||||
List<AttributedString> newLinesToDisplay = new ArrayList<>();
|
||||
int displaySize = size.getRows() - (status != null ? status.size() : 0);
|
||||
int displaySize = displayRows(status);
|
||||
if (newLines.size() > displaySize && !isTerminalDumb()) {
|
||||
StringBuilder sb = new StringBuilder(">....");
|
||||
// blanks are needed when displaying command completion candidate list
|
||||
@ -3964,13 +3945,12 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
History history = getHistory();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char prev = '0';
|
||||
for (char c: buffer.toCharArray()) {
|
||||
if ((c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^') && prev != '\\' ) {
|
||||
for (char c: buffer.replace("\\", "\\\\").toCharArray()) {
|
||||
if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '*'
|
||||
|| c == '$' || c == '.' || c == '?' || c == '+') {
|
||||
sb.append('\\');
|
||||
}
|
||||
sb.append(c);
|
||||
prev = c;
|
||||
}
|
||||
Pattern pattern = Pattern.compile(sb.toString() + ".*", Pattern.DOTALL);
|
||||
Iterator<History.Entry> iter = history.reverseIterator(history.last());
|
||||
@ -4002,7 +3982,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
AttributedStringBuilder full = new AttributedStringBuilder().tabs(TAB_WIDTH);
|
||||
full.append(prompt);
|
||||
full.append(tNewBuf);
|
||||
if (doAutosuggestion) {
|
||||
if (doAutosuggestion && !isTerminalDumb()) {
|
||||
String lastBinding = getLastBinding() != null ? getLastBinding() : "";
|
||||
if (autosuggestion == SuggestionType.HISTORY) {
|
||||
AttributedStringBuilder sb = new AttributedStringBuilder();
|
||||
@ -4010,8 +3990,9 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
sb.styled(AttributedStyle::faint, tailTip);
|
||||
full.append(sb.toAttributedString());
|
||||
} else if (autosuggestion == SuggestionType.COMPLETER) {
|
||||
if (buf.length() > 0 && buf.length() == buf.cursor()
|
||||
&& (!lastBinding.equals("\t") || buf.prevChar() == ' ' || buf.prevChar() == '=')) {
|
||||
if (buf.length() >= getInt(SUGGESTIONS_MIN_BUFFER_SIZE, DEFAULT_SUGGESTIONS_MIN_BUFFER_SIZE)
|
||||
&& buf.length() == buf.cursor()
|
||||
&& (!lastBinding.equals("\t") || buf.prevChar() == ' ' || buf.prevChar() == '=')) {
|
||||
clearChoices();
|
||||
listChoices(true);
|
||||
} else if (!lastBinding.equals("\t")) {
|
||||
@ -4184,6 +4165,9 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
List<String> missings = new ArrayList<>();
|
||||
if (computePrompts && secondaryPromptPattern.contains("%P")) {
|
||||
width = prompt.columnLength();
|
||||
if (width > size.getColumns() || prompt.contains('\n')) {
|
||||
width = new TerminalLine(prompt.toString(), 0, size.getColumns()).getEndLine().length();
|
||||
}
|
||||
for (int line = 0; line < lines.size() - 1; line++) {
|
||||
AttributedString prompt;
|
||||
buf.append(lines.get(line)).append("\n");
|
||||
@ -4398,6 +4382,9 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.info("Error while finding completion candidates", e);
|
||||
if (Log.isDebugEnabled()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4423,79 +4410,17 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
boolean caseInsensitive = isSet(Option.CASE_INSENSITIVE);
|
||||
int errors = getInt(ERRORS, DEFAULT_ERRORS);
|
||||
|
||||
// Build a list of sorted candidates
|
||||
Map<String, List<Candidate>> sortedCandidates = new HashMap<>();
|
||||
for (Candidate cand : candidates) {
|
||||
sortedCandidates
|
||||
.computeIfAbsent(AttributedString.fromAnsi(cand.value()).toString(), s -> new ArrayList<>())
|
||||
.add(cand);
|
||||
}
|
||||
|
||||
// Find matchers
|
||||
// TODO: glob completion
|
||||
List<Function<Map<String, List<Candidate>>,
|
||||
Map<String, List<Candidate>>>> matchers;
|
||||
Predicate<String> exact;
|
||||
if (prefix) {
|
||||
String wd = line.word();
|
||||
String wdi = caseInsensitive ? wd.toLowerCase() : wd;
|
||||
String wp = wdi.substring(0, line.wordCursor());
|
||||
matchers = Arrays.asList(
|
||||
simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wp)),
|
||||
simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wp)),
|
||||
typoMatcher(wp, errors, caseInsensitive)
|
||||
);
|
||||
exact = s -> caseInsensitive ? s.equalsIgnoreCase(wp) : s.equals(wp);
|
||||
} else if (isSet(Option.COMPLETE_IN_WORD)) {
|
||||
String wd = line.word();
|
||||
String wdi = caseInsensitive ? wd.toLowerCase() : wd;
|
||||
String wp = wdi.substring(0, line.wordCursor());
|
||||
String ws = wdi.substring(line.wordCursor());
|
||||
Pattern p1 = Pattern.compile(Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*");
|
||||
Pattern p2 = Pattern.compile(".*" + Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*");
|
||||
matchers = Arrays.asList(
|
||||
simpleMatcher(s -> p1.matcher(caseInsensitive ? s.toLowerCase() : s).matches()),
|
||||
simpleMatcher(s -> p2.matcher(caseInsensitive ? s.toLowerCase() : s).matches()),
|
||||
typoMatcher(wdi, errors, caseInsensitive)
|
||||
);
|
||||
exact = s -> caseInsensitive ? s.equalsIgnoreCase(wd) : s.equals(wd);
|
||||
} else {
|
||||
String wd = line.word();
|
||||
String wdi = caseInsensitive ? wd.toLowerCase() : wd;
|
||||
if (isSet(Option.EMPTY_WORD_OPTIONS) || wd.length() > 0) {
|
||||
matchers = Arrays.asList(
|
||||
simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wdi)),
|
||||
simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wdi)),
|
||||
typoMatcher(wdi, errors, caseInsensitive)
|
||||
);
|
||||
} else {
|
||||
matchers = Arrays.asList(
|
||||
simpleMatcher(s -> !s.startsWith("-"))
|
||||
);
|
||||
}
|
||||
exact = s -> caseInsensitive ? s.equalsIgnoreCase(wd) : s.equals(wd);
|
||||
}
|
||||
completionMatcher.compile(options, prefix, line, caseInsensitive, errors, getOriginalGroupName());
|
||||
// Find matching candidates
|
||||
Map<String, List<Candidate>> matching = Collections.emptyMap();
|
||||
for (Function<Map<String, List<Candidate>>,
|
||||
Map<String, List<Candidate>>> matcher : matchers) {
|
||||
matching = matcher.apply(sortedCandidates);
|
||||
if (!matching.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<Candidate> possible = completionMatcher.matches(candidates);
|
||||
// If we have no matches, bail out
|
||||
if (matching.isEmpty()) {
|
||||
if (possible.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
size.copy(terminal.getSize());
|
||||
try {
|
||||
// If we only need to display the list, do it now
|
||||
if (lst == CompletionType.List) {
|
||||
List<Candidate> possible = matching.entrySet().stream()
|
||||
.flatMap(e -> e.getValue().stream())
|
||||
.collect(Collectors.toList());
|
||||
doList(possible, line.word(), false, line::escape, forSuggestion);
|
||||
return !possible.isEmpty();
|
||||
}
|
||||
@ -4503,16 +4428,12 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
// Check if there's a single possible match
|
||||
Candidate completion = null;
|
||||
// If there's a single possible completion
|
||||
if (matching.size() == 1) {
|
||||
completion = matching.values().stream().flatMap(Collection::stream)
|
||||
.findFirst().orElse(null);
|
||||
if (possible.size() == 1) {
|
||||
completion = possible.get(0);
|
||||
}
|
||||
// Or if RECOGNIZE_EXACT is set, try to find an exact match
|
||||
else if (isSet(Option.RECOGNIZE_EXACT)) {
|
||||
completion = matching.values().stream().flatMap(Collection::stream)
|
||||
.filter(Candidate::complete)
|
||||
.filter(c -> exact.test(c.value()))
|
||||
.findFirst().orElse(null);
|
||||
completion = completionMatcher.exactMatch();
|
||||
}
|
||||
// Complete and exit
|
||||
if (completion != null && !completion.value().isEmpty()) {
|
||||
@ -4531,6 +4452,9 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
}
|
||||
if (completion.suffix() != null) {
|
||||
if (autosuggestion == SuggestionType.COMPLETER) {
|
||||
listChoices(true);
|
||||
}
|
||||
redisplay();
|
||||
Binding op = readBinding(getKeys());
|
||||
if (op != null) {
|
||||
@ -4549,10 +4473,6 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
return true;
|
||||
}
|
||||
|
||||
List<Candidate> possible = matching.entrySet().stream()
|
||||
.flatMap(e -> e.getValue().stream())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (useMenu) {
|
||||
buf.move(line.word().length() - line.wordCursor());
|
||||
buf.backspace(line.word().length());
|
||||
@ -4570,10 +4490,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
// Now, we need to find the unambiguous completion
|
||||
// TODO: need to find common suffix
|
||||
String commonPrefix = null;
|
||||
for (String key : matching.keySet()) {
|
||||
commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive);
|
||||
}
|
||||
String commonPrefix = completionMatcher.getCommonPrefix();
|
||||
boolean hasUnambiguous = commonPrefix.startsWith(current) && !commonPrefix.equals(current);
|
||||
|
||||
if (hasUnambiguous) {
|
||||
@ -4641,7 +4558,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
|
||||
protected Comparator<Candidate> getCandidateComparator(boolean caseInsensitive, String word) {
|
||||
String wdi = caseInsensitive ? word.toLowerCase() : word;
|
||||
ToIntFunction<String> wordDistance = w -> distance(wdi, caseInsensitive ? w.toLowerCase() : w);
|
||||
ToIntFunction<String> wordDistance = w -> ReaderUtils.distance(wdi, caseInsensitive ? w.toLowerCase() : w);
|
||||
return Comparator
|
||||
.comparing(Candidate::value, Comparator.comparingInt(wordDistance))
|
||||
.thenComparing(Comparator.naturalOrder());
|
||||
@ -4688,37 +4605,6 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
}
|
||||
|
||||
private Function<Map<String, List<Candidate>>,
|
||||
Map<String, List<Candidate>>> simpleMatcher(Predicate<String> pred) {
|
||||
return m -> m.entrySet().stream()
|
||||
.filter(e -> pred.test(e.getKey()))
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
|
||||
private Function<Map<String, List<Candidate>>,
|
||||
Map<String, List<Candidate>>> typoMatcher(String word, int errors, boolean caseInsensitive) {
|
||||
return m -> {
|
||||
Map<String, List<Candidate>> map = m.entrySet().stream()
|
||||
.filter(e -> distance(word, caseInsensitive ? e.getKey() : e.getKey().toLowerCase()) < errors)
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
if (map.size() > 1) {
|
||||
map.computeIfAbsent(word, w -> new ArrayList<>())
|
||||
.add(new Candidate(word, word, getOriginalGroupName(), null, null, null, false));
|
||||
}
|
||||
return map;
|
||||
};
|
||||
}
|
||||
|
||||
private int distance(String word, String cand) {
|
||||
if (word.length() < cand.length()) {
|
||||
int d1 = Levenshtein.distance(word, cand.substring(0, Math.min(cand.length(), word.length())));
|
||||
int d2 = Levenshtein.distance(word, cand);
|
||||
return Math.min(d1, d2);
|
||||
} else {
|
||||
return Levenshtein.distance(word, cand);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean nextBindingIsComplete() {
|
||||
redisplay();
|
||||
KeyMap<Binding> keyMap = keyMaps.get(MENU);
|
||||
@ -4731,6 +4617,19 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
}
|
||||
|
||||
private int displayRows() {
|
||||
return displayRows(Status.getStatus(terminal, false));
|
||||
}
|
||||
|
||||
private int displayRows(Status status) {
|
||||
return size.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();
|
||||
}
|
||||
|
||||
private class MenuSupport implements Supplier<AttributedString> {
|
||||
final List<Candidate> possible;
|
||||
final BiFunction<CharSequence, Boolean, CharSequence> escaper;
|
||||
@ -4850,10 +4749,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
|
||||
// Compute displayed prompt
|
||||
PostResult pr = computePost(possible, completion(), null, completed);
|
||||
AttributedString text = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>());
|
||||
int promptLines = text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size();
|
||||
Status status = Status.getStatus(terminal, false);
|
||||
int displaySize = size.getRows() - (status != null ? status.size() : 0) - promptLines;
|
||||
int displaySize = displayRows() - promptLines();
|
||||
if (pr.lines > displaySize) {
|
||||
int displayed = displaySize - 1;
|
||||
if (pr.selectedLine >= 0) {
|
||||
@ -4902,7 +4798,13 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
original.sort(getCandidateComparator(caseInsensitive, completed));
|
||||
mergeCandidates(original);
|
||||
computePost(original, null, possible, completed);
|
||||
|
||||
// candidate grouping is not supported by MenuSupport
|
||||
boolean defaultAutoGroup = isSet(Option.AUTO_GROUP);
|
||||
boolean defaultGroup = isSet(Option.GROUP);
|
||||
if (!isSet(Option.GROUP_PERSIST)) {
|
||||
option(Option.AUTO_GROUP, false);
|
||||
option(Option.GROUP, false);
|
||||
}
|
||||
// Build menu support
|
||||
MenuSupport menuSupport = new MenuSupport(original, completed, escaper);
|
||||
post = menuSupport;
|
||||
@ -4959,17 +4861,21 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
pushBackBinding(true);
|
||||
}
|
||||
post = null;
|
||||
option(Option.AUTO_GROUP, defaultAutoGroup);
|
||||
option(Option.GROUP, defaultGroup);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
doAutosuggestion = false;
|
||||
callWidget(REDISPLAY);
|
||||
}
|
||||
option(Option.AUTO_GROUP, defaultAutoGroup);
|
||||
option(Option.GROUP, defaultGroup);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean clearChoices() {
|
||||
return doList(new ArrayList<Candidate>(), "", false, null, false);
|
||||
return doList(new ArrayList<>(), "", false, null, false);
|
||||
}
|
||||
|
||||
protected boolean doList(List<Candidate> possible
|
||||
@ -5009,14 +4915,14 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
|
||||
boolean caseInsensitive = isSet(Option.CASE_INSENSITIVE);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
candidateStartPosition = 0;
|
||||
while (true) {
|
||||
String current = completed + sb.toString();
|
||||
List<Candidate> cands;
|
||||
if (sb.length() > 0) {
|
||||
cands = possible.stream()
|
||||
.filter(c -> caseInsensitive
|
||||
? c.value().toLowerCase().startsWith(current.toLowerCase())
|
||||
: c.value().startsWith(current))
|
||||
completionMatcher.compile(options, false, new CompletingWord(current), caseInsensitive, 0
|
||||
, null);
|
||||
cands = completionMatcher.matches(possible).stream()
|
||||
.sorted(getCandidateComparator(caseInsensitive, current))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
@ -5024,6 +4930,9 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
.sorted(getCandidateComparator(caseInsensitive, current))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
if (isSet(Option.AUTO_MENU_LIST) && candidateStartPosition == 0) {
|
||||
candidateStartPosition = candidateStartPosition(cands);
|
||||
}
|
||||
post = () -> {
|
||||
AttributedString t = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>());
|
||||
int pl = t.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size();
|
||||
@ -5035,10 +4944,11 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
redisplay(false);
|
||||
buf.cursor(oldCursor);
|
||||
println();
|
||||
List<AttributedString> ls = postResult.post.columnSplitLength(size.getColumns(), false, display.delayLineWrap());
|
||||
List<AttributedString> ls = pr.post.columnSplitLength(size.getColumns(), false, display.delayLineWrap());
|
||||
Display d = new Display(terminal, false);
|
||||
d.resize(size.getRows(), size.getColumns());
|
||||
d.update(ls, -1);
|
||||
println();
|
||||
redrawLine();
|
||||
return new AttributedString("");
|
||||
}
|
||||
@ -5089,6 +4999,59 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
}
|
||||
|
||||
private static class CompletingWord implements CompletingParsedLine {
|
||||
private final String word;
|
||||
|
||||
public CompletingWord(String word) {
|
||||
this.word = word;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence escape(CharSequence candidate, boolean complete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rawWordCursor() {
|
||||
return word.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rawWordLength() {
|
||||
return word.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String word() {
|
||||
return word;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int wordCursor() {
|
||||
return word.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int wordIndex() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> words() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String line() {
|
||||
return word;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int cursor() {
|
||||
return word.length();
|
||||
}
|
||||
}
|
||||
|
||||
protected static class PostResult {
|
||||
final AttributedString post;
|
||||
final int lines;
|
||||
@ -5156,6 +5119,63 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
private static final String DESC_SUFFIX = ")";
|
||||
private static final int MARGIN_BETWEEN_DISPLAY_AND_DESC = 1;
|
||||
private static final int MARGIN_BETWEEN_COLUMNS = 3;
|
||||
private static final int MENU_LIST_WIDTH = 25;
|
||||
|
||||
private static class TerminalLine {
|
||||
private String endLine;
|
||||
private int startPos;
|
||||
|
||||
public TerminalLine(String line, int startPos, int width) {
|
||||
this.startPos = startPos;
|
||||
endLine = line.substring(line.lastIndexOf('\n') + 1);
|
||||
boolean first = true;
|
||||
while (endLine.length() + (first ? startPos : 0) > width) {
|
||||
if (first) {
|
||||
endLine = endLine.substring(width - startPos);
|
||||
} else {
|
||||
endLine = endLine.substring(width);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
if (!first) {
|
||||
this.startPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getStartPos() {
|
||||
return startPos;
|
||||
}
|
||||
|
||||
public String getEndLine() {
|
||||
return endLine;
|
||||
}
|
||||
}
|
||||
|
||||
private int candidateStartPosition(List<Candidate> cands) {
|
||||
List<String> values = cands.stream().map(c -> AttributedString.stripAnsi(c.displ()))
|
||||
.filter(c -> !c.matches("\\w+") && c.length() > 1).collect(Collectors.toList());
|
||||
Set<String> notDelimiters = new HashSet<>();
|
||||
values.forEach(v -> v.substring(0, v.length() - 1).chars()
|
||||
.filter(c -> !Character.isDigit(c) && !Character.isAlphabetic(c))
|
||||
.forEach(c -> notDelimiters.add(Character.toString((char)c))));
|
||||
int width = size.getColumns();
|
||||
int promptLength = prompt != null ? prompt.length() : 0;
|
||||
if (promptLength > 0) {
|
||||
TerminalLine tp = new TerminalLine(prompt.toString(), 0, width);
|
||||
promptLength = tp.getEndLine().length();
|
||||
}
|
||||
TerminalLine tl = new TerminalLine(buf.substring(0, buf.cursor()), promptLength, width);
|
||||
int out = tl.getStartPos();
|
||||
String buffer = tl.getEndLine();
|
||||
for (int i = buffer.length(); i > 0; i--) {
|
||||
if (buffer.substring(0, i).matches(".*\\W")
|
||||
&& !notDelimiters.contains(buffer.substring(i - 1, i))) {
|
||||
out += i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected PostResult toColumns(List<Object> items, Candidate selection, String completed, Function<String, Integer> wcwidth, int width, boolean rowsFirst) {
|
||||
@ -5163,6 +5183,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
// TODO: support Option.LIST_PACKED
|
||||
// Compute column width
|
||||
int maxWidth = 0;
|
||||
int listSize = 0;
|
||||
for (Object item : items) {
|
||||
if (item instanceof String) {
|
||||
int len = wcwidth.apply((String) item);
|
||||
@ -5170,6 +5191,7 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
else if (item instanceof List) {
|
||||
for (Candidate cand : (List<Candidate>) item) {
|
||||
listSize++;
|
||||
int len = wcwidth.apply(cand.displ());
|
||||
if (cand.descr() != null) {
|
||||
len += MARGIN_BETWEEN_DISPLAY_AND_DESC;
|
||||
@ -5183,8 +5205,33 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
// Build columns
|
||||
AttributedStringBuilder sb = new AttributedStringBuilder();
|
||||
for (Object list : items) {
|
||||
toColumns(list, width, maxWidth, sb, selection, completed, rowsFirst, out);
|
||||
if (listSize > 0) {
|
||||
if (isSet(Option.AUTO_MENU_LIST)
|
||||
&& listSize < Math.min(getInt(MENU_LIST_MAX, DEFAULT_MENU_LIST_MAX), displayRows() - promptLines())) {
|
||||
maxWidth = Math.max(maxWidth, MENU_LIST_WIDTH);
|
||||
sb.tabs(Math.max(Math.min(candidateStartPosition, width - maxWidth - 1), 1));
|
||||
width = maxWidth + 2;
|
||||
if (!isSet(Option.GROUP_PERSIST)) {
|
||||
List<Candidate> list = new ArrayList<>();
|
||||
for (Object o : items) {
|
||||
if (o instanceof Collection) {
|
||||
list.addAll((Collection<Candidate>) o);
|
||||
}
|
||||
}
|
||||
list = list.stream()
|
||||
.sorted(getCandidateComparator(isSet(Option.CASE_INSENSITIVE), ""))
|
||||
.collect(Collectors.toList());
|
||||
toColumns(list, width, maxWidth, sb, selection, completed, rowsFirst, true, out);
|
||||
} else {
|
||||
for (Object list : items) {
|
||||
toColumns(list, width, maxWidth, sb, selection, completed, rowsFirst, true, out);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Object list : items) {
|
||||
toColumns(list, width, maxWidth, sb, selection, completed, rowsFirst, false, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '\n') {
|
||||
sb.setLength(sb.length() - 1);
|
||||
@ -5193,16 +5240,29 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed, boolean rowsFirst, int[] out) {
|
||||
protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed
|
||||
, boolean rowsFirst, boolean doMenuList, int[] out) {
|
||||
if (maxWidth <= 0 || width <= 0) {
|
||||
return;
|
||||
}
|
||||
// This is a group
|
||||
if (items instanceof String) {
|
||||
sb.style(getCompletionStyleGroup())
|
||||
if (doMenuList) {
|
||||
sb.style(AttributedStyle.DEFAULT);
|
||||
sb.append('\t');
|
||||
}
|
||||
AttributedStringBuilder asb = new AttributedStringBuilder();
|
||||
asb.style(getCompletionStyleGroup(doMenuList))
|
||||
.append((String) items)
|
||||
.style(AttributedStyle.DEFAULT)
|
||||
.append("\n");
|
||||
.style(AttributedStyle.DEFAULT);
|
||||
if (doMenuList) {
|
||||
for (int k = ((String) items).length(); k < maxWidth + 1; k++) {
|
||||
asb.append(' ');
|
||||
}
|
||||
}
|
||||
sb.style(getCompletionStyleBackground(doMenuList));
|
||||
sb.append(asb);
|
||||
sb.append("\n");
|
||||
out[0]++;
|
||||
}
|
||||
// This is a Candidate list
|
||||
@ -5224,6 +5284,11 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
index = (i, j) -> j * lines + i;
|
||||
}
|
||||
for (int i = 0; i < lines; i++) {
|
||||
if (doMenuList) {
|
||||
sb.style(AttributedStyle.DEFAULT);
|
||||
sb.append('\t');
|
||||
}
|
||||
AttributedStringBuilder asb = new AttributedStringBuilder();
|
||||
for (int j = 0; j < columns; j++) {
|
||||
int idx = index.applyAsInt(i, j);
|
||||
if (idx < candidates.size()) {
|
||||
@ -5248,56 +5313,85 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
}
|
||||
if (cand == selection) {
|
||||
out[1] = i;
|
||||
sb.style(getCompletionStyleSelection());
|
||||
asb.style(getCompletionStyleSelection(doMenuList));
|
||||
if (left.toString().regionMatches(
|
||||
isSet(Option.CASE_INSENSITIVE), 0, completed, 0, completed.length())) {
|
||||
sb.append(left.toString(), 0, completed.length());
|
||||
sb.append(left.toString(), completed.length(), left.length());
|
||||
asb.append(left.toString(), 0, completed.length());
|
||||
asb.append(left.toString(), completed.length(), left.length());
|
||||
} else {
|
||||
sb.append(left.toString());
|
||||
asb.append(left.toString());
|
||||
}
|
||||
for (int k = 0; k < maxWidth - lw - rw; k++) {
|
||||
sb.append(' ');
|
||||
asb.append(' ');
|
||||
}
|
||||
if (right != null) {
|
||||
sb.append(right);
|
||||
asb.append(right);
|
||||
}
|
||||
sb.style(AttributedStyle.DEFAULT);
|
||||
asb.style(AttributedStyle.DEFAULT);
|
||||
} else {
|
||||
if (left.toString().regionMatches(
|
||||
isSet(Option.CASE_INSENSITIVE), 0, completed, 0, completed.length())) {
|
||||
sb.style(getCompletionStyleStarting());
|
||||
sb.append(left, 0, completed.length());
|
||||
sb.style(AttributedStyle.DEFAULT);
|
||||
sb.append(left, completed.length(), left.length());
|
||||
asb.style(getCompletionStyleStarting(doMenuList));
|
||||
asb.append(left, 0, completed.length());
|
||||
asb.style(AttributedStyle.DEFAULT);
|
||||
asb.append(left, completed.length(), left.length());
|
||||
} else {
|
||||
sb.append(left);
|
||||
asb.append(left);
|
||||
}
|
||||
if (right != null || hasRightItem) {
|
||||
for (int k = 0; k < maxWidth - lw - rw; k++) {
|
||||
sb.append(' ');
|
||||
asb.append(' ');
|
||||
}
|
||||
}
|
||||
if (right != null) {
|
||||
sb.style(getCompletionStyleDescription());
|
||||
sb.append(right);
|
||||
sb.style(AttributedStyle.DEFAULT);
|
||||
asb.style(getCompletionStyleDescription(doMenuList));
|
||||
asb.append(right);
|
||||
asb.style(AttributedStyle.DEFAULT);
|
||||
} else if (doMenuList) {
|
||||
for (int k = lw; k < maxWidth; k++) {
|
||||
asb.append(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasRightItem) {
|
||||
for (int k = 0; k < MARGIN_BETWEEN_COLUMNS; k++) {
|
||||
sb.append(' ');
|
||||
asb.append(' ');
|
||||
}
|
||||
}
|
||||
if (doMenuList) {
|
||||
asb.append(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.style(getCompletionStyleBackground(doMenuList));
|
||||
sb.append(asb);
|
||||
sb.append('\n');
|
||||
}
|
||||
out[0] += lines;
|
||||
}
|
||||
}
|
||||
|
||||
private AttributedStyle getCompletionStyleStarting() {
|
||||
protected AttributedStyle getCompletionStyleStarting(boolean menuList) {
|
||||
return menuList ? getCompletionStyleListStarting() : getCompletionStyleStarting();
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleDescription(boolean menuList) {
|
||||
return menuList ? getCompletionStyleListDescription() : getCompletionStyleDescription();
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleGroup(boolean menuList) {
|
||||
return menuList ? getCompletionStyleListGroup() : getCompletionStyleGroup();
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleSelection(boolean menuList) {
|
||||
return menuList ? getCompletionStyleListSelection() : getCompletionStyleSelection();
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleBackground(boolean menuList) {
|
||||
return menuList ? getCompletionStyleListBackground() : getCompletionStyleBackground();
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleStarting() {
|
||||
return getCompletionStyle(COMPLETION_STYLE_STARTING, DEFAULT_COMPLETION_STYLE_STARTING);
|
||||
}
|
||||
|
||||
@ -5313,37 +5407,38 @@ public class LineReaderImpl implements LineReader, Flushable
|
||||
return getCompletionStyle(COMPLETION_STYLE_SELECTION, DEFAULT_COMPLETION_STYLE_SELECTION);
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleBackground() {
|
||||
return getCompletionStyle(COMPLETION_STYLE_BACKGROUND, DEFAULT_COMPLETION_STYLE_BACKGROUND);
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleListStarting() {
|
||||
return getCompletionStyle(COMPLETION_STYLE_LIST_STARTING, DEFAULT_COMPLETION_STYLE_LIST_STARTING);
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleListDescription() {
|
||||
return getCompletionStyle(COMPLETION_STYLE_LIST_DESCRIPTION, DEFAULT_COMPLETION_STYLE_LIST_DESCRIPTION);
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleListGroup() {
|
||||
return getCompletionStyle(COMPLETION_STYLE_LIST_GROUP, DEFAULT_COMPLETION_STYLE_LIST_GROUP);
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleListSelection() {
|
||||
return getCompletionStyle(COMPLETION_STYLE_LIST_SELECTION, DEFAULT_COMPLETION_STYLE_LIST_SELECTION);
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyleListBackground() {
|
||||
return getCompletionStyle(COMPLETION_STYLE_LIST_BACKGROUND, DEFAULT_COMPLETION_STYLE_LIST_BACKGROUND);
|
||||
}
|
||||
|
||||
protected AttributedStyle getCompletionStyle(String name, String value) {
|
||||
return buildStyle(getString(name, value));
|
||||
return new StyleResolver(s -> getString(s, null)).resolve("." + name, value);
|
||||
}
|
||||
|
||||
protected AttributedStyle buildStyle(String str) {
|
||||
return AttributedString.fromAnsi("\u001b[" + str + "m ").styleAt(0);
|
||||
}
|
||||
|
||||
private String getCommonStart(String str1, String str2, boolean caseInsensitive) {
|
||||
int[] s1 = str1.codePoints().toArray();
|
||||
int[] s2 = str2.codePoints().toArray();
|
||||
int len = 0;
|
||||
while (len < Math.min(s1.length, s2.length)) {
|
||||
int ch1 = s1[len];
|
||||
int ch2 = s2[len];
|
||||
if (ch1 != ch2 && caseInsensitive) {
|
||||
ch1 = Character.toUpperCase(ch1);
|
||||
ch2 = Character.toUpperCase(ch2);
|
||||
if (ch1 != ch2) {
|
||||
ch1 = Character.toLowerCase(ch1);
|
||||
ch2 = Character.toLowerCase(ch2);
|
||||
}
|
||||
}
|
||||
if (ch1 != ch2) {
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
return new String(s1, 0, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in "vi" mode for argumented history move, to move a specific
|
||||
* number of history entries forward or back.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2016, 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.
|
||||
@ -9,6 +9,7 @@
|
||||
package jdk.internal.org.jline.reader.impl;
|
||||
|
||||
import jdk.internal.org.jline.reader.LineReader;
|
||||
import jdk.internal.org.jline.utils.Levenshtein;
|
||||
|
||||
public class ReaderUtils {
|
||||
|
||||
@ -67,4 +68,14 @@ public class ReaderUtils {
|
||||
return nb;
|
||||
}
|
||||
|
||||
public static int distance(String word, String cand) {
|
||||
if (word.length() < cand.length()) {
|
||||
int d1 = Levenshtein.distance(word, cand.substring(0, Math.min(cand.length(), word.length())));
|
||||
int d2 = Levenshtein.distance(word, cand);
|
||||
return Math.min(d1, d2);
|
||||
} else {
|
||||
return Levenshtein.distance(word, cand);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ package jdk.internal.org.jline.reader.impl.completer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ -28,10 +29,11 @@ import jdk.internal.org.jline.utils.AttributedString;
|
||||
*/
|
||||
public class StringsCompleter implements Completer
|
||||
{
|
||||
protected Collection<Candidate> candidates = new ArrayList<>();
|
||||
protected Collection<Candidate> candidates;
|
||||
protected Supplier<Collection<String>> stringsSupplier;
|
||||
|
||||
public StringsCompleter() {
|
||||
this(Collections.<Candidate>emptyList());
|
||||
}
|
||||
|
||||
public StringsCompleter(Supplier<Collection<String>> stringsSupplier) {
|
||||
@ -46,6 +48,7 @@ public class StringsCompleter implements Completer
|
||||
|
||||
public StringsCompleter(Iterable<String> strings) {
|
||||
assert strings != null;
|
||||
this.candidates = new ArrayList<>();
|
||||
for (String string : strings) {
|
||||
candidates.add(new Candidate(AttributedString.stripAnsi(string), string, null, null, null, null, true));
|
||||
}
|
||||
@ -57,9 +60,10 @@ public class StringsCompleter implements Completer
|
||||
|
||||
public StringsCompleter(Collection<Candidate> candidates) {
|
||||
assert candidates != null;
|
||||
this.candidates.addAll(candidates);
|
||||
this.candidates = new ArrayList<>(candidates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(LineReader reader, final ParsedLine commandLine, final List<Candidate> candidates) {
|
||||
assert commandLine != null;
|
||||
assert candidates != null;
|
||||
@ -72,4 +76,9 @@ public class StringsCompleter implements Completer
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String value = candidates != null ? candidates.toString() : "{" + stringsSupplier.toString() + "}";
|
||||
return "StringsCompleter" + value;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.reader.impl.completer;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import jdk.internal.org.jline.reader.Candidate;
|
||||
import jdk.internal.org.jline.reader.Completer;
|
||||
import jdk.internal.org.jline.reader.LineReader;
|
||||
import jdk.internal.org.jline.reader.ParsedLine;
|
||||
import jdk.internal.org.jline.utils.AttributedString;
|
||||
|
||||
/**
|
||||
* Completer which contains multiple completers and aggregates them together.
|
||||
*
|
||||
* @author <a href="mailto:matti.rintanikkola@gmail.com">Matti Rinta-Nikkola</a>
|
||||
*/
|
||||
public class SystemCompleter implements Completer {
|
||||
private Map<String,List<Completer>> completers = new HashMap<>();
|
||||
private Map<String,String> aliasCommand = new HashMap<>();
|
||||
private StringsCompleter commands;
|
||||
private boolean compiled = false;
|
||||
|
||||
public SystemCompleter() {}
|
||||
|
||||
@Override
|
||||
public void complete(LineReader reader, ParsedLine commandLine, List<Candidate> candidates) {
|
||||
if (!compiled) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
assert commandLine != null;
|
||||
assert candidates != null;
|
||||
if (commandLine.words().size() > 0) {
|
||||
if (commandLine.words().size() == 1) {
|
||||
String buffer = commandLine.words().get(0);
|
||||
int eq = buffer.indexOf('=');
|
||||
if (eq < 0) {
|
||||
commands.complete(reader, commandLine, candidates);
|
||||
} else if (reader.getParser().validVariableName(buffer.substring(0, eq))) {
|
||||
String curBuf = buffer.substring(0, eq + 1);
|
||||
for (String c: completers.keySet()) {
|
||||
candidates.add(new Candidate(AttributedString.stripAnsi(curBuf+c)
|
||||
, c, null, null, null, null, true));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String cmd = reader.getParser().getCommand(commandLine.words().get(0));
|
||||
if (command(cmd) != null) {
|
||||
completers.get(command(cmd)).get(0).complete(reader, commandLine, candidates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCompiled() {
|
||||
return compiled;
|
||||
}
|
||||
|
||||
private String command(String cmd) {
|
||||
String out = null;
|
||||
if (cmd != null) {
|
||||
if (completers.containsKey(cmd)) {
|
||||
out = cmd;
|
||||
} else if (aliasCommand.containsKey(cmd)) {
|
||||
out = aliasCommand.get(cmd);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public void add(String command, List<Completer> completers) {
|
||||
for (Completer c : completers) {
|
||||
add(command, c);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(List<String> commands, Completer completer) {
|
||||
for (String c: commands) {
|
||||
add(c, completer);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(String command, Completer completer) {
|
||||
Objects.requireNonNull(command);
|
||||
if (compiled) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!completers.containsKey(command)) {
|
||||
completers.put(command, new ArrayList<Completer>());
|
||||
}
|
||||
if (completer instanceof ArgumentCompleter) {
|
||||
((ArgumentCompleter) completer).setStrictCommand(false);
|
||||
}
|
||||
completers.get(command).add(completer);
|
||||
}
|
||||
|
||||
public void add(SystemCompleter other) {
|
||||
if (other.isCompiled()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
for (Map.Entry<String, List<Completer>> entry: other.getCompleters().entrySet()) {
|
||||
for (Completer c: entry.getValue()) {
|
||||
add(entry.getKey(), c);
|
||||
}
|
||||
}
|
||||
addAliases(other.getAliases());
|
||||
}
|
||||
|
||||
public void addAliases(Map<String,String> aliasCommand) {
|
||||
if (compiled) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
this.aliasCommand.putAll(aliasCommand);
|
||||
}
|
||||
|
||||
private Map<String,String> getAliases() {
|
||||
return aliasCommand;
|
||||
}
|
||||
|
||||
public void compile() {
|
||||
if (compiled) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<Completer>> compiledCompleters = new HashMap<>();
|
||||
for (Map.Entry<String, List<Completer>> entry: completers.entrySet()) {
|
||||
if (entry.getValue().size() == 1) {
|
||||
compiledCompleters.put(entry.getKey(), entry.getValue());
|
||||
} else {
|
||||
compiledCompleters.put(entry.getKey(), new ArrayList<Completer>());
|
||||
compiledCompleters.get(entry.getKey()).add(new AggregateCompleter(entry.getValue()));
|
||||
}
|
||||
}
|
||||
completers = compiledCompleters;
|
||||
Set<String> cmds = new HashSet<>(completers.keySet());
|
||||
cmds.addAll(aliasCommand.keySet());
|
||||
commands = new StringsCompleter(cmds);
|
||||
compiled = true;
|
||||
}
|
||||
|
||||
public Map<String,List<Completer>> getCompleters() {
|
||||
return completers;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2018, 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.
|
||||
@ -84,7 +84,7 @@ public class DefaultHistory implements History {
|
||||
try (BufferedReader reader = Files.newBufferedReader(path)) {
|
||||
internalClear();
|
||||
reader.lines().forEach(line -> addHistoryLine(path, line));
|
||||
setHistoryFileData(path, new HistoryFileData(items.size(), items.size()));
|
||||
setHistoryFileData(path, new HistoryFileData(items.size(), offset + items.size()));
|
||||
maybeResize();
|
||||
}
|
||||
}
|
||||
@ -105,7 +105,7 @@ public class DefaultHistory implements History {
|
||||
Log.trace("Reading history from: ", path);
|
||||
try (BufferedReader reader = Files.newBufferedReader(path)) {
|
||||
reader.lines().forEach(line -> addHistoryLine(path, line, incremental));
|
||||
setHistoryFileData(path, new HistoryFileData(items.size(), items.size()));
|
||||
setHistoryFileData(path, new HistoryFileData(items.size(), offset + items.size()));
|
||||
maybeResize();
|
||||
}
|
||||
}
|
||||
@ -136,11 +136,7 @@ public class DefaultHistory implements History {
|
||||
private boolean isLineReaderHistory (Path path) throws IOException {
|
||||
Path lrp = getPath();
|
||||
if (lrp == null) {
|
||||
if (path != null) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return path == null;
|
||||
}
|
||||
return Files.isSameFile(lrp, path);
|
||||
}
|
||||
@ -226,7 +222,10 @@ public class DefaultHistory implements History {
|
||||
private void internalWrite(Path path, int from) throws IOException {
|
||||
if (path != null) {
|
||||
Log.trace("Saving history to: ", path);
|
||||
Files.createDirectories(path.toAbsolutePath().getParent());
|
||||
Path parent = path.toAbsolutePath().getParent();
|
||||
if (!Files.exists(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
// Append new items to the history file
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(path.toAbsolutePath(),
|
||||
StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) {
|
||||
@ -258,11 +257,11 @@ public class DefaultHistory implements History {
|
||||
});
|
||||
}
|
||||
// Remove duplicates
|
||||
doTrimHistory(allItems, max);
|
||||
List<Entry> trimmedItems = doTrimHistory(allItems, max);
|
||||
// Write history
|
||||
Path temp = Files.createTempFile(path.toAbsolutePath().getParent(), path.getFileName().toString(), ".tmp");
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(temp, StandardOpenOption.WRITE)) {
|
||||
for (Entry entry : allItems) {
|
||||
for (Entry entry : trimmedItems) {
|
||||
writer.append(format(entry));
|
||||
}
|
||||
}
|
||||
@ -270,8 +269,8 @@ public class DefaultHistory implements History {
|
||||
// Keep items in memory
|
||||
if (isLineReaderHistory(path)) {
|
||||
internalClear();
|
||||
offset = allItems.get(0).index();
|
||||
items.addAll(allItems);
|
||||
offset = trimmedItems.get(0).index();
|
||||
items.addAll(trimmedItems);
|
||||
setHistoryFileData(path, new HistoryFileData(items.size(), items.size()));
|
||||
} else {
|
||||
setEntriesInFile(path, allItems.size());
|
||||
@ -297,7 +296,7 @@ public class DefaultHistory implements History {
|
||||
items.clear();
|
||||
}
|
||||
|
||||
static void doTrimHistory(List<Entry> allItems, int max) {
|
||||
static List<Entry> doTrimHistory(List<Entry> allItems, int max) {
|
||||
int idx = 0;
|
||||
while (idx < allItems.size()) {
|
||||
int ridx = allItems.size() - idx - 1;
|
||||
@ -314,6 +313,12 @@ public class DefaultHistory implements History {
|
||||
while (allItems.size() > max) {
|
||||
allItems.remove(0);
|
||||
}
|
||||
int index = allItems.get(allItems.size() - 1).index() - allItems.size() + 1;
|
||||
List<Entry> out = new ArrayList<>();
|
||||
for (Entry e : allItems) {
|
||||
out.add(new EntryImpl(index++, e.time(), e.line()));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
@ -338,7 +343,7 @@ public class DefaultHistory implements History {
|
||||
|
||||
private String format(Entry entry) {
|
||||
if (reader.isSet(LineReader.Option.HISTORY_TIMESTAMPED)) {
|
||||
return Long.toString(entry.time().toEpochMilli()) + ":" + escape(entry.line()) + "\n";
|
||||
return entry.time().toEpochMilli() + ":" + escape(entry.line()) + "\n";
|
||||
}
|
||||
return escape(entry.line()) + "\n";
|
||||
}
|
||||
@ -398,6 +403,8 @@ public class DefaultHistory implements History {
|
||||
sb.append('|');
|
||||
} else if (ch == '*') {
|
||||
sb.append('.').append('*');
|
||||
} else {
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
return line.matches(sb.toString());
|
||||
@ -441,7 +448,7 @@ public class DefaultHistory implements History {
|
||||
}
|
||||
|
||||
public void resetIndex() {
|
||||
index = index > items.size() ? items.size() : index;
|
||||
index = Math.min(index, items.size());
|
||||
}
|
||||
|
||||
protected static class EntryImpl implements Entry {
|
||||
@ -622,7 +629,7 @@ public class DefaultHistory implements History {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private class HistoryFileData {
|
||||
private static class HistoryFileData {
|
||||
private int lastLoaded = 0;
|
||||
private int entriesInFile = 0;
|
||||
|
||||
|
@ -18,6 +18,7 @@ import java.util.function.IntConsumer;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import jdk.internal.org.jline.terminal.impl.NativeSignalHandler;
|
||||
import jdk.internal.org.jline.utils.ColorPalette;
|
||||
import jdk.internal.org.jline.utils.InfoCmp.Capability;
|
||||
import jdk.internal.org.jline.utils.NonBlockingReader;
|
||||
|
||||
@ -328,4 +329,10 @@ public interface Terminal extends Closeable, Flushable {
|
||||
* @return <code>true</code> if focus tracking is supported
|
||||
*/
|
||||
boolean trackFocus(boolean tracking);
|
||||
|
||||
/**
|
||||
* Color support
|
||||
*/
|
||||
ColorPalette getPalette();
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
@ -88,6 +88,7 @@ public final class TerminalBuilder {
|
||||
}
|
||||
|
||||
private static final AtomicReference<Terminal> SYSTEM_TERMINAL = new AtomicReference<>();
|
||||
private static final AtomicReference<Terminal> TERMINAL_OVERRIDE = new AtomicReference<>();
|
||||
|
||||
private String name;
|
||||
private InputStream in;
|
||||
@ -100,12 +101,13 @@ public final class TerminalBuilder {
|
||||
private Boolean jansi;
|
||||
private Boolean exec;
|
||||
private Boolean dumb;
|
||||
private Boolean color;
|
||||
private Attributes attributes;
|
||||
private Size size;
|
||||
private boolean nativeSignals = false;
|
||||
private Function<InputStream, InputStream> inputStreamWrapper = in -> in;
|
||||
private Terminal.SignalHandler signalHandler = Terminal.SignalHandler.SIG_DFL;
|
||||
private boolean paused = false;
|
||||
private Function<InputStream, InputStream> inputStreamWrapper = in -> in;
|
||||
|
||||
private TerminalBuilder() {
|
||||
}
|
||||
@ -151,6 +153,11 @@ public final class TerminalBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TerminalBuilder color(boolean color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encoding to use for reading/writing from the console.
|
||||
* If {@code null} (the default value), JLine will automatically select
|
||||
@ -205,8 +212,8 @@ public final class TerminalBuilder {
|
||||
/**
|
||||
* Attributes to use when creating a non system terminal,
|
||||
* i.e. when the builder has been given the input and
|
||||
* outut streams using the {@link #streams(InputStream, OutputStream)} method
|
||||
* or when {@link #system(boolean)} has been explicitely called with
|
||||
* output streams using the {@link #streams(InputStream, OutputStream)} method
|
||||
* or when {@link #system(boolean)} has been explicitly called with
|
||||
* <code>false</code>.
|
||||
*
|
||||
* @param attributes the attributes to use
|
||||
@ -222,8 +229,8 @@ public final class TerminalBuilder {
|
||||
/**
|
||||
* Initial size to use when creating a non system terminal,
|
||||
* i.e. when the builder has been given the input and
|
||||
* outut streams using the {@link #streams(InputStream, OutputStream)} method
|
||||
* or when {@link #system(boolean)} has been explicitely called with
|
||||
* output streams using the {@link #streams(InputStream, OutputStream)} method
|
||||
* or when {@link #system(boolean)} has been explicitly called with
|
||||
* <code>false</code>.
|
||||
*
|
||||
* @param size the initial size
|
||||
@ -246,6 +253,11 @@ public final class TerminalBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TerminalBuilder inputStreamWrapper(Function<InputStream, InputStream> wrapper) {
|
||||
this.inputStreamWrapper = wrapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial paused state of the terminal (defaults to false).
|
||||
* By default, the terminal is started, but in some cases,
|
||||
@ -261,13 +273,12 @@ public final class TerminalBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TerminalBuilder inputStreamWrapper(Function<InputStream, InputStream> wrapper) {
|
||||
this.inputStreamWrapper = wrapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Terminal build() throws IOException {
|
||||
Terminal terminal = doBuild();
|
||||
Terminal override = TERMINAL_OVERRIDE.get();
|
||||
Terminal terminal = override != null ? override : doBuild();
|
||||
if (override != null) {
|
||||
Log.debug(() -> "Overriding terminal with global value set by TerminalBuilder.setTerminalOverride");
|
||||
}
|
||||
Log.debug(() -> "Using terminal " + terminal.getClass().getSimpleName());
|
||||
if (terminal instanceof AbstractPosixTerminal) {
|
||||
Log.debug(() -> "Using pty " + ((AbstractPosixTerminal) terminal).getPty().getClass().getSimpleName());
|
||||
@ -318,117 +329,132 @@ public final class TerminalBuilder {
|
||||
dumb = getBoolean(PROP_DUMB, null);
|
||||
}
|
||||
if ((system != null && system) || (system == null && in == null && out == null)) {
|
||||
if (attributes != null || size != null) {
|
||||
Log.warn("Attributes and size fields are ignored when creating a system terminal");
|
||||
if (system != null && ((in != null && !in.equals(System.in)) || (out != null && !out.equals(System.out)))) {
|
||||
throw new IllegalArgumentException("Cannot create a system terminal using non System streams");
|
||||
}
|
||||
IllegalStateException exception = new IllegalStateException("Unable to create a system terminal");
|
||||
Terminal terminal = null;
|
||||
if (OSUtils.IS_WINDOWS) {
|
||||
boolean cygwinTerm = "cygwin".equals(System.getenv("TERM"));
|
||||
boolean ansiPassThrough = OSUtils.IS_CONEMU;
|
||||
//
|
||||
// Cygwin support
|
||||
//
|
||||
if ((OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) && exec && !cygwinTerm) {
|
||||
try {
|
||||
Pty pty = ExecPty.current();
|
||||
// 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";
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
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 (jna) {
|
||||
try {
|
||||
terminal = load(JnaSupport.class).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 (jansi) {
|
||||
try {
|
||||
terminal = load(JansiSupport.class).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 (jna) {
|
||||
try {
|
||||
Pty pty = load(JnaSupport.class).current();
|
||||
terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler);
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
Log.debug("Error creating JNA based terminal: ", t.getMessage(), t);
|
||||
exception.addSuppressed(t);
|
||||
}
|
||||
}
|
||||
if (jansi) {
|
||||
try {
|
||||
Pty pty = load(JansiSupport.class).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 (exec) {
|
||||
try {
|
||||
Pty pty = ExecPty.current();
|
||||
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);
|
||||
exception.addSuppressed(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (terminal instanceof AbstractTerminal) {
|
||||
AbstractTerminal t = (AbstractTerminal) terminal;
|
||||
if (SYSTEM_TERMINAL.compareAndSet(null, t)) {
|
||||
t.setOnClose(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SYSTEM_TERMINAL.compareAndSet(t, null);
|
||||
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
|
||||
//
|
||||
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 {
|
||||
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 (tbs.hasJnaSupport()) {
|
||||
try {
|
||||
Pty pty = tbs.getJnaSupport().current();
|
||||
terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler);
|
||||
} 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);
|
||||
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 && (dumb == null || dumb)) {
|
||||
// forced colored dumb terminal
|
||||
boolean color = getBoolean(PROP_DUMB_COLOR, false);
|
||||
// detect emacs using the env variable
|
||||
if (!color) {
|
||||
color = System.getenv("INSIDE_EMACS") != null;
|
||||
}
|
||||
// detect Intellij Idea
|
||||
if (!color) {
|
||||
String command = getParentProcessCommand();
|
||||
color = command != null && command.contains("idea");
|
||||
}
|
||||
if (!color && dumb == null) {
|
||||
if (Log.isDebugEnabled()) {
|
||||
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)");
|
||||
Boolean color = this.color;
|
||||
if (color == null) {
|
||||
color = getBoolean(PROP_DUMB_COLOR, false);
|
||||
// detect emacs using the env variable
|
||||
if (!color) {
|
||||
color = System.getenv("INSIDE_EMACS") != null;
|
||||
}
|
||||
// detect Intellij Idea
|
||||
if (!color) {
|
||||
String command = getParentProcessCommand();
|
||||
color = command != null && command.contains("idea");
|
||||
}
|
||||
if (!color) {
|
||||
color = tbs.isConsoleOutput() && 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("Creating a dumb terminal", exception);
|
||||
} else {
|
||||
Log.warn("Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)");
|
||||
}
|
||||
}
|
||||
}
|
||||
terminal = new DumbTerminal(name, color ? Terminal.TYPE_DUMB_COLOR : Terminal.TYPE_DUMB,
|
||||
new FileInputStream(FileDescriptor.in),
|
||||
inputStreamWrapper.apply(new FileInputStream(FileDescriptor.in)),
|
||||
new FileOutputStream(FileDescriptor.out),
|
||||
encoding, signalHandler);
|
||||
}
|
||||
@ -440,7 +466,7 @@ public final class TerminalBuilder {
|
||||
if (jna) {
|
||||
try {
|
||||
Pty pty = load(JnaSupport.class).open(attributes, size);
|
||||
return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler, paused);
|
||||
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);
|
||||
}
|
||||
@ -448,12 +474,12 @@ public final class TerminalBuilder {
|
||||
if (jansi) {
|
||||
try {
|
||||
Pty pty = load(JansiSupport.class).open(attributes, size);
|
||||
return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler, paused);
|
||||
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, in, out, encoding, signalHandler, paused, attributes, size);
|
||||
return new ExternalTerminal(name, type, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused, attributes, size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,7 +508,116 @@ public final class TerminalBuilder {
|
||||
return def;
|
||||
}
|
||||
|
||||
private <S> S load(Class<S> clazz) {
|
||||
private static <S> S load(Class<S> clazz) {
|
||||
return ServiceLoader.load(clazz, clazz.getClassLoader()).iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows an application to override the result of {@link #build()}. The
|
||||
* intended use case is to allow a container or server application to control
|
||||
* an embedded application that uses a LineReader that uses Terminal
|
||||
* constructed with TerminalBuilder.build but provides no public api for setting
|
||||
* the <code>LineReader</code> of the {@link Terminal}. For example, the sbt
|
||||
* 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 the REPL to
|
||||
* share the same {@link Terminal} instance used by sbt.
|
||||
*
|
||||
* <p>
|
||||
* When this method is called with a non-null {@link Terminal}, all subsequent
|
||||
* calls to {@link #build()} will return the provided {@link Terminal} regardless
|
||||
* of how the {@link TerminalBuilder} was constructed. The default behavior
|
||||
* of {@link TerminalBuilder} can be restored by calling setTerminalOverride
|
||||
* with a null {@link Terminal}
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Usage of setTerminalOverride should be restricted to cases where it
|
||||
* isn't possible to update the api of the nested application to accept
|
||||
* a {@link Terminal instance}.
|
||||
* </p>
|
||||
*
|
||||
* @param terminal the {@link Terminal} to globally override
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setTerminalOverride(final Terminal terminal) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2018, 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.
|
||||
@ -17,6 +17,7 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
@ -27,6 +28,7 @@ import jdk.internal.org.jline.terminal.Attributes.LocalFlag;
|
||||
import jdk.internal.org.jline.terminal.Cursor;
|
||||
import jdk.internal.org.jline.terminal.MouseEvent;
|
||||
import jdk.internal.org.jline.terminal.Terminal;
|
||||
import jdk.internal.org.jline.utils.ColorPalette;
|
||||
import jdk.internal.org.jline.utils.Curses;
|
||||
import jdk.internal.org.jline.utils.InfoCmp;
|
||||
import jdk.internal.org.jline.utils.InfoCmp.Capability;
|
||||
@ -38,10 +40,11 @@ public abstract class AbstractTerminal implements Terminal {
|
||||
protected final String name;
|
||||
protected final String type;
|
||||
protected final Charset encoding;
|
||||
protected final Map<Signal, SignalHandler> handlers = new HashMap<>();
|
||||
protected final Map<Signal, SignalHandler> handlers = new ConcurrentHashMap<>();
|
||||
protected final Set<Capability> bools = new HashSet<>();
|
||||
protected final Map<Capability, Integer> ints = new HashMap<>();
|
||||
protected final Map<Capability, String> strings = new HashMap<>();
|
||||
protected final ColorPalette palette = new ColorPalette(this);
|
||||
protected Status status;
|
||||
protected Runnable onClose;
|
||||
|
||||
@ -51,7 +54,7 @@ public abstract class AbstractTerminal implements Terminal {
|
||||
|
||||
public AbstractTerminal(String name, String type, Charset encoding, SignalHandler signalHandler) throws IOException {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.type = type != null ? type : "ansi";
|
||||
this.encoding = encoding != null ? encoding : Charset.defaultCharset();
|
||||
for (Signal signal : Signal.values()) {
|
||||
handlers.put(signal, signalHandler);
|
||||
@ -197,12 +200,10 @@ public abstract class AbstractTerminal implements Terminal {
|
||||
|
||||
protected void parseInfoCmp() {
|
||||
String capabilities = null;
|
||||
if (type != null) {
|
||||
try {
|
||||
capabilities = InfoCmp.getInfoCmp(type);
|
||||
} catch (Exception e) {
|
||||
Log.warn("Unable to retrieve infocmp for type " + type, e);
|
||||
}
|
||||
try {
|
||||
capabilities = InfoCmp.getInfoCmp(type);
|
||||
} catch (Exception e) {
|
||||
Log.warn("Unable to retrieve infocmp for type " + type, e);
|
||||
}
|
||||
if (capabilities == null) {
|
||||
capabilities = InfoCmp.getLoadedInfoCmp("ansi");
|
||||
@ -241,7 +242,7 @@ public abstract class AbstractTerminal implements Terminal {
|
||||
|
||||
@Override
|
||||
public boolean hasFocusSupport() {
|
||||
return type != null && type.startsWith("xterm");
|
||||
return type.startsWith("xterm");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -283,4 +284,8 @@ public abstract class AbstractTerminal implements Terminal {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorPalette getPalette() {
|
||||
return palette;
|
||||
}
|
||||
}
|
||||
|
@ -521,8 +521,6 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract int getConsoleOutputCP();
|
||||
|
||||
protected abstract int getConsoleMode();
|
||||
|
||||
protected abstract void setConsoleMode(int mode);
|
||||
|
@ -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.
|
||||
@ -25,4 +25,9 @@ public interface JansiSupport {
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -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.
|
||||
@ -29,4 +29,9 @@ public interface JnaSupport {
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -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.
|
||||
@ -20,12 +20,16 @@ import static jdk.internal.org.jline.utils.AttributedStyle.BG_COLOR_EXP;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.FG_COLOR;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.FG_COLOR_EXP;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_BACKGROUND;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_BACKGROUND_IND;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_BACKGROUND_RGB;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_BLINK;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_BOLD;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_CONCEAL;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_CROSSED_OUT;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_FAINT;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND_IND;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND_RGB;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_INVERSE;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_ITALIC;
|
||||
import static jdk.internal.org.jline.utils.AttributedStyle.F_UNDERLINE;
|
||||
@ -35,6 +39,15 @@ import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_DISABLE_ALTER
|
||||
|
||||
public abstract class AttributedCharSequence implements CharSequence {
|
||||
|
||||
public static final int TRUE_COLORS = 0x1000000;
|
||||
private static final int HIGH_COLORS = 0x7FFF;
|
||||
|
||||
public enum ForceMode {
|
||||
None,
|
||||
Force256Colors,
|
||||
ForceTrueColors
|
||||
}
|
||||
|
||||
// cache the value here as we can't afford to get it each time
|
||||
static final boolean DISABLE_ALTERNATE_CHARSET = Boolean.getBoolean(PROP_DISABLE_ALTERNATE_CHARSET);
|
||||
|
||||
@ -55,33 +68,54 @@ public abstract class AttributedCharSequence implements CharSequence {
|
||||
return toString();
|
||||
}
|
||||
int colors = 256;
|
||||
boolean force256colors = false;
|
||||
ForceMode forceMode = ForceMode.None;
|
||||
ColorPalette palette = null;
|
||||
String alternateIn = null, alternateOut = null;
|
||||
if (terminal != null) {
|
||||
Integer max_colors = terminal.getNumericCapability(Capability.max_colors);
|
||||
if (max_colors != null) {
|
||||
colors = max_colors;
|
||||
}
|
||||
force256colors = AbstractWindowsTerminal.TYPE_WINDOWS_256_COLOR.equals(terminal.getType())
|
||||
|| AbstractWindowsTerminal.TYPE_WINDOWS_CONEMU.equals(terminal.getType());
|
||||
if (AbstractWindowsTerminal.TYPE_WINDOWS_256_COLOR.equals(terminal.getType())
|
||||
|| AbstractWindowsTerminal.TYPE_WINDOWS_CONEMU.equals(terminal.getType())) {
|
||||
forceMode = ForceMode.Force256Colors;
|
||||
}
|
||||
palette = terminal.getPalette();
|
||||
if (!DISABLE_ALTERNATE_CHARSET) {
|
||||
alternateIn = Curses.tputs(terminal.getStringCapability(Capability.enter_alt_charset_mode));
|
||||
alternateOut = Curses.tputs(terminal.getStringCapability(Capability.exit_alt_charset_mode));
|
||||
}
|
||||
}
|
||||
return toAnsi(colors, force256colors, alternateIn, alternateOut);
|
||||
return toAnsi(colors, forceMode, palette, alternateIn, alternateOut);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String toAnsi(int colors, boolean force256colors) {
|
||||
return toAnsi(colors, force256colors, null, null);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String toAnsi(int colors, boolean force256colors, String altIn, String altOut) {
|
||||
return toAnsi(colors, force256colors ? ForceMode.Force256Colors : ForceMode.None, null, altIn, altOut);
|
||||
}
|
||||
|
||||
public String toAnsi(int colors, ForceMode force) {
|
||||
return toAnsi(colors, force, null, null, null);
|
||||
}
|
||||
|
||||
public String toAnsi(int colors, ForceMode force, ColorPalette palette) {
|
||||
return toAnsi(colors, force, palette, null, null);
|
||||
}
|
||||
|
||||
public String toAnsi(int colors, ForceMode force, ColorPalette palette, String altIn, String altOut) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int style = 0;
|
||||
int foreground = -1;
|
||||
int background = -1;
|
||||
long style = 0;
|
||||
long foreground = 0;
|
||||
long background = 0;
|
||||
boolean alt = false;
|
||||
if (palette == null) {
|
||||
palette = ColorPalette.DEFAULT;
|
||||
}
|
||||
for (int i = 0; i < length(); i++) {
|
||||
char c = charAt(i);
|
||||
if (altIn != null && altOut != null) {
|
||||
@ -105,14 +139,14 @@ public abstract class AttributedCharSequence implements CharSequence {
|
||||
sb.append(alt ? altIn : altOut);
|
||||
}
|
||||
}
|
||||
int s = styleCodeAt(i) & ~F_HIDDEN; // The hidden flag does not change the ansi styles
|
||||
long s = styleCodeAt(i) & ~F_HIDDEN; // The hidden flag does not change the ansi styles
|
||||
if (style != s) {
|
||||
int d = (style ^ s) & MASK;
|
||||
int fg = (s & F_FOREGROUND) != 0 ? (s & FG_COLOR) >>> FG_COLOR_EXP : -1;
|
||||
int bg = (s & F_BACKGROUND) != 0 ? (s & BG_COLOR) >>> BG_COLOR_EXP : -1;
|
||||
long d = (style ^ s) & MASK;
|
||||
long fg = (s & F_FOREGROUND) != 0 ? s & (FG_COLOR | F_FOREGROUND) : 0;
|
||||
long bg = (s & F_BACKGROUND) != 0 ? s & (BG_COLOR | F_BACKGROUND) : 0;
|
||||
if (s == 0) {
|
||||
sb.append("\033[0m");
|
||||
foreground = background = -1;
|
||||
foreground = background = 0;
|
||||
} else {
|
||||
sb.append("\033[");
|
||||
boolean first = true;
|
||||
@ -135,18 +169,38 @@ public abstract class AttributedCharSequence implements CharSequence {
|
||||
first = attr(sb, (s & F_CROSSED_OUT) != 0 ? "9" : "29", first);
|
||||
}
|
||||
if (foreground != fg) {
|
||||
if (fg >= 0) {
|
||||
int rounded = Colors.roundColor(fg, colors);
|
||||
if (rounded < 8 && !force256colors) {
|
||||
first = attr(sb, "3" + Integer.toString(rounded), first);
|
||||
// small hack to force setting bold again after a foreground color change
|
||||
d |= (s & F_BOLD);
|
||||
} else if (rounded < 16 && !force256colors) {
|
||||
first = attr(sb, "9" + Integer.toString(rounded - 8), first);
|
||||
// small hack to force setting bold again after a foreground color change
|
||||
d |= (s & F_BOLD);
|
||||
} else {
|
||||
first = attr(sb, "38;5;" + Integer.toString(rounded), first);
|
||||
if (fg > 0) {
|
||||
int rounded = -1;
|
||||
if ((fg & F_FOREGROUND_RGB) != 0) {
|
||||
int r = (int)(fg >> (FG_COLOR_EXP + 16)) & 0xFF;
|
||||
int g = (int)(fg >> (FG_COLOR_EXP + 8)) & 0xFF;
|
||||
int b = (int)(fg >> FG_COLOR_EXP) & 0xFF;
|
||||
if (colors >= HIGH_COLORS) {
|
||||
first = attr(sb, "38;2;" + r + ";" + g + ";" + b, first);
|
||||
} else {
|
||||
rounded = palette.round(r, g, b);
|
||||
}
|
||||
} else if ((fg & F_FOREGROUND_IND) != 0) {
|
||||
rounded = palette.round((int)(fg >> FG_COLOR_EXP) & 0xFF);
|
||||
}
|
||||
if (rounded >= 0) {
|
||||
if (colors >= HIGH_COLORS && force == ForceMode.ForceTrueColors) {
|
||||
int col = palette.getColor(rounded);
|
||||
int r = (col >> 16) & 0xFF;
|
||||
int g = (col >> 8) & 0xFF;
|
||||
int b = col & 0xFF;
|
||||
first = attr(sb, "38;2;" + r + ";" + g + ";" + b, first);
|
||||
} else if (force == ForceMode.Force256Colors || rounded >= 16) {
|
||||
first = attr(sb, "38;5;" + rounded, first);
|
||||
} else if (rounded >= 8) {
|
||||
first = attr(sb, "9" + (rounded - 8), first);
|
||||
// small hack to force setting bold again after a foreground color change
|
||||
d |= (s & F_BOLD);
|
||||
} else {
|
||||
first = attr(sb, "3" + rounded, first);
|
||||
// small hack to force setting bold again after a foreground color change
|
||||
d |= (s & F_BOLD);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
first = attr(sb, "39", first);
|
||||
@ -154,14 +208,34 @@ public abstract class AttributedCharSequence implements CharSequence {
|
||||
foreground = fg;
|
||||
}
|
||||
if (background != bg) {
|
||||
if (bg >= 0) {
|
||||
int rounded = Colors.roundColor(bg, colors);
|
||||
if (rounded < 8 && !force256colors) {
|
||||
first = attr(sb, "4" + Integer.toString(rounded), first);
|
||||
} else if (rounded < 16 && !force256colors) {
|
||||
first = attr(sb, "10" + Integer.toString(rounded - 8), first);
|
||||
} else {
|
||||
first = attr(sb, "48;5;" + Integer.toString(rounded), first);
|
||||
if (bg > 0) {
|
||||
int rounded = -1;
|
||||
if ((bg & F_BACKGROUND_RGB) != 0) {
|
||||
int r = (int)(bg >> (BG_COLOR_EXP + 16)) & 0xFF;
|
||||
int g = (int)(bg >> (BG_COLOR_EXP + 8)) & 0xFF;
|
||||
int b = (int)(bg >> BG_COLOR_EXP) & 0xFF;
|
||||
if (colors >= HIGH_COLORS) {
|
||||
first = attr(sb, "48;2;" + r + ";" + g + ";" + b, first);
|
||||
} else {
|
||||
rounded = palette.round(r, g, b);
|
||||
}
|
||||
} else if ((bg & F_BACKGROUND_IND) != 0) {
|
||||
rounded = palette.round((int)(bg >> BG_COLOR_EXP) & 0xFF);
|
||||
}
|
||||
if (rounded >= 0) {
|
||||
if (colors >= HIGH_COLORS && force == ForceMode.ForceTrueColors) {
|
||||
int col = palette.getColor(rounded);
|
||||
int r = (col >> 16) & 0xFF;
|
||||
int g = (col >> 8) & 0xFF;
|
||||
int b = col & 0xFF;
|
||||
first = attr(sb, "48;2;" + r + ";" + g + ";" + b, first);
|
||||
} else if (force == ForceMode.Force256Colors || rounded >= 16) {
|
||||
first = attr(sb, "48;5;" + rounded, first);
|
||||
} else if (rounded >= 8) {
|
||||
first = attr(sb, "10" + (rounded - 8), first);
|
||||
} else {
|
||||
first = attr(sb, "4" + rounded, first);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
first = attr(sb, "49", first);
|
||||
@ -220,7 +294,7 @@ public abstract class AttributedCharSequence implements CharSequence {
|
||||
|
||||
public abstract AttributedStyle styleAt(int index);
|
||||
|
||||
int styleCodeAt(int index) {
|
||||
long styleCodeAt(int index) {
|
||||
return styleAt(index).getStyle();
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import java.util.regex.Pattern;
|
||||
public class AttributedString extends AttributedCharSequence {
|
||||
|
||||
final char[] buffer;
|
||||
final int[] style;
|
||||
final long[] style;
|
||||
final int start;
|
||||
final int end;
|
||||
public static final AttributedString EMPTY = new AttributedString("");
|
||||
@ -78,7 +78,7 @@ public class AttributedString extends AttributedCharSequence {
|
||||
for (int i = 0; i < l; i++) {
|
||||
buffer[i] = str.charAt(start + i);
|
||||
}
|
||||
style = new int[l];
|
||||
style = new long[l];
|
||||
if (s != null) {
|
||||
Arrays.fill(style, s.getStyle());
|
||||
}
|
||||
@ -87,7 +87,7 @@ public class AttributedString extends AttributedCharSequence {
|
||||
}
|
||||
}
|
||||
|
||||
AttributedString(char[] buffer, int[] style, int start, int end) {
|
||||
AttributedString(char[] buffer, long[] style, int start, int end) {
|
||||
this.buffer = buffer;
|
||||
this.style = style;
|
||||
this.start = start;
|
||||
@ -142,7 +142,7 @@ public class AttributedString extends AttributedCharSequence {
|
||||
}
|
||||
|
||||
@Override
|
||||
int styleCodeAt(int index) {
|
||||
long styleCodeAt(int index) {
|
||||
return style[start + index];
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ public class AttributedString extends AttributedCharSequence {
|
||||
Matcher matcher = pattern.matcher(this);
|
||||
boolean result = matcher.find();
|
||||
if (result) {
|
||||
int[] newstyle = this.style.clone();
|
||||
long[] newstyle = this.style.clone();
|
||||
do {
|
||||
for (int i = matcher.start(); i < matcher.end(); i++) {
|
||||
newstyle[this.start + i] = (newstyle[this.start + i] & ~style.getMask()) | style.getStyle();
|
||||
@ -185,7 +185,7 @@ public class AttributedString extends AttributedCharSequence {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private boolean arrEq(int[] a1, int[] a2, int s1, int s2, int l) {
|
||||
private boolean arrEq(long[] a1, long[] a2, int s1, int s2, int l) {
|
||||
for (int i = 0; i < l; i++) {
|
||||
if (a1[s1+i] != a2[s2+i]) {
|
||||
return false;
|
||||
|
@ -24,7 +24,7 @@ import java.util.regex.Pattern;
|
||||
public class AttributedStringBuilder extends AttributedCharSequence implements Appendable {
|
||||
|
||||
private char[] buffer;
|
||||
private int[] style;
|
||||
private long[] style;
|
||||
private int length;
|
||||
private TabStops tabs = new TabStops(0);
|
||||
private int lastLineLength = 0;
|
||||
@ -44,7 +44,7 @@ public class AttributedStringBuilder extends AttributedCharSequence implements A
|
||||
|
||||
public AttributedStringBuilder(int capacity) {
|
||||
buffer = new char[capacity];
|
||||
style = new int[capacity];
|
||||
style = new long[capacity];
|
||||
length = 0;
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ public class AttributedStringBuilder extends AttributedCharSequence implements A
|
||||
}
|
||||
|
||||
@Override
|
||||
int styleCodeAt(int index) {
|
||||
long styleCodeAt(int index) {
|
||||
return style[index];
|
||||
}
|
||||
|
||||
@ -89,11 +89,17 @@ public class AttributedStringBuilder extends AttributedCharSequence implements A
|
||||
|
||||
@Override
|
||||
public AttributedStringBuilder append(CharSequence csq) {
|
||||
if (csq == null) {
|
||||
csq = "null"; // Required by Appendable.append
|
||||
}
|
||||
return append(new AttributedString(csq, current));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributedStringBuilder append(CharSequence csq, int start, int end) {
|
||||
if (csq == null) {
|
||||
csq = "null"; // Required by Appendable.append
|
||||
}
|
||||
return append(csq.subSequence(start, end));
|
||||
}
|
||||
|
||||
@ -152,7 +158,7 @@ public class AttributedStringBuilder extends AttributedCharSequence implements A
|
||||
ensureCapacity(length + end - start);
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = str.charAt(i);
|
||||
int s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle();
|
||||
long s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle();
|
||||
if (tabs.defined() && c == '\t') {
|
||||
insertTab(new AttributedStyle(s, 0));
|
||||
} else {
|
||||
@ -286,12 +292,10 @@ public class AttributedStringBuilder extends AttributedCharSequence implements A
|
||||
int r = Integer.parseInt(params[++j]);
|
||||
int g = Integer.parseInt(params[++j]);
|
||||
int b = Integer.parseInt(params[++j]);
|
||||
// convert to 256 colors
|
||||
int col = 16 + (r >> 3) * 36 + (g >> 3) * 6 + (b >> 3);
|
||||
if (ansiParam == 38) {
|
||||
current = current.foreground(col);
|
||||
current = current.foreground(r, g, b);
|
||||
} else {
|
||||
current = current.background(col);
|
||||
current = current.background(r, g, b);
|
||||
}
|
||||
}
|
||||
} else if (ansiParam2 == 5) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2018, 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.
|
||||
@ -26,24 +26,28 @@ public class AttributedStyle {
|
||||
|
||||
public static final int BRIGHT = 8;
|
||||
|
||||
static final int F_BOLD = 0x00000001;
|
||||
static final int F_FAINT = 0x00000002;
|
||||
static final int F_ITALIC = 0x00000004;
|
||||
static final int F_UNDERLINE = 0x00000008;
|
||||
static final int F_BLINK = 0x00000010;
|
||||
static final int F_INVERSE = 0x00000020;
|
||||
static final int F_CONCEAL = 0x00000040;
|
||||
static final int F_CROSSED_OUT = 0x00000080;
|
||||
static final int F_FOREGROUND = 0x00000100;
|
||||
static final int F_BACKGROUND = 0x00000200;
|
||||
static final int F_HIDDEN = 0x00000400;
|
||||
static final long F_BOLD = 0x00000001;
|
||||
static final long F_FAINT = 0x00000002;
|
||||
static final long F_ITALIC = 0x00000004;
|
||||
static final long F_UNDERLINE = 0x00000008;
|
||||
static final long F_BLINK = 0x00000010;
|
||||
static final long F_INVERSE = 0x00000020;
|
||||
static final long F_CONCEAL = 0x00000040;
|
||||
static final long F_CROSSED_OUT = 0x00000080;
|
||||
static final long F_FOREGROUND_IND = 0x00000100;
|
||||
static final long F_FOREGROUND_RGB = 0x00000200;
|
||||
static final long F_FOREGROUND = F_FOREGROUND_IND | F_FOREGROUND_RGB;
|
||||
static final long F_BACKGROUND_IND = 0x00000400;
|
||||
static final long F_BACKGROUND_RGB = 0x00000800;
|
||||
static final long F_BACKGROUND = F_BACKGROUND_IND | F_BACKGROUND_RGB;
|
||||
static final long F_HIDDEN = 0x00001000;
|
||||
|
||||
static final int MASK = 0x000007FF;
|
||||
static final long MASK = 0x00001FFF;
|
||||
|
||||
static final int FG_COLOR_EXP = 16;
|
||||
static final int BG_COLOR_EXP = 24;
|
||||
static final int FG_COLOR = 0xFF << FG_COLOR_EXP;
|
||||
static final int BG_COLOR = 0xFF << BG_COLOR_EXP;
|
||||
static final int FG_COLOR_EXP = 15;
|
||||
static final int BG_COLOR_EXP = 39;
|
||||
static final long FG_COLOR = 0xFFFFFFL << FG_COLOR_EXP;
|
||||
static final long BG_COLOR = 0xFFFFFFL << BG_COLOR_EXP;
|
||||
|
||||
public static final AttributedStyle DEFAULT = new AttributedStyle();
|
||||
public static final AttributedStyle BOLD = DEFAULT.bold();
|
||||
@ -53,8 +57,8 @@ public class AttributedStyle {
|
||||
public static final AttributedStyle HIDDEN = DEFAULT.hidden();
|
||||
public static final AttributedStyle HIDDEN_OFF = DEFAULT.hiddenOff();
|
||||
|
||||
final int style;
|
||||
final int mask;
|
||||
final long style;
|
||||
final long mask;
|
||||
|
||||
public AttributedStyle() {
|
||||
this(0, 0);
|
||||
@ -64,7 +68,7 @@ public class AttributedStyle {
|
||||
this(s.style, s.mask);
|
||||
}
|
||||
|
||||
public AttributedStyle(int style, int mask) {
|
||||
public AttributedStyle(long style, long mask) {
|
||||
this.style = style;
|
||||
this.mask = mask & MASK | ((style & F_FOREGROUND) != 0 ? FG_COLOR : 0)
|
||||
| ((style & F_BACKGROUND) != 0 ? BG_COLOR : 0);
|
||||
@ -135,7 +139,7 @@ public class AttributedStyle {
|
||||
}
|
||||
|
||||
public AttributedStyle inverseNeg() {
|
||||
int s = (style & F_INVERSE) != 0 ? style & ~F_INVERSE : style | F_INVERSE;
|
||||
long s = (style & F_INVERSE) != 0 ? style & ~F_INVERSE : style | F_INVERSE;
|
||||
return new AttributedStyle(s, mask | F_INVERSE);
|
||||
}
|
||||
|
||||
@ -172,7 +176,15 @@ public class AttributedStyle {
|
||||
}
|
||||
|
||||
public AttributedStyle foreground(int color) {
|
||||
return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND | ((color << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND);
|
||||
return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND_IND | (((long) color << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND_IND);
|
||||
}
|
||||
|
||||
public AttributedStyle foreground(int r, int g, int b) {
|
||||
return foregroundRgb(r << 16 | g << 8 | b);
|
||||
}
|
||||
|
||||
public AttributedStyle foregroundRgb(int color) {
|
||||
return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND_RGB | ((((long) color & 0xFFFFFF) << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND_RGB);
|
||||
}
|
||||
|
||||
public AttributedStyle foregroundOff() {
|
||||
@ -184,7 +196,15 @@ public class AttributedStyle {
|
||||
}
|
||||
|
||||
public AttributedStyle background(int color) {
|
||||
return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND | ((color << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND);
|
||||
return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND_IND | (((long) color << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND_IND);
|
||||
}
|
||||
|
||||
public AttributedStyle background(int r, int g, int b) {
|
||||
return backgroundRgb(r << 16 | g << 8 | b);
|
||||
}
|
||||
|
||||
public AttributedStyle backgroundRgb(int color) {
|
||||
return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND_RGB | ((((long) color & 0xFFFFFF) << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND_RGB);
|
||||
}
|
||||
|
||||
public AttributedStyle backgroundOff() {
|
||||
@ -214,11 +234,11 @@ public class AttributedStyle {
|
||||
return new AttributedStyle(style & ~F_HIDDEN, mask & ~F_HIDDEN);
|
||||
}
|
||||
|
||||
public int getStyle() {
|
||||
public long getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
public int getMask() {
|
||||
public long getMask() {
|
||||
return mask;
|
||||
}
|
||||
|
||||
@ -234,8 +254,22 @@ public class AttributedStyle {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = style;
|
||||
result = 31 * result + mask;
|
||||
return result;
|
||||
return 31 * Long.hashCode(style) + Long.hashCode(mask);
|
||||
}
|
||||
|
||||
public String toAnsi() {
|
||||
AttributedStringBuilder sb = new AttributedStringBuilder();
|
||||
sb.styled(this, " ");
|
||||
String s = sb.toAnsi(AttributedCharSequence.TRUE_COLORS, AttributedCharSequence.ForceMode.None);
|
||||
return s.length() > 1 ? s.substring(2, s.indexOf('m')) : s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttributedStyle{" +
|
||||
"style=" + style +
|
||||
", mask=" + mask +
|
||||
", ansi=" + toAnsi() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.org.jline.terminal.Terminal;
|
||||
|
||||
/**
|
||||
* Color palette
|
||||
*/
|
||||
public class ColorPalette {
|
||||
|
||||
public static final String XTERM_INITC = "\\E]4;%p1%d;rgb\\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\\E\\\\";
|
||||
|
||||
public static final ColorPalette DEFAULT = new ColorPalette();
|
||||
|
||||
private final Terminal terminal;
|
||||
private String distanceName;
|
||||
private Colors.Distance distance;
|
||||
private boolean osc4;
|
||||
private int[] palette;
|
||||
|
||||
public ColorPalette() {
|
||||
this.terminal = null;
|
||||
this.distanceName = null;
|
||||
this.palette = Colors.DEFAULT_COLORS_256;
|
||||
}
|
||||
|
||||
public ColorPalette(Terminal terminal) throws IOException {
|
||||
this(terminal, null);
|
||||
}
|
||||
|
||||
public ColorPalette(Terminal terminal, String distance) throws IOException {
|
||||
this.terminal = terminal;
|
||||
this.distanceName = distance;
|
||||
loadPalette(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the distance to use for rounding colors.
|
||||
* @return the name of the color distance
|
||||
*/
|
||||
public String getDistanceName() {
|
||||
return distanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the color distance to use when rounding RGB colors to the palette.
|
||||
* @param name the name of the color distance
|
||||
*/
|
||||
public void setDistance(String name) {
|
||||
this.distanceName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the terminal has the capability to change colors.
|
||||
* @return <code>true</code> if the terminal can change colors
|
||||
*/
|
||||
public boolean canChange() {
|
||||
return terminal != null && terminal.getBooleanCapability(InfoCmp.Capability.can_change);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the palette from the terminal.
|
||||
* If the palette has already been loaded, subsequent calls will simply return <code>true</code>.
|
||||
*
|
||||
* @return <code>true</code> if the palette has been successfully loaded.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean loadPalette() throws IOException {
|
||||
if (!osc4) {
|
||||
loadPalette(true);
|
||||
}
|
||||
return osc4;
|
||||
}
|
||||
|
||||
protected void loadPalette(boolean doLoad) throws IOException {
|
||||
if (terminal != null) {
|
||||
int[] pal = doLoad ? doLoad(terminal) : null;
|
||||
if (pal != null) {
|
||||
this.palette = pal;
|
||||
this.osc4 = true;
|
||||
} else {
|
||||
Integer cols = terminal.getNumericCapability(InfoCmp.Capability.max_colors);
|
||||
if (cols != null) {
|
||||
if (cols == Colors.DEFAULT_COLORS_88.length) {
|
||||
this.palette = Colors.DEFAULT_COLORS_88;
|
||||
} else {
|
||||
this.palette = Arrays.copyOf(Colors.DEFAULT_COLORS_256, Math.min(cols, 256));
|
||||
}
|
||||
} else {
|
||||
this.palette = Arrays.copyOf(Colors.DEFAULT_COLORS_256, 256);
|
||||
}
|
||||
this.osc4 = false;
|
||||
}
|
||||
} else {
|
||||
this.palette = Colors.DEFAULT_COLORS_256;
|
||||
this.osc4 = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the palette length
|
||||
* @return the palette length
|
||||
*/
|
||||
public int getLength() {
|
||||
return this.palette.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific color in the palette
|
||||
* @param index the index of the color
|
||||
* @return the color at the given index
|
||||
*/
|
||||
public int getColor(int index) {
|
||||
return palette[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the color of the palette
|
||||
* @param index the index of the color
|
||||
* @param color the new color value
|
||||
*/
|
||||
public void setColor(int index, int color) {
|
||||
palette[index] = color;
|
||||
if (canChange()) {
|
||||
String initc = terminal.getStringCapability(InfoCmp.Capability.initialize_color);
|
||||
if (initc != null || osc4) {
|
||||
// initc expects color in 0..1000 range
|
||||
int r = (((color >> 16) & 0xFF) * 1000) / 255 + 1;
|
||||
int g = (((color >> 8) & 0xFF) * 1000) / 255 + 1;
|
||||
int b = ((color & 0xFF) * 1000) / 255 + 1;
|
||||
if (initc == null) {
|
||||
// This is the xterm version
|
||||
initc = XTERM_INITC;
|
||||
}
|
||||
Curses.tputs(terminal.writer(), initc, index, r, g, b);
|
||||
terminal.writer().flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReal() {
|
||||
return osc4;
|
||||
}
|
||||
|
||||
public int round(int r, int g, int b) {
|
||||
return Colors.roundColor((r << 16) + (g << 8) + b, palette, palette.length, getDist());
|
||||
}
|
||||
|
||||
public int round(int col) {
|
||||
if (col >= palette.length) {
|
||||
col = Colors.roundColor(DEFAULT.getColor(col), palette, palette.length, getDist());
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
protected Colors.Distance getDist() {
|
||||
if (distance == null) {
|
||||
distance = Colors.getDistance(distanceName);
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
private static int[] doLoad(Terminal terminal) throws IOException {
|
||||
PrintWriter writer = terminal.writer();
|
||||
NonBlockingReader reader = terminal.reader();
|
||||
|
||||
int[] palette = new int[256];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
StringBuilder req = new StringBuilder(1024);
|
||||
req.append("\033]4");
|
||||
for (int j = 0; j < 16; j++) {
|
||||
req.append(';').append(i * 16 + j).append(";?");
|
||||
}
|
||||
req.append("\033\\");
|
||||
writer.write(req.toString());
|
||||
writer.flush();
|
||||
|
||||
boolean black = true;
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (reader.peek(50) < 0) {
|
||||
break;
|
||||
}
|
||||
if (reader.read(10) != '\033'
|
||||
|| reader.read(10) != ']'
|
||||
|| reader.read(10) != '4'
|
||||
|| reader.read(10) != ';') {
|
||||
return null;
|
||||
}
|
||||
int idx = 0;
|
||||
int c;
|
||||
while (true) {
|
||||
c = reader.read(10);
|
||||
if (c >= '0' && c <= '9') {
|
||||
idx = idx * 10 + (c - '0');
|
||||
} else if (c == ';') {
|
||||
break;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (idx > 255) {
|
||||
return null;
|
||||
}
|
||||
if (reader.read(10) != 'r'
|
||||
|| reader.read(10) != 'g'
|
||||
|| reader.read(10) != 'b'
|
||||
|| reader.read(10) != ':') {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(16);
|
||||
List<String> rgb = new ArrayList<>();
|
||||
while (true) {
|
||||
c = reader.read(10);
|
||||
if (c == '\007') {
|
||||
rgb.add(sb.toString());
|
||||
break;
|
||||
} else if (c == '\033') {
|
||||
c = reader.read(10);
|
||||
if (c == '\\') {
|
||||
rgb.add(sb.toString());
|
||||
break;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
|
||||
sb.append((char) c);
|
||||
} else if (c == '/') {
|
||||
rgb.add(sb.toString());
|
||||
sb.setLength(0);
|
||||
}
|
||||
}
|
||||
if (rgb.size() != 3) {
|
||||
return null;
|
||||
}
|
||||
double r = Integer.parseInt(rgb.get(0), 16) / ((1 << (4 * rgb.get(0).length())) - 1.0);
|
||||
double g = Integer.parseInt(rgb.get(1), 16) / ((1 << (4 * rgb.get(1).length())) - 1.0);
|
||||
double b = Integer.parseInt(rgb.get(2), 16) / ((1 << (4 * rgb.get(2).length())) - 1.0);
|
||||
palette[idx] = (int)((Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255));
|
||||
black &= palette[idx] == 0;
|
||||
}
|
||||
if (black) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int max = 256;
|
||||
while (max > 0 && palette[--max] == 0);
|
||||
return Arrays.copyOfRange(palette, 0, max + 1);
|
||||
}
|
||||
}
|
@ -25,42 +25,92 @@ public class Colors {
|
||||
* Default 256 colors palette
|
||||
*/
|
||||
public static final int[] DEFAULT_COLORS_256 = {
|
||||
// 16 ansi
|
||||
0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0,
|
||||
0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
|
||||
|
||||
0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f,
|
||||
0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af,
|
||||
0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff,
|
||||
0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f,
|
||||
0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af,
|
||||
0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff,
|
||||
0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f,
|
||||
0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af,
|
||||
0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,
|
||||
0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,
|
||||
0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af,
|
||||
0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
|
||||
0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
|
||||
0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,
|
||||
0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
|
||||
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
|
||||
0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af,
|
||||
0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
|
||||
0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,
|
||||
0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af,
|
||||
0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
|
||||
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
|
||||
0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af,
|
||||
0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
|
||||
0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
|
||||
0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af,
|
||||
0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
|
||||
// 6x6x6 color cube
|
||||
0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff,
|
||||
0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff,
|
||||
0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff,
|
||||
0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff,
|
||||
0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff,
|
||||
0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff,
|
||||
|
||||
0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff,
|
||||
0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff,
|
||||
0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff,
|
||||
0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff,
|
||||
0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5fd7d7, 0x5fd7ff,
|
||||
0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,
|
||||
|
||||
0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff,
|
||||
0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff,
|
||||
0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff,
|
||||
0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
|
||||
0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff,
|
||||
0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff,
|
||||
|
||||
0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff,
|
||||
0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
|
||||
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff,
|
||||
0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff,
|
||||
0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff,
|
||||
0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
|
||||
|
||||
0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff,
|
||||
0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff,
|
||||
0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff,
|
||||
0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
|
||||
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff,
|
||||
0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff,
|
||||
|
||||
0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff,
|
||||
0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
|
||||
0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff,
|
||||
0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff,
|
||||
0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff,
|
||||
0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
|
||||
|
||||
// 24 grey ramp
|
||||
0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
|
||||
0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
|
||||
0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee,
|
||||
};
|
||||
|
||||
/**
|
||||
* Default 88 colors palette
|
||||
*/
|
||||
public static final int[] DEFAULT_COLORS_88 = {
|
||||
// 16 ansi
|
||||
0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0,
|
||||
0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
|
||||
|
||||
// 4x4x4 color cube
|
||||
0x000000, 0x00008b, 0x0000cd, 0x0000ff,
|
||||
0x008b00, 0x008b8b, 0x008bcd, 0x008bff,
|
||||
0x00cd00, 0x00cd8b, 0x00cdcd, 0x00cdff,
|
||||
0x00ff00, 0x00ff8b, 0x00ffcd, 0x00ffff,
|
||||
|
||||
0x8b0000, 0x8b008b, 0x8b00cd, 0x8b00ff,
|
||||
0x8b8b00, 0x8b8b8b, 0x8b8bcd, 0x8b8bff,
|
||||
0x8bcd00, 0x8bcd8b, 0x8bcdcd, 0x8bcdff,
|
||||
0x8bff00, 0x8bff8b, 0x8bffcd, 0x8bffff,
|
||||
|
||||
0xcd0000, 0xcd008b, 0xcd00cd, 0xcd00ff,
|
||||
0xcd8b00, 0xcd8b8b, 0xcd8bcd, 0xcd8bff,
|
||||
0xcdcd00, 0xcdcd8b, 0xcdcdcd, 0xcdcdff,
|
||||
0xcdff00, 0xcdff8b, 0xcdffcd, 0xcdffff,
|
||||
|
||||
0xff0000, 0xff008b, 0xff00cd, 0xff00ff,
|
||||
0xff8b00, 0xff8b8b, 0xff8bcd, 0xff8bff,
|
||||
0xffcd00, 0xffcd8b, 0xffcdcd, 0xffcdff,
|
||||
0xffff00, 0xffff8b, 0xffffcd, 0xffffff,
|
||||
|
||||
// 8 grey ramp
|
||||
0x2e2e2e, 0x5c5c5c, 0x737373, 0x8b8b8b, 0xa2a2a2, 0xb9b9b9, 0xd0d0d0, 0xe7e7e7,
|
||||
};
|
||||
|
||||
/** D50 illuminant for CAM color spaces */
|
||||
public static final double[] D50 = new double[] { 96.422f, 100.0f, 82.521f };
|
||||
/** D65 illuminant for CAM color spaces */
|
||||
@ -74,11 +124,11 @@ public class Colors {
|
||||
public static final double[] darkSurrounding = new double[] { 0.8, 0.525, 0.8 };
|
||||
|
||||
/** sRGB encoding environment */
|
||||
public static final double[] sRGB_encoding_environment = vc(D50, 64.0, 64/5, dimSurrounding);
|
||||
public static final double[] sRGB_encoding_environment = vc(D50, 64.0, 64.0/5, dimSurrounding);
|
||||
/** sRGB typical environment */
|
||||
public static final double[] sRGB_typical_environment = vc(D50, 200.0, 200/5, averageSurrounding);
|
||||
public static final double[] sRGB_typical_environment = vc(D50, 200.0, 200.0/5, averageSurrounding);
|
||||
/** Adobe RGB environment */
|
||||
public static final double[] AdobeRGB_environment = vc(D65, 160.0, 160/5, averageSurrounding);
|
||||
public static final double[] AdobeRGB_environment = vc(D65, 160.0, 160.0/5, averageSurrounding);
|
||||
|
||||
private static int[] COLORS_256 = DEFAULT_COLORS_256;
|
||||
|
||||
@ -130,15 +180,16 @@ public class Colors {
|
||||
return roundColor((r << 16) + (g << 8) + b, COLORS_256, max, (String) null);
|
||||
}
|
||||
|
||||
private static int roundColor(int color, int[] colors, int max, String dist) {
|
||||
static int roundColor(int color, int[] colors, int max, String dist) {
|
||||
return roundColor(color, colors, max, getDistance(dist));
|
||||
}
|
||||
|
||||
private interface Distance {
|
||||
@FunctionalInterface
|
||||
interface Distance {
|
||||
double compute(int c1, int c2);
|
||||
}
|
||||
|
||||
private static int roundColor(int color, int[] colors, int max, Distance distance) {
|
||||
static int roundColor(int color, int[] colors, int max, Distance distance) {
|
||||
double best_distance = Integer.MAX_VALUE;
|
||||
int best_index = Integer.MAX_VALUE;
|
||||
for (int idx = 0; idx < max; idx++) {
|
||||
@ -151,7 +202,7 @@ public class Colors {
|
||||
return best_index;
|
||||
}
|
||||
|
||||
private static Distance getDistance(String dist) {
|
||||
static Distance getDistance(String dist) {
|
||||
if (dist == null) {
|
||||
dist = System.getProperty(PROP_COLOR_DISTANCE, "cie76");
|
||||
}
|
||||
|
@ -352,11 +352,82 @@ public final class Curses {
|
||||
out.append(Integer.toString(toInteger(stack.pop())));
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
if (ch == ':') {
|
||||
ch = str.charAt(index++);
|
||||
}
|
||||
boolean alternate = false;
|
||||
boolean left = false;
|
||||
boolean space = false;
|
||||
boolean plus = false;
|
||||
int width = 0;
|
||||
int prec = -1;
|
||||
int cnv;
|
||||
while ("-+# ".indexOf(ch) >= 0) {
|
||||
switch (ch) {
|
||||
case '-': left = true; break;
|
||||
case '+': plus = true; break;
|
||||
case '#': alternate = true; break;
|
||||
case ' ': space = true; break;
|
||||
}
|
||||
ch = str.charAt(index++);
|
||||
}
|
||||
if ("123456789".indexOf(ch) >= 0) {
|
||||
do {
|
||||
width = width * 10 + (ch - '0');
|
||||
ch = str.charAt(index++);
|
||||
} while ("0123456789".indexOf(ch) >= 0);
|
||||
}
|
||||
if (ch == '.') {
|
||||
prec = 0;
|
||||
ch = str.charAt(index++);
|
||||
}
|
||||
if ("0123456789".indexOf(ch) >= 0) {
|
||||
do {
|
||||
prec = prec * 10 + (ch - '0');
|
||||
ch = str.charAt(index++);
|
||||
} while ("0123456789".indexOf(ch) >= 0);
|
||||
}
|
||||
if ("cdoxXs".indexOf(ch) < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
cnv = ch;
|
||||
if (exec) {
|
||||
String res;
|
||||
if (cnv == 's') {
|
||||
res = (String) stack.pop();
|
||||
if (prec >= 0) {
|
||||
res = res.substring(0, prec);
|
||||
}
|
||||
} else {
|
||||
int p = toInteger(stack.pop());
|
||||
StringBuilder fmt = new StringBuilder(16);
|
||||
fmt.append('%');
|
||||
if (alternate) {
|
||||
fmt.append('#');
|
||||
}
|
||||
if (plus) {
|
||||
fmt.append('+');
|
||||
}
|
||||
if (space) {
|
||||
fmt.append(' ');
|
||||
}
|
||||
if (prec >= 0) {
|
||||
fmt.append('0');
|
||||
fmt.append(prec);
|
||||
}
|
||||
fmt.append((char) cnv);
|
||||
res = String.format(fmt.toString(), p);
|
||||
}
|
||||
if (width > res.length()) {
|
||||
res = String.format("%" + (left ? "-" : "") + width + "s", res);
|
||||
}
|
||||
out.append(res);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '$':
|
||||
if (str.charAt(index) == '<') {
|
||||
if (index < length && str.charAt(index) == '<') {
|
||||
// We don't honour delays, just skip
|
||||
int nb = 0;
|
||||
while ((ch = str.charAt(++index)) != '>') {
|
||||
|
@ -72,6 +72,10 @@ public class Display {
|
||||
public void setDelayLineWrap(boolean v) { delayLineWrap = v; }
|
||||
|
||||
public void resize(int rows, int columns) {
|
||||
if (rows == 0 || columns == 0) {
|
||||
columns = Integer.MAX_VALUE - 1;
|
||||
rows = 1;
|
||||
}
|
||||
if (this.rows != rows || this.columns != columns) {
|
||||
this.rows = rows;
|
||||
this.columns = columns;
|
||||
|
@ -619,7 +619,7 @@ public final class InfoCmp {
|
||||
static {
|
||||
for (String s : Arrays.asList("dumb", "dumb-color", "ansi", "xterm", "xterm-256color",
|
||||
"windows", "windows-256color", "windows-conemu", "windows-vtp",
|
||||
"screen", "screen-256color")) {
|
||||
"screen", "screen-256color", "rxvt-unicode", "rxvt-unicode-256color", "rxvt-basic", "rxvt")) {
|
||||
setDefaultInfoCmp(s, () -> loadDefaultInfoCmp(s));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2016, 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.
|
||||
@ -22,8 +22,9 @@ import java.util.function.Supplier;
|
||||
* @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
|
||||
* @since 2.0
|
||||
*/
|
||||
public final class Log
|
||||
{
|
||||
public final class Log {
|
||||
// private static final Logger logger = Logger.getLogger("org.jline");
|
||||
|
||||
public static void trace(final Object... messages) {
|
||||
// log(Level.FINEST, messages);
|
||||
}
|
||||
@ -110,7 +111,6 @@ public final class Log
|
||||
// }
|
||||
//
|
||||
// static void logr(final Level level, final Supplier<LogRecord> record) {
|
||||
// Logger logger = Logger.getLogger("org.jline");
|
||||
// if (logger.isLoggable(level)) {
|
||||
// // inform record of the logger-name
|
||||
// LogRecord tmp = record.get();
|
||||
@ -120,7 +120,6 @@ public final class Log
|
||||
// }
|
||||
//
|
||||
// static boolean isEnabled(Level level) {
|
||||
// Logger logger = Logger.getLogger("org.jline");
|
||||
// return logger.isLoggable(level);
|
||||
// }
|
||||
|
||||
|
@ -106,6 +106,20 @@ public class NonBlockingPumpInputStream extends NonBlockingInputStream {
|
||||
return res;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
rewind(readBuffer, writeBuffer);
|
||||
return res;
|
||||
}
|
||||
|
||||
public synchronized void setIoException(IOException exception) {
|
||||
this.ioException = exception;
|
||||
notifyAll();
|
||||
|
@ -74,7 +74,11 @@ public class NonBlockingPumpReader extends NonBlockingReader {
|
||||
// Blocks until more input is available or the reader is closed.
|
||||
if (!closed && count == 0) {
|
||||
try {
|
||||
notEmpty.await(timeout, TimeUnit.MILLISECONDS);
|
||||
if (timeout > 0L) {
|
||||
notEmpty.await(timeout, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
notEmpty.await();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw (IOException) new InterruptedIOException().initCause(e);
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ public class PumpReader extends Reader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(CharBuffer target) throws IOException {
|
||||
public synchronized int read(CharBuffer target) throws IOException {
|
||||
if (!target.hasRemaining()) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2016, 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.
|
||||
@ -9,6 +9,7 @@
|
||||
package jdk.internal.org.jline.utils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -91,8 +92,12 @@ public final class Signals {
|
||||
Object signal;
|
||||
try {
|
||||
signal = constructor.newInstance(name);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.trace(() -> "Ignoring unsupported signal " + name);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof IllegalArgumentException) {
|
||||
Log.trace(() -> "Ignoring unsupported signal " + name);
|
||||
} else {
|
||||
Log.debug("Error registering handler for signal ", name, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Class<?> signalHandlerClass = Class.forName("sun.misc.SignalHandler");
|
||||
|
@ -32,6 +32,38 @@ public class StyleResolver {
|
||||
this.source = requireNonNull(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the RGB color for the given name.
|
||||
* <p>
|
||||
* Bright color can be specified with: {@code !<color>} or {@code bright-<color>}.
|
||||
* <p>
|
||||
* Full xterm256 color can be specified with: {@code ~<color>}.
|
||||
* RGB colors can be specified with: {@code x<rgb>} or {@code #<rgb>} where {@code rgb} is
|
||||
* a 24 bits hexadecimal color.
|
||||
*
|
||||
* @param name the name of the color
|
||||
* @return color code, or {@code null} if unable to determine.
|
||||
*/
|
||||
private static Integer colorRgb(String name) {
|
||||
name = name.toLowerCase(Locale.US);
|
||||
// check hexadecimal color
|
||||
if (name.charAt(0) == 'x' || name.charAt(0) == '#') {
|
||||
try {
|
||||
return Integer.parseInt(name.substring(1), 16);
|
||||
} catch (NumberFormatException e) {
|
||||
// log.warning("Invalid hexadecimal color: " + name);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// load indexed color
|
||||
Integer color = color(name);
|
||||
if (color != null && color != -1) {
|
||||
color = Colors.DEFAULT_COLORS_256[color];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color identifier for the given name.
|
||||
* <p>
|
||||
@ -44,21 +76,20 @@ public class StyleResolver {
|
||||
*/
|
||||
private static Integer color(String name) {
|
||||
int flags = 0;
|
||||
name = name.toLowerCase(Locale.US);
|
||||
|
||||
if (name.equals("default")) {
|
||||
return -1;
|
||||
}
|
||||
// extract bright flag from color name
|
||||
if (name.charAt(0) == '!') {
|
||||
name = name.substring(1, name.length());
|
||||
else if (name.charAt(0) == '!') {
|
||||
name = name.substring(1);
|
||||
flags = BRIGHT;
|
||||
} else if (name.startsWith("bright-")) {
|
||||
name = name.substring(7, name.length());
|
||||
name = name.substring(7);
|
||||
flags = BRIGHT;
|
||||
} else if (name.charAt(0) == '~') {
|
||||
name = name.substring(1);
|
||||
try {
|
||||
// TODO: if the palette is not the default one, should be
|
||||
// TODO: translate into 24-bits first and let the #toAnsi() call
|
||||
// TODO: round with the current palette ?
|
||||
name = name.substring(1, name.length());
|
||||
return Colors.rgbColor(name);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// log.warning("Invalid style-color name: " + name);
|
||||
@ -297,25 +328,51 @@ public class StyleResolver {
|
||||
String colorName = parts[1].trim();
|
||||
|
||||
// resolve the color-name
|
||||
Integer color = color(colorName);
|
||||
if (color == null) {
|
||||
// log.warning("Invalid color-name: " + colorName);
|
||||
} else {
|
||||
// resolve and apply color-mode
|
||||
switch (colorMode.toLowerCase(Locale.US)) {
|
||||
case "foreground":
|
||||
case "fg":
|
||||
case "f":
|
||||
return style.foreground(color);
|
||||
Integer color;
|
||||
// resolve and apply color-mode
|
||||
switch (colorMode.toLowerCase(Locale.US)) {
|
||||
case "foreground":
|
||||
case "fg":
|
||||
case "f":
|
||||
color = color(colorName);
|
||||
if (color == null) {
|
||||
// log.warning("Invalid color-name: " + colorName);
|
||||
break;
|
||||
}
|
||||
return color >= 0 ? style.foreground(color) : style.foregroundDefault();
|
||||
|
||||
case "background":
|
||||
case "bg":
|
||||
case "b":
|
||||
return style.background(color);
|
||||
case "background":
|
||||
case "bg":
|
||||
case "b":
|
||||
color = color(colorName);
|
||||
if (color == null) {
|
||||
// log.warning("Invalid color-name: " + colorName);
|
||||
break;
|
||||
}
|
||||
return color >= 0 ? style.background(color) : style.backgroundDefault();
|
||||
|
||||
default:
|
||||
// log.warning("Invalid color-mode: " + colorMode);
|
||||
}
|
||||
case "foreground-rgb":
|
||||
case "fg-rgb":
|
||||
case "f-rgb":
|
||||
color = colorRgb(colorName);
|
||||
if (color == null) {
|
||||
// log.warning("Invalid color-name: " + colorName);
|
||||
break;
|
||||
}
|
||||
return color >= 0 ? style.foregroundRgb(color) : style.foregroundDefault();
|
||||
|
||||
case "background-rgb":
|
||||
case "bg-rgb":
|
||||
case "b-rgb":
|
||||
color = colorRgb(colorName);
|
||||
if (color == null) {
|
||||
// log.warning("Invalid color-name: " + colorName);
|
||||
break;
|
||||
}
|
||||
return color >= 0 ? style.backgroundRgb(color) : style.backgroundDefault();
|
||||
|
||||
default:
|
||||
// log.warning("Invalid color-mode: " + colorMode);
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
# Reconstructed via infocmp from file: /lib/terminfo/r/rxvt-basic
|
||||
rxvt-basic|rxvt-m|rxvt terminal base (X Window System),
|
||||
am, bce, eo, km, mir, msgr, xenl, xon,
|
||||
cols#80, it#8, lines#24,
|
||||
acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
|
||||
bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l,
|
||||
clear=\E[H\E[2J, cnorm=\E[?25h, cr=\r,
|
||||
csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
|
||||
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,
|
||||
dl=\E[%p1%dM, dl1=\E[M, ed=\E[J, el=\E[K, el1=\E[1K,
|
||||
enacs=\E(B\E)0, flash=\E[?5h\E[?5l, home=\E[H,
|
||||
hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, ich1=\E[@,
|
||||
il=\E[%p1%dL, il1=\E[L, ind=\n, is1=\E[?47l\E=\E[?1l,
|
||||
is2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l,
|
||||
kDC=\E[3$, kEND=\E[8$, kHOM=\E[7$, kLFT=\E[d, kNXT=\E[6$,
|
||||
kPRV=\E[5$, kRIT=\E[c, ka1=\EOw, ka3=\EOy, kb2=\EOu, kbs=^?,
|
||||
kc1=\EOq, kc3=\EOs, kcbt=\E[Z, kcub1=\E[D, kcud1=\E[B,
|
||||
kcuf1=\E[C, kcuu1=\E[A, kdch1=\E[3~, kel=\E[8\^,
|
||||
kend=\E[8~, kent=\EOM, kf1=\E[11~, kf10=\E[21~,
|
||||
kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, kf14=\E[26~,
|
||||
kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, kf18=\E[32~,
|
||||
kf19=\E[33~, kf2=\E[12~, kf20=\E[34~, kf21=\E[23$,
|
||||
kf22=\E[24$, kf23=\E[11\^, kf24=\E[12\^, kf25=\E[13\^,
|
||||
kf26=\E[14\^, kf27=\E[15\^, kf28=\E[17\^, kf29=\E[18\^,
|
||||
kf3=\E[13~, kf30=\E[19\^, kf31=\E[20\^, kf32=\E[21\^,
|
||||
kf33=\E[23\^, kf34=\E[24\^, kf35=\E[25\^, kf36=\E[26\^,
|
||||
kf37=\E[28\^, kf38=\E[29\^, kf39=\E[31\^, kf4=\E[14~,
|
||||
kf40=\E[32\^, kf41=\E[33\^, kf42=\E[34\^, kf43=\E[23@,
|
||||
kf44=\E[24@, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~,
|
||||
kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, khome=\E[7~,
|
||||
kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~,
|
||||
rc=\E8, rev=\E[7m, ri=\EM, rmacs=^O, rmcup=\E[2J\E[?47l\E8,
|
||||
rmir=\E[4l, rmkx=\E>, rmso=\E[27m, rmul=\E[24m,
|
||||
rs1=\E>\E[1;3;4;5;6l\E[?7h\E[m\E[r\E[2J\E[H,
|
||||
rs2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l\E=\E[?1000l\E[?25h,
|
||||
s0ds=\E(B, s1ds=\E(0, sc=\E7,
|
||||
sgr=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;,
|
||||
sgr0=\E[0m\017, smacs=^N, smcup=\E7\E[?47h, smir=\E[4h,
|
||||
smkx=\E=, smso=\E[7m, smul=\E[4m, tbc=\E[3g,
|
||||
vpa=\E[%i%p1%dd,
|
@ -0,0 +1,44 @@
|
||||
# Reconstructed via infocmp from file: /lib/terminfo/r/rxvt-unicode-256color
|
||||
rxvt-unicode-256color|rxvt-unicode terminal with 256 colors (X Window System),
|
||||
am, bce, bw, ccc, eo, hs, km, mc5i, mir, msgr, npc, xenl, xon,
|
||||
btns#5, colors#0x100, cols#80, it#8, lines#24, lm#0, ncv#0,
|
||||
pairs#0x7fff,
|
||||
acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
|
||||
bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l,
|
||||
clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r,
|
||||
csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
|
||||
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,
|
||||
cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM,
|
||||
dl1=\E[M, dsl=\E]2;\007, ech=\E[%p1%dX, ed=\E[J, el=\E[K,
|
||||
el1=\E[1K, enacs=, flash=\E[?5h$<20/>\E[?5l, fsl=^G,
|
||||
home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@,
|
||||
ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=\n, indn=\E[%p1%dS,
|
||||
initc=\E]4;%p1%d;rgb\:%p2%{65535}%*%{1000}%/%4.4X/%p3%{65535}%*%{1000}%/%4.4X/%p4%{65535}%*%{1000}%/%4.4X\E\\,
|
||||
is1=\E[!p,
|
||||
is2=\E[r\E[m\E[2J\E[?7;25h\E[?1;3;4;5;6;9;66;1000;1001;1049l\E[4l,
|
||||
kDC=\E[3$, kEND=\E[8$, kFND=\E[1$, kHOM=\E[7$, kIC=\E[2$,
|
||||
kLFT=\E[d, kNXT=\E[6$, kPRV=\E[5$, kRIT=\E[c, ka1=\EOw,
|
||||
ka3=\EOy, kb2=\EOu, kbs=^?, kc1=\EOq, kc3=\EOs, kcbt=\E[Z,
|
||||
kcub1=\E[D, kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A,
|
||||
kdch1=\E[3~, kel=\E[8\^, kend=\E[8~, kent=\EOM, kf1=\E[11~,
|
||||
kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~,
|
||||
kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~,
|
||||
kf18=\E[32~, kf19=\E[33~, kf2=\E[12~, kf20=\E[34~,
|
||||
kf3=\E[13~, kf4=\E[14~, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~,
|
||||
kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, khome=\E[7~,
|
||||
kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~,
|
||||
mc0=\E[i, mc4=\E[4i, mc5=\E[5i, op=\E[39;49m, rc=\E8,
|
||||
rev=\E[7m, ri=\EM, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B,
|
||||
rmam=\E[?7l, rmcup=\E[r\E[?1049l, rmir=\E[4l, rmkx=\E>,
|
||||
rmso=\E[27m, rmul=\E[24m, rs1=\Ec,
|
||||
rs2=\E[r\E[m\E[?7;25h\E[?1;3;4;5;6;9;66;1000;1001;1049l\E[4l,
|
||||
s0ds=\E(B, s1ds=\E(0, s2ds=\E*B, s3ds=\E+B, sc=\E7,
|
||||
setab=\E[48;5;%p1%dm, setaf=\E[38;5;%p1%dm,
|
||||
setb=%?%p1%{7}%>%t\E[48;5;%p1%dm%e\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m%;,
|
||||
setf=%?%p1%{7}%>%t\E[38;5;%p1%dm%e\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m%;,
|
||||
sgr=\E[%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m%?%p9%t\E(0%e\E(B%;,
|
||||
sgr0=\E[m\E(B, sitm=\E[3m, smacs=\E(0, smam=\E[?7h,
|
||||
smcup=\E[?1049h, smir=\E[4h, smkx=\E=, smso=\E[7m,
|
||||
smul=\E[4m, tbc=\E[3g, tsl=\E]2;, u6=\E[%i%d;%dR, u7=\E[6n,
|
||||
u8=\E[?1;2c, u9=\E[c, vpa=\E[%i%p1%dd,
|
@ -0,0 +1,44 @@
|
||||
# Reconstructed via infocmp from file: /lib/terminfo/r/rxvt-unicode
|
||||
rxvt-unicode|rxvt-unicode terminal (X Window System),
|
||||
am, bce, bw, ccc, eo, hs, km, mc5i, mir, msgr, npc, xenl, xon,
|
||||
btns#5, colors#88, cols#80, it#8, lines#24, lm#0, ncv#0,
|
||||
pairs#7744,
|
||||
acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
|
||||
bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l,
|
||||
clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r,
|
||||
csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
|
||||
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,
|
||||
cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM,
|
||||
dl1=\E[M, dsl=\E]2;\007, ech=\E[%p1%dX, ed=\E[J, el=\E[K,
|
||||
el1=\E[1K, enacs=, flash=\E[?5h$<20/>\E[?5l, fsl=^G,
|
||||
home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@,
|
||||
ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=\n, indn=\E[%p1%dS,
|
||||
initc=\E]4;%p1%d;rgb\:%p2%{65535}%*%{1000}%/%4.4X/%p3%{65535}%*%{1000}%/%4.4X/%p4%{65535}%*%{1000}%/%4.4X\E\\,
|
||||
is1=\E[!p,
|
||||
is2=\E[r\E[m\E[2J\E[?7;25h\E[?1;3;4;5;6;9;66;1000;1001;1049l\E[4l,
|
||||
kDC=\E[3$, kEND=\E[8$, kFND=\E[1$, kHOM=\E[7$, kIC=\E[2$,
|
||||
kLFT=\E[d, kNXT=\E[6$, kPRV=\E[5$, kRIT=\E[c, ka1=\EOw,
|
||||
ka3=\EOy, kb2=\EOu, kbs=^?, kc1=\EOq, kc3=\EOs, kcbt=\E[Z,
|
||||
kcub1=\E[D, kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A,
|
||||
kdch1=\E[3~, kel=\E[8\^, kend=\E[8~, kent=\EOM, kf1=\E[11~,
|
||||
kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~,
|
||||
kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~,
|
||||
kf18=\E[32~, kf19=\E[33~, kf2=\E[12~, kf20=\E[34~,
|
||||
kf3=\E[13~, kf4=\E[14~, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~,
|
||||
kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, khome=\E[7~,
|
||||
kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~,
|
||||
mc0=\E[i, mc4=\E[4i, mc5=\E[5i, op=\E[39;49m, rc=\E8,
|
||||
rev=\E[7m, ri=\EM, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B,
|
||||
rmam=\E[?7l, rmcup=\E[r\E[?1049l, rmir=\E[4l, rmkx=\E>,
|
||||
rmso=\E[27m, rmul=\E[24m, rs1=\Ec,
|
||||
rs2=\E[r\E[m\E[?7;25h\E[?1;3;4;5;6;9;66;1000;1001;1049l\E[4l,
|
||||
s0ds=\E(B, s1ds=\E(0, s2ds=\E*B, s3ds=\E+B, sc=\E7,
|
||||
setab=\E[48;5;%p1%dm, setaf=\E[38;5;%p1%dm,
|
||||
setb=%?%p1%{7}%>%t\E[48;5;%p1%dm%e\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m%;,
|
||||
setf=%?%p1%{7}%>%t\E[38;5;%p1%dm%e\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m%;,
|
||||
sgr=\E[%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m%?%p9%t\E(0%e\E(B%;,
|
||||
sgr0=\E[m\E(B, sitm=\E[3m, smacs=\E(0, smam=\E[?7h,
|
||||
smcup=\E[?1049h, smir=\E[4h, smkx=\E=, smso=\E[7m,
|
||||
smul=\E[4m, tbc=\E[3g, tsl=\E]2;, u6=\E[%i%d;%dR, u7=\E[6n,
|
||||
u8=\E[?1;2c, u9=\E[c, vpa=\E[%i%p1%dd,
|
@ -0,0 +1,43 @@
|
||||
# Reconstructed via infocmp from file: /lib/terminfo/r/rxvt
|
||||
rxvt|rxvt terminal emulator (X Window System),
|
||||
am, bce, eo, km, mir, msgr, xenl, xon,
|
||||
colors#8, cols#80, it#8, lines#24, ncv@, pairs#64,
|
||||
acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
|
||||
bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l,
|
||||
clear=\E[H\E[2J, cnorm=\E[?25h, cr=\r,
|
||||
csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
|
||||
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,
|
||||
dl=\E[%p1%dM, dl1=\E[M, ed=\E[J, el=\E[K, el1=\E[1K,
|
||||
enacs=\E(B\E)0, flash=\E[?5h\E[?5l, home=\E[H,
|
||||
hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, ich1=\E[@,
|
||||
il=\E[%p1%dL, il1=\E[L, ind=\n, is1=\E[?47l\E=\E[?1l,
|
||||
is2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l,
|
||||
kDC=\E[3$, kEND=\E[8$, kHOM=\E[7$, kLFT=\E[d, kNXT=\E[6$,
|
||||
kPRV=\E[5$, kRIT=\E[c, ka1=\EOw, ka3=\EOy, kb2=\EOu, kbs=^?,
|
||||
kc1=\EOq, kc3=\EOs, kcbt=\E[Z, kcub1=\E[D, kcud1=\E[B,
|
||||
kcuf1=\E[C, kcuu1=\E[A, kdch1=\E[3~, kel=\E[8\^,
|
||||
kend=\E[8~, kent=\EOM, kf1=\E[11~, kf10=\E[21~,
|
||||
kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, kf14=\E[26~,
|
||||
kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, kf18=\E[32~,
|
||||
kf19=\E[33~, kf2=\E[12~, kf20=\E[34~, kf21=\E[23$,
|
||||
kf22=\E[24$, kf23=\E[11\^, kf24=\E[12\^, kf25=\E[13\^,
|
||||
kf26=\E[14\^, kf27=\E[15\^, kf28=\E[17\^, kf29=\E[18\^,
|
||||
kf3=\E[13~, kf30=\E[19\^, kf31=\E[20\^, kf32=\E[21\^,
|
||||
kf33=\E[23\^, kf34=\E[24\^, kf35=\E[25\^, kf36=\E[26\^,
|
||||
kf37=\E[28\^, kf38=\E[29\^, kf39=\E[31\^, kf4=\E[14~,
|
||||
kf40=\E[32\^, kf41=\E[33\^, kf42=\E[34\^, kf43=\E[23@,
|
||||
kf44=\E[24@, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~,
|
||||
kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, khome=\E[7~,
|
||||
kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~,
|
||||
op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, rmacs=^O,
|
||||
rmcup=\E[2J\E[?47l\E8, rmir=\E[4l, rmkx=\E>, rmso=\E[27m,
|
||||
rmul=\E[24m,
|
||||
rs1=\E>\E[1;3;4;5;6l\E[?7h\E[m\E[r\E[2J\E[H,
|
||||
rs2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l\E=\E[?1000l\E[?25h,
|
||||
s0ds=\E(B, s1ds=\E(0, sc=\E7, setab=\E[4%p1%dm,
|
||||
setaf=\E[3%p1%dm,
|
||||
sgr=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;,
|
||||
sgr0=\E[m\017, smacs=^N, smcup=\E7\E[?47h, smir=\E[4h,
|
||||
smkx=\E=, smso=\E[7m, smul=\E[4m, tbc=\E[3g,
|
||||
vpa=\E[%i%p1%dd,
|
@ -1,4 +1,4 @@
|
||||
## JLine v3.14.0
|
||||
## JLine v3.20.0
|
||||
|
||||
### JLine License
|
||||
<pre>
|
||||
|
@ -14,6 +14,7 @@ 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;
|
||||
@ -47,4 +48,30 @@ public class JnaSupportImpl implements JnaSupport {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -70,16 +70,42 @@ public class JnaWinSysTerminal extends AbstractWindowsTerminal {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public static boolean isWindowsConsole() {
|
||||
try {
|
||||
IntByReference mode = new IntByReference();
|
||||
Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode);
|
||||
Kernel32.INSTANCE.GetConsoleMode(consoleIn, 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);
|
||||
strings.put(InfoCmp.Capability.key_mouse, "\\E[M");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getConsoleOutputCP() {
|
||||
return Kernel32.INSTANCE.GetConsoleOutputCP();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getConsoleMode() {
|
||||
IntByReference mode = new IntByReference();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2021, 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
|
||||
@ -57,11 +57,6 @@ public class AbstractWindowsTerminalTest {
|
||||
}
|
||||
};
|
||||
var t = new AbstractWindowsTerminal(out, "test", "vt100", null, -1, false, SignalHandler.SIG_DFL, isWrapper) {
|
||||
@Override
|
||||
protected int getConsoleOutputCP() {
|
||||
throw new UnsupportedOperationException("unexpected.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getConsoleMode() {
|
||||
return -1;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2021, 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
|
||||
@ -34,6 +34,7 @@ import java.io.StringWriter;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import jdk.internal.org.jline.terminal.Size;
|
||||
import jdk.internal.org.jline.terminal.Terminal.SignalHandler;
|
||||
import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal;
|
||||
|
||||
public class KeyConversionTest {
|
||||
@ -58,11 +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, null, in -> in) {
|
||||
@Override
|
||||
protected int getConsoleOutputCP() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
0, true, SignalHandler.SIG_DFL, in -> in) {
|
||||
@Override
|
||||
protected int getConsoleMode() {
|
||||
return 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user