diff --git a/.github/workflows/master-build.yml b/.github/workflows/master-build.yml
index 090d9d4c6..76d70238e 100644
--- a/.github/workflows/master-build.yml
+++ b/.github/workflows/master-build.yml
@@ -56,7 +56,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
- java: [ '1.8', '11' ]
+ java: [ '1.8', '11', '14' ]
steps:
- uses: actions/checkout@v2
diff --git a/README.md b/README.md
index 5d254fa22..c02ef55c3 100644
--- a/README.md
+++ b/README.md
@@ -37,18 +37,19 @@ The big bundle is named:
jline-${jline.version}.jar
-The dependencies are minimal: you may use JLine without any dependency on *nix systems, but in order to support windows or more advanced usage, you will need to add either [`jansi`](https://repo1.maven.org/maven2/org/fusesource/jansi/jansi/1.17/jansi-1.17.jar) or [`jna`](https://repo1.maven.org/maven2/net/java/dev/jna/jna/4.5.1/jna-4.5.1.jar) library.
+The dependencies are minimal: you may use JLine without any dependency on *nix systems, but in order to support windows or more advanced usage, you will need to add either [`Jansi`](https://repo1.maven.org/maven2/org/fusesource/jansi/jansi/1.18/jansi-1.18.jar) or [`JNA`](https://repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar) library.
-You can also use finer grained jars:
+You can also use fine grained jars:
* `jline-terminal`: the `Terminal` api and implementations
-* `jline-terminal-jansi`: terminal implementations leveraging the `jansi` library
-* `jline-terminal-jna`: terminal implementations leveraging the `jna` library
+* `jline-terminal-jansi`: terminal implementations leveraging the `Jansi` library
+* `jline-terminal-jna`: terminal implementations leveraging the `JNA` library
* `jline-reader`: the line reader (including completion, history, etc...)
-* `jline-groovy`: jline [ScriptEngine](https://github.com/jline/jline3/blob/master/reader/src/main/java/org/jline/reader/ScriptEngine.java) implementation using Groovy
* `jline-style`: styling api
* `jline-remote-ssh`: helpers for using jline with [Mina SSHD](http://mina.apache.org/sshd-project/)
* `jline-remote-telnet`: helpers for using jline over telnet (including a telnet server implementation)
* `jline-builtins`: several high level tools: `less` pager, `nano` editor, `screen` multiplexer, etc...
+* `jline-console`: command registry, object printer and widget implementations
+* `jline-groovy`: `ScriptEngine` implementation using Groovy
## Supported platforms
@@ -100,8 +101,9 @@ All the jars and releases are available from Maven Central, so you'll find every
## Requirements
-* Maven 3.3+ (prefer included maven-wrapper)
+* Maven 3.3+
* Java 8+
+* Graal 19.3+ (native-image)
Check out and build:
@@ -111,6 +113,12 @@ cd jline3
./build rebuild
```
+Build Graal native-image demo:
+
+```sh
+./build rebuild -Pnative-image
+```
+
## Results
The following artifacts are build:
@@ -125,11 +133,12 @@ The fine grained bundles are located at:
terminal-jansi/target/jline-jansi-${jline.version}.jar
terminal-jna/target/jline-jna-${jline.version}.jar
reader/target/jline-reader-${jline.version}.jar
- groovy/target/jline-groovy-${jline.version}.jar
style/target/jline-style-${jline.version}.jar
- builtins/target/jline-builtins-${jline.version}.jar
remote-telnet/target/jline-remote-telnet-${jline.version}.jar
remote-ssh/target/jline-remote-ssh-${jline.version}.jar
+ builtins/target/jline-builtins-${jline.version}.jar
+ console/target/jline-console-${jline.version}.jar
+ groovy/target/jline-groovy-${jline.version}.jar
Maven has a concept of `SNAPSHOT`. During development, the jline version will always ends with `-SNAPSHOT`, which means that the version is in development and not a release.
@@ -143,8 +152,11 @@ To run the demo, simply use one of the following commands after having build `JL
# Gogo terminal
./build demo
-# groovy REPL
+# Groovy REPL
./build repl
+
+# Graal native-image
+./build graal
```
## Continuous Integration
diff --git a/appveyor.yml b/appveyor.yml
index e938828b0..5ef2e7dea 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -5,12 +5,12 @@ install:
Add-Type -AssemblyName System.IO.Compression.FileSystem
if (!(Test-Path -Path "C:\maven" )) {
(new-object System.Net.WebClient).DownloadFile(
- 'http://www.us.apache.org/dist/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.zip',
+ 'http://www.us.apache.org/dist/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.zip',
'C:\maven-bin.zip'
)
[System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven")
}
- - cmd: SET M2_HOME=C:\maven\apache-maven-3.2.5
+ - cmd: SET M2_HOME=C:\maven\apache-maven-3.3.9
- cmd: SET PATH=%M2_HOME%\bin;%JAVA_HOME%\bin;%PATH%
- cmd: SET MAVEN_OPTS=-XX:MaxPermSize=2g -Xmx4g
- cmd: SET JAVA_OPTS=-XX:MaxPermSize=2g -Xmx4g
diff --git a/build.config b/build.config
index 92653bb97..d0eee320d 100644
--- a/build.config
+++ b/build.config
@@ -13,3 +13,7 @@ function command_demo() {
function command_repl() {
exec demo/jline-repl.sh $*
}
+
+function command_graal() {
+ exec graal/target/graal $*
+}
diff --git a/builtins/pom.xml b/builtins/pom.xml
index 5d27c7eea..c736cdccd 100644
--- a/builtins/pom.xml
+++ b/builtins/pom.xml
@@ -16,12 +16,16 @@
org.jline
jline-parent
- 3.14.1-SNAPSHOT
+ 3.18.1-SNAPSHOT
jline-builtins
JLine Builtins
+
+ org.jline.builtins
+
+
org.jline
@@ -31,10 +35,6 @@
org.jline
jline-style
-
- org.jline
- jline-groovy
-
com.googlecode.juniversalchardet
@@ -62,33 +62,31 @@
default-compile
-
- **/TTop.java
- **/Builtins.java
-
+
+ **/ConsoleEngineImpl.java
+ **/TTop.java
+
-Xlint:all,-options
-Werror
- -profile
- compact3
-
+
- noncompact
+ compact
compile
-
- **/TTop.java
- **/Builtins.java
-
+
+ **/ConsoleEngineImpl.java
+ **/TTop.java
+
-Xlint:all,-options
-Werror
-profile
- compact3
+ compact1
diff --git a/builtins/src/main/java/org/jline/builtins/Builtins.java b/builtins/src/main/java/org/jline/builtins/Builtins.java
deleted file mode 100644
index 5a3fa4172..000000000
--- a/builtins/src/main/java/org/jline/builtins/Builtins.java
+++ /dev/null
@@ -1,575 +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 org.jline.builtins;
-
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.nio.file.Path;
-import java.util.*;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import org.jline.builtins.Completers.FilesCompleter;
-import org.jline.builtins.Completers.OptDesc;
-import org.jline.builtins.Completers.OptionCompleter;
-import org.jline.builtins.Completers.SystemCompleter;
-import org.jline.builtins.Options.HelpException;
-import org.jline.builtins.Widgets.ArgDesc;
-import org.jline.builtins.Widgets.CmdDesc;
-import org.jline.reader.*;
-import org.jline.reader.LineReader.Option;
-import org.jline.reader.impl.completer.ArgumentCompleter;
-import org.jline.reader.impl.completer.NullCompleter;
-import org.jline.reader.impl.completer.StringsCompleter;
-import org.jline.terminal.Terminal;
-import org.jline.utils.AttributedString;
-
-/**
- * Builtins: create tab completers, execute and create descriptions for builtins commands.
- *
- * @author Matti Rinta-Nikkola
- */
-public class Builtins implements CommandRegistry {
- public enum Command {NANO
- , LESS
- , HISTORY
- , WIDGET
- , KEYMAP
- , SETOPT
- , SETVAR
- , UNSETOPT
- , TTOP};
- private ConfigurationPath configPath;
- private final Function widgetCreator;
- private final Supplier workDir;
- private Map commandName = new HashMap<>();
- private Map nameCommand = new HashMap<>();
- private Map aliasCommand = new HashMap<>();
- private final Map commandExecute = new HashMap<>();
- private LineReader reader;
- private Exception exception;
-
- public Builtins(Path workDir, ConfigurationPath configPath, Function widgetCreator) {
- this(null, () -> workDir, configPath, widgetCreator);
- }
-
- public Builtins(Set commands, Path workDir, ConfigurationPath configpath, Function widgetCreator) {
- this(commands, () -> workDir, configpath, widgetCreator);
- }
-
- public Builtins(Supplier workDir, ConfigurationPath configPath, Function widgetCreator) {
- this(null, workDir, configPath, widgetCreator);
- }
-
- public Builtins(Set commands, Supplier workDir, ConfigurationPath configpath, Function widgetCreator) {
- this.configPath = configpath;
- this.widgetCreator = widgetCreator;
- this.workDir = workDir;
- Set cmds = new HashSet<>();
- if (commands == null) {
- cmds = new HashSet<>(EnumSet.allOf(Command.class));
- } else {
- cmds = new HashSet<>(commands);
- }
- for (Command c: cmds) {
- commandName.put(c, c.name().toLowerCase());
- }
- doNameCommand();
- commandExecute.put(Command.NANO, new CommandMethods(this::nano, this::nanoCompleter));
- commandExecute.put(Command.LESS, new CommandMethods(this::less, this::lessCompleter));
- commandExecute.put(Command.HISTORY, new CommandMethods(this::history, this::historyCompleter));
- commandExecute.put(Command.WIDGET, new CommandMethods(this::widget, this::widgetCompleter));
- commandExecute.put(Command.KEYMAP, new CommandMethods(this::keymap, this::keymapCompleter));
- commandExecute.put(Command.SETOPT, new CommandMethods(this::setopt, this::setoptCompleter));
- commandExecute.put(Command.SETVAR, new CommandMethods(this::setvar, this::setvarCompleter));
- commandExecute.put(Command.UNSETOPT, new CommandMethods(this::unsetopt, this::unsetoptCompleter));
- commandExecute.put(Command.TTOP, new CommandMethods(this::ttop, this::ttopCompleter));
- }
-
- public Set commandNames() {
- return nameCommand.keySet();
- }
-
- public Map commandAliases() {
- return aliasCommand;
- }
-
- private void doNameCommand() {
- nameCommand = commandName.entrySet()
- .stream()
- .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
- }
-
- public void setLineReader(LineReader reader) {
- this.reader = reader;
- }
-
- public void rename(Command command, String newName) {
- if (nameCommand.containsKey(newName)) {
- throw new IllegalArgumentException("Duplicate command name!");
- } else if (!commandName.containsKey(command)) {
- throw new IllegalArgumentException("Command does not exists!");
- }
- commandName.put(command, newName);
- doNameCommand();
- }
-
- public void alias(String alias, String command) {
- if (!nameCommand.keySet().contains(command)) {
- throw new IllegalArgumentException("Command does not exists!");
- }
- aliasCommand.put(alias, command);
- }
-
- @Override
- public boolean hasCommand(String name) {
- if (nameCommand.containsKey(name) || aliasCommand.containsKey(name)) {
- return true;
- }
- return false;
- }
-
- @Override
- public SystemCompleter compileCompleters() {
- SystemCompleter out = new SystemCompleter();
- for (Map.Entry entry: commandName.entrySet()) {
- out.add(entry.getValue(), commandExecute.get(entry.getKey()).compileCompleter().apply(entry.getValue()));
- }
- out.addAliases(aliasCommand);
- return out;
- }
-
- private Command command(String name) {
- Command out = null;
- if (!hasCommand(name)) {
- throw new IllegalArgumentException("Command does not exists!");
- }
- if (aliasCommand.containsKey(name)) {
- name = aliasCommand.get(name);
- }
- if (nameCommand.containsKey(name)) {
- out = nameCommand.get(name);
- } else {
- throw new IllegalArgumentException("Command does not exists!");
- }
- return out;
- }
-
- @Override
- public Object execute(CommandRegistry.CommandSession session, String command, String[] args) throws Exception {
- exception = null;
- commandExecute.get(command(command)).execute().accept(new CommandInput(args, session));
- if (exception != null) {
- throw exception;
- }
- return null;
- }
-
- private List commandOptions(String command) {
- try {
- execute(new CommandRegistry.CommandSession(), command, new String[] {"--help"});
- } catch (HelpException e) {
- return compileCommandOptions(e.getMessage());
- } catch (Exception e) {
-
- }
- return null;
- }
-
- private void less(CommandInput input) {
- try {
- Commands.less(input.terminal(), input.in(), input.out(), input.err(), workDir.get(), input.args());
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private void nano(CommandInput input) {
- try {
- Commands.nano(input.terminal(), input.out(), input.err(), workDir.get(), input.args(), configPath);
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private void history(CommandInput input) {
- try {
- Commands.history(reader, input.out(), input.err(), workDir.get(), input.args());
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private void widget(CommandInput input) {
- try {
- Commands.widget(reader, input.out(), input.err(), widgetCreator, input.args());
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private void keymap(CommandInput input) {
- try {
- Commands.keymap(reader, input.out(), input.err(), input.args());
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private void setopt(CommandInput input) {
- try {
- Commands.setopt(reader, input.out(), input.err(), input.args());
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private void setvar(CommandInput input) {
- try {
- Commands.setvar(reader, input.out(), input.err(), input.args());
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private void unsetopt(CommandInput input) {
- try {
- Commands.unsetopt(reader, input.out(), input.err(), input.args());
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private void ttop(CommandInput input) {
- try {
- TTop.ttop(input.terminal(), input.out(), input.err(), input.args());
- } catch (Exception e) {
- this.exception = e;
- }
- }
-
- private List unsetOptions(boolean set) {
- List out = new ArrayList<>();
- for (Option option : Option.values()) {
- if (set == (reader.isSet(option) == option.isDef())) {
- out.add((option.isDef() ? "no-" : "") + option.toString().toLowerCase().replace('_', '-'));
- }
- }
- return out;
- }
-
- private Set allWidgets() {
- Set out = new HashSet<>();
- for (String s: reader.getWidgets().keySet()) {
- out.add(s);
- out.add(reader.getWidgets().get(s).toString());
- }
- return out;
- }
-
- private List nanoCompleter(String name) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new OptionCompleter(new FilesCompleter(workDir)
- , this::commandOptions
- , 1)
- ));
- return completers;
- }
-
- private List lessCompleter(String name) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new OptionCompleter(new FilesCompleter(workDir)
- , this::commandOptions
- , 1)
- ));
- return completers;
- }
-
- private List historyCompleter(String name) {
- List completers = new ArrayList<>();
- List optDescs = commandOptions(commandName.get(Command.HISTORY));
- for (OptDesc o : optDescs) {
- if (o.shortOption() != null && (o.shortOption().equals("-A") || o.shortOption().equals("-W")
- || o.shortOption().equals("-R"))) {
- o.setValueCompleter(new FilesCompleter(workDir));
- }
- }
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new OptionCompleter(NullCompleter.INSTANCE
- , optDescs
- , 1)
- ));
- return completers;
- }
-
- private List widgetCompleter(String name) {
- List completers = new ArrayList<>();
- List optDescs = commandOptions(commandName.get(Command.WIDGET));
- Candidate aliasOption = new Candidate("-A", "-A", null, null, null, null, true);
- Iterator i = optDescs.iterator();
- while (i.hasNext()) {
- OptDesc o = i.next();
- if (o.shortOption() != null) {
- if (o.shortOption().equals("-D")) {
- o.setValueCompleter(new StringsCompleter(() -> reader.getWidgets().keySet()));
- } else if (o.shortOption().equals("-A")) {
- aliasOption = new Candidate(o.shortOption(), o.shortOption(), null, o.description(), null, null, true);
- i.remove();
- }
- }
- }
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new OptionCompleter(NullCompleter.INSTANCE
- , optDescs
- , 1)
- ));
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new StringsCompleter(aliasOption), new StringsCompleter(() -> allWidgets())
- , new StringsCompleter(() -> reader.getWidgets().keySet()), NullCompleter.INSTANCE));
- return completers;
- }
-
- private List keymapCompleter(String name) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new OptionCompleter(NullCompleter.INSTANCE
- , this::commandOptions
- , 1)
- ));
- return completers;
- }
-
- private List setvarCompleter(String name) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new StringsCompleter(() -> reader.getVariables().keySet()), NullCompleter.INSTANCE));
- return completers;
- }
-
- private List setoptCompleter(String name) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new StringsCompleter(() -> unsetOptions(true))));
- return completers;
- }
-
- private List unsetoptCompleter(String name) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new StringsCompleter(() -> unsetOptions(false))));
- return completers;
- }
-
- private List ttopCompleter(String name) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE
- , new OptionCompleter(NullCompleter.INSTANCE
- , this::commandOptions
- , 1)
- ));
- return completers;
- }
-
- private static String[] splitToLines(String message) {
- return message.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
- }
-
- private static AttributedString highlightComment(String comment) {
- return HelpException.highlightComment(comment, HelpException.defaultStyle());
- }
-
- public static CmdDesc compileCommandDescription(String helpMessage) {
- List main = new ArrayList<>();
- Map> options = new HashMap<>();
- String[] msg = splitToLines(helpMessage);
- String prevOpt = null;
- boolean mainDone = false;
- boolean start = false;
- for (String s: msg) {
- if (!start) {
- if (s.trim().startsWith("Usage: ")) {
- s = s.split("Usage:")[1];
- start = true;
- } else {
- continue;
- }
- }
- if (s.matches("^\\s+-.*$")) {
- mainDone = true;
- int ind = s.lastIndexOf(" ");
- if (ind > 0) {
- String o = s.substring(0, ind);
- String d = s.substring(ind);
- if (o.trim().length() > 0) {
- prevOpt = o.trim();
- options.put(prevOpt, new ArrayList<>(Arrays.asList(highlightComment(d.trim()))));
- }
- }
- } else if (s.matches("^[\\s]{20}.*$") && prevOpt != null && options.containsKey(prevOpt)) {
- int ind = s.lastIndexOf(" ");
- if (ind > 0) {
- options.get(prevOpt).add(highlightComment(s.substring(ind).trim()));
- }
- } else {
- prevOpt = null;
- }
- if (!mainDone) {
- main.add(HelpException.highlightSyntax(s.trim(), HelpException.defaultStyle()));
- }
- }
- return new CmdDesc(main, ArgDesc.doArgNames(Arrays.asList("")), options);
- }
-
- public static List compileCommandOptions(String helpMessage) {
- List out = new ArrayList<>();
- String[] msg = splitToLines(helpMessage);
- boolean start = false;
- for (String s: msg) {
- if (!start) {
- if (s.trim().startsWith("Usage: ")) {
- s = s.split("Usage:")[1];
- start = true;
- }
- continue;
- }
- if (s.matches("^\\s+-.*$")) {
- int ind = s.lastIndexOf(" ");
- if (ind > 0) {
- String[] op = s.substring(0, ind).trim().split("\\s+");
- String d = s.substring(ind).trim();
- String so = null;
- String lo = null;
- if (op.length == 1) {
- if (op[0].startsWith("--")) {
- lo = op[0];
- } else {
- so = op[0];
- }
- } else {
- so = op[0];
- lo = op[1];
- }
- lo = lo == null ? lo : lo.split("=")[0];
- out.add(new OptDesc(so, lo, d));
- }
- }
- }
- return out;
- }
-
- public static List compileCommandInfo(String helpMessage) {
- List out = new ArrayList<>();
- String[] msg = splitToLines(helpMessage);
- boolean first = true;
- for (String s : msg) {
- if (s.trim().startsWith("Usage: ")) {
- break;
- } else {
- if (first && s.contains(" - ")) {
- out.add(s.substring(s.indexOf(" - ") + 3).trim());
- } else {
- out.add(s.trim());
- }
- first = false;
- }
- }
- return out;
- }
-
- public static class CommandInput {
- String[] args;
- Object[] xargs;
- Terminal terminal;
- InputStream in;
- PrintStream out;
- PrintStream err;
-
- public CommandInput(String[] args, CommandRegistry.CommandSession session) {
- this(args, null, session);
- }
-
- public CommandInput(String[] args, Object[] xargs, CommandRegistry.CommandSession session) {
- this(args, session.terminal(), session.in(), session.out(), session.err());
- if (xargs != null) {
- this.xargs = xargs;
- this.args = new String[xargs.length];
- for (int i = 0; i < xargs.length; i++) {
- this.args[i] = xargs[i] != null ? xargs[i].toString() : "";
- }
- }
- }
-
- public CommandInput(String[] args, Terminal terminal, InputStream in, PrintStream out, PrintStream err) {
- this.args = args;
- this.terminal = terminal;
- this.in = in;
- this.out = out;
- this.err = err;
- }
-
- public String[] args() {
- return args;
- }
-
- public Object[] xargs() {
- return xargs;
- }
-
- public Terminal terminal() {
- return terminal;
- }
-
- public InputStream in() {
- return in;
- }
-
- public PrintStream out() {
- return out;
- }
-
- public PrintStream err() {
- return err;
- }
-
- }
-
- public static class CommandMethods {
- Consumer execute;
- Function executeFunction;
- Function> compileCompleter;
-
- public CommandMethods(Function execute, Function> compileCompleter) {
- this.executeFunction = execute;
- this.compileCompleter = compileCompleter;
- }
-
- public CommandMethods(Consumer execute, Function> compileCompleter) {
- this.execute = execute;
- this.compileCompleter = compileCompleter;
- }
-
- public Consumer execute() {
- return execute;
- }
-
- public Function executeFunction() {
- return executeFunction;
- }
-
- public Function> compileCompleter() {
- return compileCompleter;
- }
- }
-
-}
-
diff --git a/builtins/src/main/java/org/jline/builtins/Commands.java b/builtins/src/main/java/org/jline/builtins/Commands.java
index 69ca2617a..a2bfa189e 100644
--- a/builtins/src/main/java/org/jline/builtins/Commands.java
+++ b/builtins/src/main/java/org/jline/builtins/Commands.java
@@ -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.
@@ -8,6 +8,7 @@
*/
package org.jline.builtins;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@@ -40,13 +41,11 @@
import java.util.function.Supplier;
import org.jline.builtins.Completers.CompletionData;
-import org.jline.builtins.Options;
import org.jline.builtins.Options.HelpException;
import org.jline.builtins.Source.StdInSource;
import org.jline.builtins.Source.URLSource;
import org.jline.keymap.KeyMap;
import org.jline.reader.Binding;
-import org.jline.reader.ConfigurationPath;
import org.jline.reader.Highlighter;
import org.jline.reader.History;
import org.jline.reader.LineReader;
@@ -57,6 +56,7 @@
import org.jline.terminal.Terminal;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
+import org.jline.utils.StyleResolver;
public class Commands {
@@ -1034,4 +1034,181 @@ public static void setvar(LineReader lineReader, PrintStream out, PrintStream er
}
}
+ public static void colors(Terminal terminal, PrintStream out, String[] argv) throws HelpException, IOException {
+ String[] usage = {
+ "colors - view 256-color table",
+ "Usage: colors [OPTIONS]",
+ " -? --help Displays command help",
+ " -c --columns=COLUMNS Number of columns in name table",
+ " -n --name Color name table (default number table)",
+ " -s --small View 16-color table (default 256-color)"
+ };
+ Options opt = Options.compile(usage).parse(argv);
+ if (opt.isSet("help")) {
+ throw new Options.HelpException(opt.usage());
+ }
+ int columns = 256;
+ if (opt.isSet("columns")) {
+ columns = opt.getNumber("columns");
+ }
+ new Colors(terminal, out).printColors(opt.isSet("name"), opt.isSet("small"), columns);
+ }
+
+ private static class Colors {
+ boolean name;
+ private final Terminal terminal;
+ private final PrintStream out;
+ private final List colors = Arrays.asList("black","red","green","yellow","blue","magenta","cyan","white"
+ , "!black","!red","!green","!yellow","!blue","!magenta","!cyan","!white");
+
+ public Colors(Terminal terminal, PrintStream out) {
+ this.terminal = terminal;
+ this.out = out;
+ }
+
+ private String getStyle(String color) {
+ String out;
+ char fg = ' ';
+ if (name) {
+ out = "bg:~" + color.substring(1);
+ fg = color.charAt(0);
+ } else if (color.substring(1).matches("\\d+")) {
+ out = "48;5;" + color.substring(1);
+ fg = color.charAt(0);
+ } else {
+ out = "bg:" + color;
+ }
+ if (color.startsWith("!") || color.equals("white") || fg == 'b') {
+ out += ",fg:black";
+ } else {
+ out += ",fg:!white";
+ }
+ return out;
+ }
+
+ private String foreground(int idx) {
+ String fg = "w";
+ if ((idx > 6 && idx < 16)
+ || (idx > 33 && idx < 52)
+ || (idx > 69 && idx < 88)
+ || (idx > 105 && idx < 124)
+ || (idx > 141 && idx < 160)
+ || (idx > 177 && idx < 196)
+ || (idx > 213 && idx < 232)
+ || idx > 243) {
+ fg = "b";
+ }
+ return fg;
+ }
+
+ private String addPadding(int width, String field) {
+ int s = width - field.length();
+ int left = s/2;
+ StringBuilder lp = new StringBuilder();
+ StringBuilder rp = new StringBuilder();
+ for (int i = 0; i < left; i++) {
+ lp.append(" ");
+ }
+ for (int i = 0; i < s - left; i++) {
+ rp.append(" ");
+ }
+ return lp.toString() + field + rp.toString();
+ }
+
+ public void printColors(boolean name, boolean small, int columns) throws IOException {
+ this.name = name;
+ AttributedStringBuilder asb = new AttributedStringBuilder();
+ int width = terminal.getWidth();
+ String tableName = small ? " 16-color " : "256-color ";
+ if (!name) {
+ out.print(tableName);
+ out.print("table, fg: ");
+ if (!small) {
+ out.print("/ 38;5;");
+ }
+ out.println();
+ out.print(" bg: ");
+ if (!small) {
+ out.print("/ 48;5;");
+ }
+ out.println("\n");
+ boolean narrow = width < 180;
+ for (String c : colors) {
+ AttributedStyle ss = new StyleResolver(this::getStyle).resolve('.' + c, null);
+ asb.style(ss);
+ asb.append(addPadding(11,c));
+ asb.style(AttributedStyle.DEFAULT);
+ if (c.equals("white")) {
+ if (narrow || small) {
+ asb.append('\n');
+ } else {
+ asb.append(" ");
+ }
+ } else if (c.equals("!white")) {
+ asb.append('\n');
+ }
+ }
+ asb.append('\n');
+ if (!small) {
+ for (int i = 16; i < 256; i++) {
+ String fg = foreground(i);
+ String code = Integer.toString(i);
+ AttributedStyle ss = new StyleResolver(this::getStyle).resolve("." + fg + code, null);
+ asb.style(ss);
+ String str = " ";
+ if (i < 100) {
+ str = " ";
+ } else if (i > 231) {
+ str = i % 2 == 0 ? " " : " ";
+ }
+ asb.append(str).append(code).append(' ');
+ if (i == 51 || i == 87 || i == 123 || i == 159 || i == 195 || i == 231
+ || narrow
+ && (i == 33 || i == 69 || i == 105 || i == 141 || i == 177 || i == 213 || i == 243)
+ ) {
+ asb.style(AttributedStyle.DEFAULT);
+ asb.append('\n');
+ if (i == 231) {
+ asb.append('\n');
+ }
+ }
+ }
+ }
+ } else {
+ out.print(tableName);
+ out.println("table, fg:~ OR 38;5;");
+ out.println(" bg:~ OR 48;5;");
+ out.println();
+ InputStream inputStream = new Source.ResourceSource("/org/jline/utils/colors.txt", null).read();
+ BufferedReader reader = new BufferedReader(new java.io.InputStreamReader(inputStream));
+ String line = reader.readLine();
+ int col = 0;
+ Integer idx = 0;
+ int colWidth = 22;
+ while (line != null) {
+ line = line.trim();
+ if (!line.isEmpty() && !line.startsWith("#")) {
+ String fg = foreground(idx);
+ AttributedStyle ss = new StyleResolver(this::getStyle).resolve("." + fg + line, null);
+ asb.style(ss);
+ asb.append(String.valueOf(idx)).append(addPadding(colWidth - idx.toString().length(), line));
+ col++;
+ idx++;
+ if ((col + 1)*colWidth > width || col + 1 > columns) {
+ col = 0;
+ asb.style(AttributedStyle.DEFAULT);
+ asb.append('\n');
+ }
+ }
+ if (small && idx == 16) {
+ break;
+ }
+ line = reader.readLine();
+ }
+ reader.close();
+ }
+ asb.toAttributedString().println(terminal);
+ }
+ }
+
}
diff --git a/builtins/src/main/java/org/jline/builtins/Completers.java b/builtins/src/main/java/org/jline/builtins/Completers.java
index 90063f027..18dcd976b 100644
--- a/builtins/src/main/java/org/jline/builtins/Completers.java
+++ b/builtins/src/main/java/org/jline/builtins/Completers.java
@@ -20,10 +20,8 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
@@ -32,15 +30,14 @@
import org.jline.reader.Candidate;
import org.jline.reader.LineReader;
import org.jline.reader.LineReader.Option;
-import org.jline.reader.impl.completer.AggregateCompleter;
-import org.jline.reader.impl.completer.ArgumentCompleter;
import org.jline.reader.impl.completer.NullCompleter;
import org.jline.reader.impl.completer.StringsCompleter;
import org.jline.reader.ParsedLine;
import org.jline.terminal.Terminal;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
-import org.jline.utils.AttributedStyle;
+import org.jline.utils.OSUtils;
+import org.jline.utils.StyleResolver;
public class Completers {
@@ -330,6 +327,7 @@ protected String getSeparator(boolean useForwardSlash) {
*/
public static class FileNameCompleter implements org.jline.reader.Completer
{
+ protected static StyleResolver resolver = Styles.lsStyle();
public void complete(LineReader reader, ParsedLine commandLine, final List candidates) {
assert commandLine != null;
@@ -363,10 +361,10 @@ public void complete(LineReader reader, ParsedLine commandLine, final List> completers = new HashMap<>();
- private Map aliasCommand = new HashMap<>();
- private StringsCompleter commands;
- private boolean compiled = false;
-
- public SystemCompleter() {}
-
- @Override
- public void complete(LineReader reader, ParsedLine commandLine, List 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 completers) {
- for (org.jline.reader.Completer c : completers) {
- add(command, c);
- }
- }
-
- public void add(List commands, org.jline.reader.Completer completer) {
- for (String c: commands) {
- add(c, completer);
- }
- }
-
- public void add(String command, org.jline.reader.Completer completer) {
- Objects.requireNonNull(command);
- if (compiled) {
- throw new IllegalStateException();
- }
- if (!completers.containsKey(command)) {
- completers.put(command, new ArrayList());
- }
- 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> entry: other.getCompleters().entrySet()) {
- for (org.jline.reader.Completer c: entry.getValue()) {
- add(entry.getKey(), c);
- }
- }
- addAliases(other.getAliases());
- }
-
- public void addAliases(Map aliasCommand) {
- if (compiled) {
- throw new IllegalStateException();
- }
- this.aliasCommand.putAll(aliasCommand);
- }
-
- public Map getAliases() {
- return aliasCommand;
- }
-
- public void compile() {
- if (compiled) {
- return;
- }
- Map> compiledCompleters = new HashMap<>();
- for (Map.Entry> entry: completers.entrySet()) {
- if (entry.getValue().size() == 1) {
- compiledCompleters.put(entry.getKey(), entry.getValue());
- } else {
- compiledCompleters.put(entry.getKey(), new ArrayList());
- compiledCompleters.get(entry.getKey()).add(new AggregateCompleter(entry.getValue()));
- }
- }
- completers = compiledCompleters;
- Set cmds = new HashSet<>(completers.keySet());
- cmds.addAll(aliasCommand.keySet());
- commands = new StringsCompleter(cmds);
- compiled = true;
- }
-
- public Map> getCompleters() {
- return completers;
- }
- }
-
public static class OptDesc {
private String shortOption;
private String longOption;
@@ -705,14 +580,14 @@ protected static List compile(Map> optionValues, Co
for (Map.Entry> entry: optionValues.entrySet()) {
if (entry.getKey().startsWith("--")) {
out.add(new OptDesc(null, entry.getKey(), new StringsCompleter(entry.getValue())));
- } else if (entry.getKey().matches("-[a-zA-Z]{1}")) {
+ } else if (entry.getKey().matches("-[a-zA-Z]")) {
out.add(new OptDesc(entry.getKey(), null, new StringsCompleter(entry.getValue())));
}
}
for (String o: options) {
if (o.startsWith("--")) {
out.add(new OptDesc(null, o));
- } else if (o.matches("-[a-zA-Z]{1}")) {
+ } else if (o.matches("-[a-zA-Z]")) {
out.add(new OptDesc(o, null));
}
}
@@ -815,9 +690,10 @@ protected boolean completeValue(LineReader reader, final ParsedLine commandLine,
if (v.startsWith(partialValue)) {
out = true;
String val = c.value();
- if (valueCompleter instanceof Completers.FilesCompleter
- || valueCompleter instanceof Completers.DirectoriesCompleter) {
- val = FileNameCompleter.getDisplay(reader.getTerminal(), Paths.get(c.value()));
+ if (valueCompleter instanceof FileNameCompleter) {
+ FileNameCompleter cc = (FileNameCompleter)valueCompleter;
+ String sep = cc.getSeparator(reader.isSet(LineReader.Option.USE_FORWARD_SLASH));
+ val = cc.getDisplay(reader.getTerminal(), Paths.get(c.value()), FileNameCompleter.resolver, sep);
}
candidates.add(new Candidate(curBuf + v, val, null, null, null, null, c.complete()));
}
@@ -931,6 +807,10 @@ public OptionCompleter(Collection options, int startPos) {
this.startPos = startPos;
}
+ public void setStartPos(int startPos) {
+ this.startPos = startPos;
+ }
+
@Override
public void complete(LineReader reader, final ParsedLine commandLine, List candidates) {
assert commandLine != null;
@@ -941,12 +821,12 @@ public void complete(LineReader reader, final ParsedLine commandLine, List usedOptions = new ArrayList<>();
for (int i = startPos; i < words.size(); i++) {
@@ -985,11 +865,14 @@ public void complete(LineReader reader, final ParsedLine commandLine, List 1 && shortOptionValueCompleter(command, words.get(words.size() - 2)) != null) {
shortOptionValueCompleter(command, words.get(words.size() - 2)).complete(reader, commandLine, candidates);
+ } else if (words.size() > 1 && longOptionValueCompleter(command, words.get(words.size() - 2)) != null) {
+ longOptionValueCompleter(command, words.get(words.size() - 2)).complete(reader, commandLine, candidates);
} else if (!argsCompleters.isEmpty()) {
int args = -1;
for (int i = startPos; i < words.size(); i++) {
if (!words.get(i).startsWith("-")) {
- if (i > 0 && shortOptionValueCompleter(command, words.get(i - 1)) == null) {
+ if (i > 0 && shortOptionValueCompleter(command, words.get(i - 1)) == null
+ && longOptionValueCompleter(command, words.get(i - 1)) == null) {
args++;
}
}
@@ -1004,6 +887,15 @@ public void complete(LineReader reader, final ParsedLine commandLine, List optDescs = commandOptions == null ? options : commandOptions.apply(command);
+ OptDesc option = findOptDesc(optDescs, opt);
+ return option.hasValue() ? option.valueCompleter() : null;
+ }
+
private org.jline.reader.Completer shortOptionValueCompleter(String command, String opt) {
if (!opt.matches("-[a-zA-Z]+")) {
return null;
@@ -1038,4 +930,18 @@ private OptDesc findOptDesc(Collection optDescs, String opt) {
return new OptDesc();
}
}
+
+ public static class AnyCompleter implements org.jline.reader.Completer {
+ public static final AnyCompleter INSTANCE = new AnyCompleter();
+
+ @Override
+ public void complete(LineReader reader, ParsedLine commandLine, List candidates) {
+ assert commandLine != null;
+ assert candidates != null;
+ String buffer = commandLine.word().substring(0, commandLine.wordCursor());
+ candidates.add(new Candidate(AttributedString.stripAnsi(buffer)
+ , buffer, null, null, null, null, true));
+ }
+ }
+
}
diff --git a/reader/src/main/java/org/jline/reader/ConfigurationPath.java b/builtins/src/main/java/org/jline/builtins/ConfigurationPath.java
similarity index 98%
rename from reader/src/main/java/org/jline/reader/ConfigurationPath.java
rename to builtins/src/main/java/org/jline/builtins/ConfigurationPath.java
index d44c2bb3b..c15d49368 100644
--- a/reader/src/main/java/org/jline/reader/ConfigurationPath.java
+++ b/builtins/src/main/java/org/jline/builtins/ConfigurationPath.java
@@ -6,7 +6,7 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
-package org.jline.reader;
+package org.jline.builtins;
import java.io.IOException;
import java.nio.file.Path;
diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleOptionGetter.java b/builtins/src/main/java/org/jline/builtins/ConsoleOptionGetter.java
new file mode 100644
index 000000000..955736cc2
--- /dev/null
+++ b/builtins/src/main/java/org/jline/builtins/ConsoleOptionGetter.java
@@ -0,0 +1,19 @@
+/*
+ * 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 org.jline.builtins;
+
+public interface ConsoleOptionGetter {
+
+ /**
+ * Return console option value
+ * @param name the option name
+ * @return option value
+ */
+ Object consoleOption(String name);
+}
diff --git a/builtins/src/main/java/org/jline/builtins/Less.java b/builtins/src/main/java/org/jline/builtins/Less.java
index ffee353d8..1d0a8f797 100644
--- a/builtins/src/main/java/org/jline/builtins/Less.java
+++ b/builtins/src/main/java/org/jline/builtins/Less.java
@@ -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.
@@ -22,12 +22,7 @@
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
+import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -38,7 +33,6 @@
import org.jline.builtins.Source.URLSource;
import org.jline.keymap.BindingReader;
import org.jline.keymap.KeyMap;
-import org.jline.reader.ConfigurationPath;
import org.jline.terminal.Attributes;
import org.jline.terminal.Size;
import org.jline.terminal.Terminal;
@@ -73,7 +67,7 @@ public class Less {
public boolean ignoreCaseAlways;
public boolean noKeypad;
public boolean noInit;
- protected List tabs = Arrays.asList(4);
+ protected List tabs = Collections.singletonList(4);
protected String syntaxName;
private String historyLog = null;
@@ -112,11 +106,12 @@ public class Less {
protected final Size size = new Size();
SyntaxHighlighter syntaxHighlighter;
- private List syntaxFiles = new ArrayList<>();
+ private final List syntaxFiles = new ArrayList<>();
private boolean highlight = true;
+ private boolean nanorcIgnoreErrors;
public static String[] usage() {
- final String[] usage = {
+ return new String[]{
"less - file pager",
"Usage: less [OPTIONS] [FILES]",
" -? --help Show help",
@@ -136,7 +131,6 @@ public static String[] usage() {
" --ignorercfiles Don't look at the system's lessrc nor at the user's lessrc.",
" -H --historylog=name Log search strings to file, so they can be retrieved in later sessions"
};
- return usage;
}
public Less(Terminal terminal, Path currentDir) {
@@ -164,7 +158,8 @@ public Less(Terminal terminal, Path currentDir, Options opts, ConfigurationPath
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:/usr/share/nano/*.nanorc");
try {
Files.find(Paths.get("/usr/share/nano"), Integer.MAX_VALUE, (path, f) -> pathMatcher.matches(path))
- .forEach(p -> syntaxFiles.add(p));
+ .forEach(syntaxFiles::add);
+ nanorcIgnoreErrors = true;
} catch (IOException e) {
errorMessage = "Encountered error while reading nanorc files";
}
@@ -202,6 +197,7 @@ public Less(Terminal terminal, Path currentDir, Options opts, ConfigurationPath
}
if (opts.isSet("syntax")) {
syntaxName = opts.get("syntax");
+ nanorcIgnoreErrors = false;
}
if (opts.isSet("no-init")) {
noInit = true;
@@ -233,7 +229,7 @@ private void parseConfig(Path file) throws IOException {
if (parts.get(1).contains("*") || parts.get(1).contains("?")) {
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + parts.get(1));
Files.find(Paths.get(new File(parts.get(1)).getParent()), Integer.MAX_VALUE, (path, f) -> pathMatcher.matches(path))
- .forEach(p -> syntaxFiles.add(p));
+ .forEach(syntaxFiles::add);
} else {
syntaxFiles.add(Paths.get(parts.get(1)));
}
@@ -687,7 +683,7 @@ private void moveToMatch(boolean forward, boolean spanFiles) throws IOException
}
private class LineEditor {
- private int begPos;
+ private final int begPos;
public LineEditor(int begPos) {
this.begPos = begPos;
@@ -841,10 +837,10 @@ private void addFile() throws IOException, InterruptedException {
LineEditor lineEditor = new LineEditor(begPos);
while (true) {
checkInterrupted();
- Operation op = null;
- switch (op=bindingReader.readBinding(fileKeyMap)) {
+ Operation op;
+ switch (op = bindingReader.readBinding(fileKeyMap)) {
case ACCEPT:
- String name = buffer.toString().substring(begPos);
+ String name = buffer.substring(begPos);
addSource(name);
try {
openSource();
@@ -894,8 +890,8 @@ private boolean search() throws IOException, InterruptedException {
LineEditor lineEditor = new LineEditor(begPos);
while (true) {
checkInterrupted();
- Operation op = null;
- switch (op=bindingReader.readBinding(searchKeyMap)) {
+ Operation op;
+ switch (op = bindingReader.readBinding(searchKeyMap)) {
case UP:
buffer.setLength(0);
buffer.append(type);
@@ -910,7 +906,7 @@ private boolean search() throws IOException, InterruptedException {
break;
case ACCEPT:
try {
- String _pattern = buffer.toString().substring(1);
+ String _pattern = buffer.substring(1);
if (type == '&') {
displayPattern = _pattern.length() > 0 ? _pattern : null;
getPattern(true);
@@ -969,7 +965,7 @@ private void help() throws IOException {
try {
openSource();
display(false);
- Operation op = null;
+ Operation op;
do {
checkInterrupted();
op = bindingReader.readBinding(keys, null, false);
@@ -998,7 +994,7 @@ protected void openSource() throws IOException {
reader.close();
wasOpen = true;
}
- boolean open = false;
+ boolean open;
boolean displayMessage = false;
do {
Source source = sources.get(sourceIdx);
@@ -1021,7 +1017,7 @@ protected void openSource() throws IOException {
if (sourceIdx == 0) {
syntaxHighlighter = SyntaxHighlighter.build(syntaxFiles, null, "none");
} else {
- syntaxHighlighter = SyntaxHighlighter.build(syntaxFiles, source.getName(), syntaxName);
+ syntaxHighlighter = SyntaxHighlighter.build(syntaxFiles, source.getName(), syntaxName, nanorcIgnoreErrors);
}
open = true;
if (displayMessage) {
@@ -1277,7 +1273,7 @@ int getStrictPositiveNumberInBuffer(int def) {
}
private Pair nextLine2display(int line, Pattern dpCompiled) throws IOException {
- AttributedString curLine = null;
+ AttributedString curLine;
do {
curLine = getLine(line++);
} while (!toBeDisplayed(curLine, dpCompiled));
@@ -1285,7 +1281,7 @@ private Pair nextLine2display(int line, Pattern dpCom
}
private Pair prevLine2display(int line, Pattern dpCompiled) throws IOException {
- AttributedString curLine = null;
+ AttributedString curLine;
do {
curLine = getLine(line--);
} while (line > 0 && !toBeDisplayed(curLine, dpCompiled));
diff --git a/builtins/src/main/java/org/jline/builtins/Nano.java b/builtins/src/main/java/org/jline/builtins/Nano.java
index 696e99dcc..b5299d161 100644
--- a/builtins/src/main/java/org/jline/builtins/Nano.java
+++ b/builtins/src/main/java/org/jline/builtins/Nano.java
@@ -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.
@@ -20,6 +20,7 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
+import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;
import java.nio.file.Files;
@@ -41,10 +42,10 @@
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
import org.jline.keymap.BindingReader;
import org.jline.keymap.KeyMap;
-import org.jline.reader.ConfigurationPath;
import org.jline.reader.Editor;
import org.jline.terminal.Attributes;
import org.jline.terminal.Attributes.ControlChar;
@@ -78,6 +79,7 @@ public class Nano implements Editor {
protected final Size size;
protected final Path root;
protected final int vsusp;
+ private final List syntaxFiles = new ArrayList<>();
// Keys
protected KeyMap keys;
@@ -131,10 +133,9 @@ public class Nano implements Editor {
protected List cutbuffer = new ArrayList<>();
protected boolean mark = false;
protected boolean highlight = true;
- private List syntaxFiles = new ArrayList<>();
private boolean searchToReplace = false;
-
protected boolean readNewBuffer = true;
+ private boolean nanorcIgnoreErrors;
protected enum WriteMode {
WRITE,
@@ -155,7 +156,7 @@ protected enum CursorMovement {
}
public static String[] usage() {
- final String[] usage = {
+ return new String[]{
"nano - edit files",
"Usage: nano [OPTIONS] [FILES]",
" -? --help Show help",
@@ -184,7 +185,6 @@ public static String[] usage() {
" -E --tabstospaces Convert typed tabs to spaces.",
" -i --autoindent Indent new lines to the previous line's indentation."
};
- return usage;
}
protected class Buffer {
@@ -210,7 +210,7 @@ protected class Buffer {
protected Buffer(String file) {
this.file = file;
- this.syntaxHighlighter = SyntaxHighlighter.build(syntaxFiles, file, syntaxName);
+ this.syntaxHighlighter = SyntaxHighlighter.build(syntaxFiles, file, syntaxName, nanorcIgnoreErrors);
}
void open() throws IOException {
@@ -433,7 +433,7 @@ LinkedList computeOffsets(String line) {
}
boolean isBreakable(char ch) {
- return atBlanks ? ch == ' ' : true;
+ return !atBlanks || ch == ' ';
}
void moveToChar(int pos) {
@@ -480,7 +480,6 @@ boolean backspace(int count) {
lines.remove(line + 1);
offsets.remove(line + 1);
count--;
- dirty = true;
} else {
int nb = Math.min(pos, count);
int curPos = length(text.substring(0, pos - nb));
@@ -489,8 +488,8 @@ boolean backspace(int count) {
offsets.set(line, computeOffsets(text));
moveToChar(curPos);
count -= nb;
- dirty = true;
}
+ dirty = true;
}
ensureCursorVisible();
return true;
@@ -528,7 +527,7 @@ boolean moveRight(int chars, boolean fromBeginning) {
firstColumnToDisplay = 0;
offsetInLine = 0;
column = 0;
- chars = chars <= length(getLine(line)) ? chars : length(getLine(line));
+ chars = Math.min(chars, length(getLine(line)));
}
boolean ret = true;
while (--chars >= 0) {
@@ -758,14 +757,14 @@ List computeHeader() {
int nb = max - p1.length() - "File: ...".length();
int cut;
cut = Math.max(0, Math.min(p0.length(), p0.length() - nb));
- middle = "File: ..." + p0.substring(cut, p0.length()) + p1;
+ middle = "File: ..." + p0.substring(cut) + p1;
}
if (middle == null || middle.length() > max) {
left = null;
max = mend - 2;
int nb = max - "File: ...".length();
int cut = Math.max(0, Math.min(src.length(), src.length() - nb));
- middle = "File: ..." + src.substring(cut, src.length());
+ middle = "File: ..." + src.substring(cut);
if (middle.length() > max) {
middle = middle.substring(0, max);
}
@@ -945,7 +944,7 @@ public void moveTo(int x, int y) {
public void gotoLine(int x, int y) {
line = y < lines.size() ? y : lines.size() - 1;
- x = x <= length(lines.get(line)) ? x : length(lines.get(line));
+ x = Math.min(x, length(lines.get(line)));
firstLineToDisplay = line > 0 ? line - 1 : line;
offsetInLine = 0;
offsetInLineToDisplay = 0;
@@ -1419,30 +1418,48 @@ void replaceFromCursor(int chars, String string) {
}
}
+ /**
+ * Java implementation of nanorc highlighter
+ *
+ * @author Matti Rinta-Nikkola
+ */
public static class SyntaxHighlighter {
- private List rules = new ArrayList<>();
+ private final List rules = new ArrayList<>();
+ private boolean startEndHighlight;
private int ruleStartId = 0;
private SyntaxHighlighter() {}
protected static SyntaxHighlighter build(List syntaxFiles, String file, String syntaxName) {
+ return build(syntaxFiles, file, syntaxName, false);
+ }
+
+ protected static SyntaxHighlighter build(List syntaxFiles, String file, String syntaxName
+ , boolean ignoreErrors) {
SyntaxHighlighter out = new SyntaxHighlighter();
List defaultRules = new ArrayList<>();
- if (syntaxName == null || (syntaxName != null && !syntaxName.equals("none"))) {
- for (Path p: syntaxFiles) {
- NanorcParser parser = new NanorcParser(p, syntaxName, file);
- try {
- parser.parse();
- if (parser.matches()) {
- out.addRules(parser.getHighlightRules());
- return out;
- } else if (parser.isDefault()) {
- defaultRules.addAll(parser.getHighlightRules());
+ try {
+ if (syntaxName == null || (syntaxName != null && !syntaxName.equals("none"))) {
+ for (Path p : syntaxFiles) {
+ try {
+ NanorcParser parser = new NanorcParser(p, syntaxName, file);
+ parser.parse();
+ if (parser.matches()) {
+ out.addRules(parser.getHighlightRules());
+ return out;
+ } else if (parser.isDefault()) {
+ defaultRules.addAll(parser.getHighlightRules());
+ }
+ } catch (IOException e) {
+ // ignore
}
- } catch (IOException e) {
}
+ out.addRules(defaultRules);
+ }
+ } catch (PatternSyntaxException e) {
+ if (!ignoreErrors) {
+ throw e;
}
- out.addRules(defaultRules);
}
return out;
}
@@ -1472,7 +1489,7 @@ public static SyntaxHighlighter build(Path nanorc, String syntaxName) {
Paths.get(new File(parts.get(1)).getParent()),
Integer.MAX_VALUE,
(path, f) -> pathMatcher.matches(path))
- .forEach(p -> syntaxFiles.add(p));
+ .forEach(syntaxFiles::add);
} else {
syntaxFiles.add(Paths.get(parts.get(1)));
}
@@ -1483,6 +1500,31 @@ public static SyntaxHighlighter build(Path nanorc, String syntaxName) {
reader.close();
out = build(syntaxFiles, null, syntaxName);
} catch (Exception e) {
+ // ignore
+ }
+ return out;
+ }
+
+ /**
+ * Build SyntaxHighlighter
+ *
+ * @param nanorcUrl Url of nanorc file
+ * @return SyntaxHighlighter
+ */
+ public static SyntaxHighlighter build(String nanorcUrl) {
+ SyntaxHighlighter out = new SyntaxHighlighter();
+ InputStream inputStream;
+ try {
+ if (nanorcUrl.startsWith("classpath:")) {
+ inputStream = new Source.ResourceSource(nanorcUrl.substring(10), null).read();
+ } else {
+ inputStream = new Source.URLSource(new URL(nanorcUrl), null).read();
+ }
+ NanorcParser parser = new NanorcParser(inputStream, null, null);
+ parser.parse();
+ out.addRules(parser.getHighlightRules());
+ } catch (IOException e) {
+ // ignore
}
return out;
}
@@ -1493,6 +1535,7 @@ private void addRules(List rules) {
public void reset() {
ruleStartId = 0;
+ startEndHighlight = false;
}
public AttributedString highlight(String string) {
@@ -1509,7 +1552,9 @@ public AttributedString highlight(AttributedString line) {
}
AttributedStringBuilder asb = new AttributedStringBuilder();
asb.append(line);
- for (int i = ruleStartId; i < rules.size(); i++) {
+ int startId = ruleStartId;
+ boolean endHighlight = startEndHighlight;
+ for (int i = startId; i < (endHighlight ? startId + 1 : rules.size()); i++) {
HighlightRule rule = rules.get(i);
switch (rule.getType()) {
case PATTERN:
@@ -1521,12 +1566,12 @@ public AttributedString highlight(AttributedString line) {
Matcher end = rule.getEnd().matcher(asb.toAttributedString());
while (!done) {
AttributedStringBuilder a = new AttributedStringBuilder();
- if (ruleStartId == i) { // first rule should never be type
- // START_END or we will fail here!
+ if (startEndHighlight && ruleStartId == i) {
if (end.find()) {
- a.append(asb.columnSubSequence(0, end.end()),rule.getStyle());
+ a.append(asb.columnSubSequence(0, end.end()), rule.getStyle());
a.append(asb.columnSubSequence(end.end(), asb.length()));
ruleStartId = 0;
+ startEndHighlight = false;
} else {
a.append(asb, rule.getStyle());
done = true;
@@ -1540,6 +1585,7 @@ public AttributedString highlight(AttributedString line) {
a.append(asb.columnSubSequence(end.end(), asb.length()));
} else {
ruleStartId = i;
+ startEndHighlight = true;
a.append(asb.columnSubSequence(start.start(),asb.length()), rule.getStyle());
done = true;
}
@@ -1554,13 +1600,14 @@ public AttributedString highlight(AttributedString line) {
}
return asb.toAttributedString();
}
+
}
private static class HighlightRule {
- public enum RuleType {PATTERN, START_END};
- private RuleType type;
+ public enum RuleType {PATTERN, START_END}
+ private final RuleType type;
private Pattern pattern;
- private AttributedStyle style;
+ private final AttributedStyle style;
private Pattern start;
private Pattern end;
@@ -1621,30 +1668,42 @@ public static RuleType evalRuleType(List colorCfg) {
private static class NanorcParser {
private static final String DEFAULT_SYNTAX = "default";
- private File file;
- private String name;
- private String target;
+ private final String name;
+ private final String target;
+ private final List highlightRules = new ArrayList<>();
+ private final BufferedReader reader;
private boolean matches = false;
- private List highlightRules = new ArrayList<>();
private String syntaxName;
- public NanorcParser(Path file, String name) {
- this(file, name, null);
+ public NanorcParser(Path file, String name, String target) throws IOException {
+ this(new Source.PathSource(file, null).read(), name, target);
}
- public NanorcParser(Path file, String name, String target) {
- this.file = file.toFile();
+ public NanorcParser(InputStream in, String name, String target) {
+ this.reader = new BufferedReader(new InputStreamReader(in));
this.name = name;
this.target = target;
}
public void parse() throws IOException {
- BufferedReader reader = new BufferedReader(new FileReader(file));
String line = reader.readLine();
- while (line!= null) {
+ while (line != null) {
line = line.trim();
if (line.length() > 0 && !line.startsWith("#")) {
- line = line.replaceAll("\\\\<", "\\\\b").replaceAll("\\\\>", "\\\\b").replaceAll("\\[\\[:space:\\]\\]", "\\\\s");
+ line = line.replaceAll("\\\\<", "\\\\b")
+ .replaceAll("\\\\>", "\\\\b")
+ .replaceAll("\\[:alnum:]", "\\\\p{Alnum}")
+ .replaceAll("\\[:alpha:]", "\\\\p{Alpha}")
+ .replaceAll("\\[:blank:]", "\\\\p{Blank}")
+ .replaceAll("\\[:cntrl:]", "\\\\p{Cntrl}")
+ .replaceAll("\\[:digit:]", "\\\\p{Digit}")
+ .replaceAll("\\[:graph:]", "\\\\p{Graph}")
+ .replaceAll("\\[:lower:]", "\\\\p{Lower}")
+ .replaceAll("\\[:print:]", "\\\\p{Print}")
+ .replaceAll("\\[:punct:]", "\\\\p{Punct}")
+ .replaceAll("\\[:space:]", "\\\\s")
+ .replaceAll("\\[:upper:]", "\\\\p{Upper}")
+ .replaceAll("\\[:xdigit:]", "\\\\p{XDigit}");
List parts = Parser.split(line);
if (parts.get(0).equals("syntax")) {
syntaxName = parts.get(1);
@@ -1718,42 +1777,63 @@ private Integer toColor(String styleString) {
out += AttributedStyle.MAGENTA;
} else if (styleString.equals("cyan")) {
out += AttributedStyle.CYAN;
+ } else if (styleString.matches("\\d+")) {
+ out = Integer.parseInt(styleString);
}
}
return out;
}
+ private AttributedStyle setStyle(String name, AttributedStyle style) {
+ AttributedStyle out = style;
+ switch (name) {
+ case "blink":
+ out = style.blink();
+ break;
+ case "bold":
+ out = style.bold();
+ break;
+ case "conceal":
+ out = style.conceal();
+ break;
+ case "faint":
+ out = style.faint();
+ break;
+ case "hidden":
+ out = style.hidden();
+ break;
+ case "inverse":
+ out = style.inverse();
+ break;
+ case "italic":
+ out = style.italic();
+ break;
+ case "underline":
+ out = style.underline();
+ break;
+ default:
+ }
+ return out;
+ }
+
private void addHighlightRule(List parts, boolean caseInsensitive) {
AttributedStyle style = AttributedStyle.DEFAULT.foreground(AttributedStyle.BLACK + AttributedStyle.BRIGHT);
String[] styleStrings = parts.get(1).split(",");
- Integer fcolor = toColor(styleStrings[0]);
- Integer bcolor = styleStrings.length > 1 ? toColor(styleStrings[1]) : null;
+ List styles = Arrays.asList("blink", "bold", "conceal", "faint", "hidden", "inverse", "italic", "underline");
+ int colorStart = 0;
+ // GNU nano version >= v5 support styles: bold and italic, rest jline extension
+ while (styles.contains(styleStrings[colorStart])) {
+ style = setStyle(styleStrings[colorStart], style);
+ colorStart++;
+ }
+ Integer fcolor = toColor(styleStrings[colorStart]);
+ Integer bcolor = styleStrings.length > 1 + colorStart ? toColor(styleStrings[colorStart + 1]) : null;
if (fcolor != null) {
style = style.foreground(fcolor);
}
if (bcolor != null) {
style = style.background(bcolor);
}
- // extended nanorc..
- if (styleStrings.length > 2) {
- if (styleStrings[2].equals("blink")) {
- style = style.blink();
- } else if (styleStrings[2].equals("bold")) {
- style = style.bold();
- } else if (styleStrings[2].equals("conceal")) {
- style = style.conceal();
- } else if (styleStrings[2].equals("faint")) {
- style = style.faint();
- } else if (styleStrings[2].equals("hidden")) {
- style = style.hidden();
- } else if (styleStrings[2].equals("inverse")) {
- style = style.inverse();
- } else if (styleStrings[2].equals("italic")) {
- style = style.italic();
- } else if (styleStrings[2].equals("underline")) {
- style = style.underline();
- }
- }
if (HighlightRule.evalRuleType(parts) == HighlightRule.RuleType.PATTERN) {
for (int i = 2; i < parts.size(); i++) {
@@ -1777,7 +1857,7 @@ private Pattern doPattern(String regex, boolean caseInsensitive) {
protected static class Parser {
protected static List split(String s) {
- List out = new ArrayList();
+ List out = new ArrayList<>();
if (s.length() == 0) {
return out;
}
@@ -1786,13 +1866,20 @@ protected static List split(String s) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '"') {
- depth = depth == 0 ? 1 : 0;
- } else if (c ==' ' && depth == 0 && sb.length() > 0) {
+ if (depth == 0) {
+ depth = 1;
+ } else {
+ char nextChar = i < s.length() - 1 ? s.charAt(i + 1) : ' ';
+ if (nextChar == ' ') {
+ depth = 0;
+ }
+ }
+ } else if (c == ' ' && depth == 0 && sb.length() > 0) {
out.add(stripQuotes(sb.toString()));
sb = new StringBuilder();
continue;
}
- if (sb.length() > 0 || (c!=' ' && c!='\t')) {
+ if (sb.length() > 0 || (c != ' ' && c != '\t')) {
sb.append(c);
}
}
@@ -1812,8 +1899,8 @@ private static String stripQuotes(String s) {
}
protected static class PatternHistory {
- private Path historyFile;
- private int size = 100;
+ private final Path historyFile;
+ private final int size = 100;
private List patterns = new ArrayList<>();
private int patternId = -1;
private boolean lastMoveUp = false;
@@ -1881,9 +1968,7 @@ public void add(String pattern) {
if (pattern.trim().length() == 0) {
return;
}
- if (patterns.contains(pattern)) {
- patterns.remove(pattern);
- }
+ patterns.remove(pattern);
if (patterns.size() > size) {
patterns.remove(patterns.size() - 1);
}
@@ -1906,6 +1991,7 @@ public void persist() {
}
}
} catch (Exception e) {
+ // ignore
}
}
@@ -1922,6 +2008,7 @@ private void load() {
}
}
} catch (Exception e) {
+ // ignore
}
}
@@ -1952,7 +2039,7 @@ public Nano(Terminal terminal, Path root, Options opts, ConfigurationPath config
terminal.setAttributes(attrs);
}
Path nanorc = configPath != null ? configPath.getConfig("jnanorc") : null;
- boolean ignorercfiles = opts!=null && opts.isSet("ignorercfiles");
+ boolean ignorercfiles = opts != null && opts.isSet("ignorercfiles");
if (nanorc != null && !ignorercfiles) {
try {
parseConfig(nanorc);
@@ -1963,14 +2050,19 @@ public Nano(Terminal terminal, Path root, Options opts, ConfigurationPath config
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:/usr/share/nano/*.nanorc");
try {
Files.find(Paths.get("/usr/share/nano"), Integer.MAX_VALUE, (path, f) -> pathMatcher.matches(path))
- .forEach(p -> syntaxFiles.add(p));
+ .forEach(syntaxFiles::add);
+ nanorcIgnoreErrors = true;
} catch (IOException e) {
errorMessage = "Encountered error while reading nanorc files";
}
}
if (opts != null) {
this.restricted = opts.isSet("restricted");
- this.syntaxName = opts.isSet("syntax") ? opts.get("syntax") : null;
+ this.syntaxName = null;
+ if (opts.isSet("syntax")) {
+ this.syntaxName = opts.get("syntax");
+ nanorcIgnoreErrors = false;
+ }
if (opts.isSet("backup")) {
writeBackup = true;
}
@@ -2047,7 +2139,7 @@ private void parseConfig(Path file) throws IOException {
if (parts.get(1).contains("*") || parts.get(1).contains("?")) {
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + parts.get(1));
Files.find(Paths.get(new File(parts.get(1)).getParent()), Integer.MAX_VALUE, (path, f) -> pathMatcher.matches(path))
- .forEach(p -> syntaxFiles.add(p));
+ .forEach(syntaxFiles::add);
} else {
syntaxFiles.add(Paths.get(parts.get(1)));
}
@@ -2976,7 +3068,6 @@ void searchAndReplace() {
int col = searchBackwards ? buffer.length(buffer.getLine(re[0])) - re[1] : re[1];
int match = re[0]*10000 + col;
if (matches.contains(match)) {
- found = false;
break;
} else {
matches.add(match);
@@ -3008,7 +3099,7 @@ void searchAndReplace() {
}
message = "Replaced " + replaced + " occurrences";
} catch (Exception e) {
- return;
+ // ignore
} finally {
searchToReplace = false;
matchedLength = -1;
diff --git a/builtins/src/main/java/org/jline/builtins/Options.java b/builtins/src/main/java/org/jline/builtins/Options.java
index 499b79b0e..a51266739 100644
--- a/builtins/src/main/java/org/jline/builtins/Options.java
+++ b/builtins/src/main/java/org/jline/builtins/Options.java
@@ -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.
@@ -38,7 +38,6 @@
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
@@ -525,33 +524,25 @@ public HelpException(String message) {
super(message);
}
- public static final String DEFAULT_COLORS = "ti=1;34:co=1:ar=3:op=33";
-
public static StyleResolver defaultStyle() {
- return style(DEFAULT_COLORS);
- }
-
- public static StyleResolver style(String str) {
- Map colors = Arrays.stream(str.split(":"))
- .collect(Collectors.toMap(s -> s.substring(0, s.indexOf('=')),
- s -> s.substring(s.indexOf('=') + 1)));
- return new StyleResolver(colors::get);
+ return Styles.helpStyle();
}
- public static AttributedString highlight(String msg, StyleResolver resolver) {
- Matcher tm = Pattern.compile("(^|\\n)(Usage:)").matcher(msg);
+ public static AttributedString highlight(String msg, StyleResolver resolver) {
+ Matcher tm = Pattern.compile("(^|\\n)(Usage|Summary)(:)").matcher(msg);
if (tm.find()) {
+ boolean subcommand = tm.group(2).equals("Summary");
AttributedStringBuilder asb = new AttributedStringBuilder(msg.length());
// Command
AttributedStringBuilder acommand = new AttributedStringBuilder()
.append(msg.substring(0, tm.start(2)))
- .styleMatches(Pattern.compile("(?:^\\s*)([a-z]+[a-z-]*){1}\\b"),
+ .styleMatches(Pattern.compile("(?:^\\s*)([a-z]+[a-zA-Z0-9-]*)\\b"),
Collections.singletonList(resolver.resolve(".co")));
asb.append(acommand);
// Title
- asb.styled(resolver.resolve(".ti"), "Usage").append(":");
+ asb.styled(resolver.resolve(".ti"), tm.group(2)).append(":");
// Syntax
- for (String line : msg.substring(tm.end(2)).split("\n")) {
+ for (String line : msg.substring(tm.end(3)).split("\n")) {
int ind = line.lastIndexOf(" ");
String syntax, comment;
if (ind > 20) {
@@ -562,7 +553,7 @@ public static AttributedString highlight(String msg, StyleResolver resolver) {
comment = "";
}
- asb.append(_highlightSyntax(syntax, resolver));
+ asb.append(_highlightSyntax(syntax, resolver, subcommand));
asb.append(_highlightComment(comment, resolver));
asb.append("\n");
}
@@ -572,15 +563,19 @@ public static AttributedString highlight(String msg, StyleResolver resolver) {
}
}
+ public static AttributedString highlightSyntax(String syntax, StyleResolver resolver, boolean subcommands) {
+ return _highlightSyntax(syntax, resolver, subcommands).toAttributedString();
+ }
+
public static AttributedString highlightSyntax(String syntax, StyleResolver resolver) {
- return _highlightSyntax(syntax, resolver).toAttributedString();
+ return _highlightSyntax(syntax, resolver, false).toAttributedString();
}
public static AttributedString highlightComment(String comment, StyleResolver resolver) {
return _highlightComment(comment, resolver).toAttributedString();
}
- private static AttributedStringBuilder _highlightSyntax(String syntax, StyleResolver resolver) {
+ private static AttributedStringBuilder _highlightSyntax(String syntax, StyleResolver resolver, boolean subcommand) {
StringBuilder indent = new StringBuilder();
for (char c : syntax.toCharArray()) {
if (c != ' ') {
@@ -590,24 +585,26 @@ private static AttributedStringBuilder _highlightSyntax(String syntax, StyleReso
}
AttributedStringBuilder asyntax = new AttributedStringBuilder().append(syntax.substring(indent.length()));
// command
- asyntax.styleMatches(Pattern.compile("(?:^)([a-z]+[a-z-]*){1}\\b"),
+ asyntax.styleMatches(Pattern.compile("(?:^)([a-z]+[a-zA-Z0-9-]*)\\b"),
Collections.singletonList(resolver.resolve(".co")));
- // argument
- asyntax.styleMatches(Pattern.compile("(?:<|\\[|\\s|=)([A-Za-z]+[A-Za-z_-]*){1}\\b"),
- Collections.singletonList(resolver.resolve(".ar")));
- // option
- asyntax.styleMatches(Pattern.compile("(?:^|\\s|\\[)(-\\$|-\\?|[-]{1,2}[A-Za-z-]+\\b){1}"),
- Collections.singletonList(resolver.resolve(".op")));
+ if (!subcommand) {
+ // argument
+ asyntax.styleMatches(Pattern.compile("(?:<|\\[|\\s|=)([A-Za-z]+[A-Za-z_-]*)\\b"),
+ Collections.singletonList(resolver.resolve(".ar")));
+ // option
+ asyntax.styleMatches(Pattern.compile("(?:^|\\s|\\[)(-\\$|-\\?|[-]{1,2}[A-Za-z-]+\\b)"),
+ Collections.singletonList(resolver.resolve(".op")));
+ }
return new AttributedStringBuilder().append(indent).append(asyntax);
}
private static AttributedStringBuilder _highlightComment(String comment, StyleResolver resolver) {
AttributedStringBuilder acomment = new AttributedStringBuilder().append(comment);
// option
- acomment.styleMatches(Pattern.compile("(?:\\s|\\[)(-\\$|-\\?|[-]{1,2}[A-Za-z-]+\\b){1}"),
+ acomment.styleMatches(Pattern.compile("(?:\\s|\\[)(-\\$|-\\?|[-]{1,2}[A-Za-z-]+\\b)"),
Collections.singletonList(resolver.resolve(".op")));
// argument in comment
- acomment.styleMatches(Pattern.compile("(?:\\s)([a-z]+[-]+[a-z]+|[A-Z_]{2,}){1}(?:\\s)"),
+ acomment.styleMatches(Pattern.compile("(?:\\s)([a-z]+[-]+[a-z]+|[A-Z_]{2,})(?:\\s)"),
Collections.singletonList(resolver.resolve(".ar")));
return acomment;
diff --git a/builtins/src/main/java/org/jline/builtins/Styles.java b/builtins/src/main/java/org/jline/builtins/Styles.java
new file mode 100644
index 000000000..249c8b719
--- /dev/null
+++ b/builtins/src/main/java/org/jline/builtins/Styles.java
@@ -0,0 +1,81 @@
+/*
+ * 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 org.jline.builtins;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.jline.utils.StyleResolver;
+
+public class Styles {
+ private static final String DEFAULT_LS_COLORS = "di=1;91:ex=1;92:ln=1;96:fi=";
+ private static final String DEFAULT_HELP_COLORS = "ti=1;34:co=1:ar=3:op=33";
+ private static final String DEFAULT_PRNT_COLORS = "th=1;34:rn=1;34:mk=1;34:em=31:vs=32";
+ private static final String LS_COLORS = "LS_COLORS";
+ private static final String HELP_COLORS = "HELP_COLORS";
+ private static final String PRNT_COLORS = "PRNT_COLORS";
+
+ private static final String KEY = "([a-z]{2}|\\*\\.[a-zA-Z0-9]+)";
+ private static final String VALUE = "[0-9]*(;[0-9]+){0,2}";
+ private static final String ANSI_STYLE_PATTERN = KEY + "=" + VALUE + "(:" + KEY + "=" + VALUE + ")*(:|)";
+
+ public static StyleResolver lsStyle() {
+ return style(LS_COLORS, DEFAULT_LS_COLORS);
+ }
+
+ public static StyleResolver helpStyle() {
+ return style(HELP_COLORS, DEFAULT_HELP_COLORS);
+ }
+
+ public static StyleResolver prntStyle() {
+ return style(PRNT_COLORS, DEFAULT_PRNT_COLORS);
+ }
+
+ public static boolean isAnsiStylePattern(String style) {
+ return style.matches(ANSI_STYLE_PATTERN);
+ }
+
+ private static StyleResolver style(String name, String defStyle) {
+ String style = consoleOption(name);
+ if (style == null) {
+ style = defStyle;
+ }
+ return style(style);
+ }
+
+ private static String consoleOption(String name) {
+ String out = null;
+ try {
+ ConsoleOptionGetter cog = (ConsoleOptionGetter)Class.forName("org.jline.console.SystemRegistry")
+ .getDeclaredMethod("get").invoke(null);
+ if (cog != null) {
+ out = (String)cog.consoleOption(name);
+ if (out != null && !out.matches(ANSI_STYLE_PATTERN)) {
+ out = null;
+ }
+ }
+ } catch (Exception e) {
+ }
+ if (out == null) {
+ out = System.getenv(name);
+ if (out != null && !out.matches(ANSI_STYLE_PATTERN)) {
+ out = null;
+ }
+ }
+ return out;
+ }
+
+ private static StyleResolver style(String style) {
+ Map colors = Arrays.stream(style.split(":"))
+ .collect(Collectors.toMap(s -> s.substring(0, s.indexOf('=')),
+ s -> s.substring(s.indexOf('=') + 1)));
+ return new StyleResolver(colors::get);
+ }
+}
diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java
deleted file mode 100644
index 84ad8ca90..000000000
--- a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java
+++ /dev/null
@@ -1,1371 +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 org.jline.builtins;
-
-import static org.jline.keymap.KeyMap.ctrl;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.PrintStream;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-import org.jline.builtins.Completers.OptDesc;
-import org.jline.builtins.Completers.OptionCompleter;
-import org.jline.builtins.CommandRegistry;
-import org.jline.builtins.ConsoleEngine.ExecutionResult;
-import org.jline.builtins.Widgets;
-import org.jline.builtins.Builtins.CommandMethods;
-import org.jline.builtins.Options.HelpException;
-import org.jline.reader.Completer;
-import org.jline.reader.ConfigurationPath;
-import org.jline.reader.EndOfFileException;
-import org.jline.reader.ParsedLine;
-import org.jline.reader.Parser;
-import org.jline.reader.Parser.ParseContext;
-import org.jline.reader.impl.completer.AggregateCompleter;
-import org.jline.reader.impl.completer.ArgumentCompleter;
-import org.jline.reader.impl.completer.NullCompleter;
-import org.jline.reader.impl.completer.StringsCompleter;
-import org.jline.terminal.Attributes;
-import org.jline.terminal.Attributes.InputFlag;
-import org.jline.terminal.Terminal;
-import org.jline.terminal.TerminalBuilder;
-import org.jline.utils.AttributedStringBuilder;
-import org.jline.utils.AttributedStyle;
-import org.jline.utils.OSUtils;
-
-/**
- * Aggregate command registeries.
- *
- * @author Matti Rinta-Nikkola
- */
-public class SystemRegistryImpl implements SystemRegistry {
- public enum Command {
- EXIT, HELP
- };
-
- public enum Pipe {
- FLIP, NAMED, AND, OR
- }
-
- private static final Class>[] BUILTIN_REGISTERIES = { Builtins.class, ConsoleEngineImpl.class };
- private CommandRegistry[] commandRegistries;
- private Integer consoleId;
- private Parser parser;
- private ConfigurationPath configPath;
- private Map commandName = new HashMap<>();
- private Map nameCommand = new HashMap<>();
- private Map aliasCommand = new HashMap<>();
- private Map pipeName = new HashMap<>();
- private final Map commandExecute = new HashMap<>();
- private Map> commandInfos = new HashMap<>();
- private Exception exception;
- private CommandOutputStream outputStream;
- private ScriptStore scriptStore = new ScriptStore();
-
- public SystemRegistryImpl(Parser parser, Terminal terminal, ConfigurationPath configPath) {
- this.parser = parser;
- this.configPath = configPath;
- outputStream = new CommandOutputStream(terminal);
- Set cmds = new HashSet<>(EnumSet.allOf(Command.class));
- for (Command c : cmds) {
- commandName.put(c, c.name().toLowerCase());
- }
- doNameCommand();
- pipeName.put(Pipe.FLIP, "|;");
- pipeName.put(Pipe.NAMED, "|");
- pipeName.put(Pipe.AND, "&&");
- pipeName.put(Pipe.OR, "||");
- commandExecute.put(Command.EXIT, new CommandMethods(this::exit, this::exitCompleter));
- commandExecute.put(Command.HELP, new CommandMethods(this::help, this::helpCompleter));
- }
-
- private void doNameCommand() {
- nameCommand = commandName.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
- }
-
- public void rename(Pipe pipe, String name) {
- if (name.matches("/w+") || pipeName.containsValue(name)) {
- throw new IllegalArgumentException();
- }
- pipeName.put(pipe, name);
- }
-
- @Override
- public Collection getPipeNames() {
- return pipeName.values();
- }
-
- @Override
- public void setCommandRegistries(CommandRegistry... commandRegistries) {
- this.commandRegistries = commandRegistries;
- for (int i = 0; i < commandRegistries.length; i++) {
- if (commandRegistries[i] instanceof ConsoleEngine) {
- if (consoleId != null) {
- throw new IllegalArgumentException();
- } else {
- this.consoleId = i;
- ((ConsoleEngine) commandRegistries[i]).setSystemRegistry(this);
- this.scriptStore = new ScriptStore((ConsoleEngine)commandRegistries[i]);
- }
- } else if (commandRegistries[i] instanceof SystemRegistry) {
- throw new IllegalArgumentException();
- }
- }
- SystemRegistry.add(this);
- }
-
- @Override
- public void initialize(File script) {
- if (consoleId != null) {
- try {
- consoleEngine().execute(script);
- } catch (Exception e) {
- trace(e);
- }
- }
- }
-
- @Override
- public Set commandNames() {
- Set out = new HashSet<>();
- for (CommandRegistry r : commandRegistries) {
- out.addAll(r.commandNames());
- }
- out.addAll(localCommandNames());
- return out;
- }
-
- private Set localCommandNames() {
- return nameCommand.keySet();
- }
-
- @Override
- public Map commandAliases() {
- Map out = new HashMap<>();
- for (CommandRegistry r : commandRegistries) {
- out.putAll(r.commandAliases());
- }
- out.putAll(aliasCommand);
- return out;
- }
-
- private Command command(String name) {
- Command out = null;
- if (!isLocalCommand(name)) {
- throw new IllegalArgumentException("Command does not exists!");
- }
- if (aliasCommand.containsKey(name)) {
- name = aliasCommand.get(name);
- }
- if (nameCommand.containsKey(name)) {
- out = nameCommand.get(name);
- } else {
- throw new IllegalArgumentException("Command does not exists!");
- }
- return out;
- }
-
- private List localCommandInfo(String command) {
- try {
- localExecute(command, new String[] { "--help" });
- } catch (HelpException e) {
- exception = null;
- return Builtins.compileCommandInfo(e.getMessage());
- } catch (Exception e) {
- trace(e);
- }
- return new ArrayList<>();
- }
-
- @Override
- public List commandInfo(String command) {
- int id = registryId(command);
- List out = new ArrayList<>();
- if (id > -1) {
- if (!commandInfos.containsKey(command)) {
- commandInfos.put(command, commandRegistries[id].commandInfo(command));
- }
- out = commandInfos.get(command);
- } else if (scriptStore.hasScript(command)) {
- out = consoleEngine().commandInfo(command);
- } else if (isLocalCommand(command)) {
- out = localCommandInfo(command);
- }
- return out;
- }
-
- @Override
- public boolean hasCommand(String command) {
- return registryId(command) > -1 || isLocalCommand(command);
- }
-
- private boolean isLocalCommand(String command) {
- return nameCommand.containsKey(command) || aliasCommand.containsKey(command);
- }
-
- private boolean isCommandOrScript(String command) {
- if (hasCommand(command)) {
- return true;
- }
- return scriptStore.hasScript(command);
- }
-
- @Override
- public Completers.SystemCompleter compileCompleters() {
- Completers.SystemCompleter out = CommandRegistry.aggregateCompleters(commandRegistries);
- Completers.SystemCompleter local = new Completers.SystemCompleter();
- for (Map.Entry entry : commandName.entrySet()) {
- local.add(entry.getValue(), commandExecute.get(entry.getKey()).compileCompleter().apply(entry.getValue()));
- }
- local.addAliases(aliasCommand);
- out.add(local);
- out.compile();
- return out;
- }
-
- @Override
- public Completer completer() {
- List completers = new ArrayList<>();
- completers.add(compileCompleters());
- if (consoleId != null) {
- completers.addAll(consoleEngine().scriptCompleters());
- }
- return new AggregateCompleter(completers);
- }
-
- private Widgets.CmdDesc localCommandDescription(String command) {
- if (!isLocalCommand(command)) {
- throw new IllegalArgumentException();
- }
- try {
- localExecute(command, new String[] { "--help" });
- } catch (HelpException e) {
- exception = null;
- return Builtins.compileCommandDescription(e.getMessage());
- } catch (Exception e) {
- trace(e);
- }
- return null;
- }
-
- @Override
- public Widgets.CmdDesc commandDescription(String command) {
- Widgets.CmdDesc out = new Widgets.CmdDesc(false);
- int id = registryId(command);
- if (id > -1) {
- out = commandRegistries[id].commandDescription(command);
- } else if (scriptStore.hasScript(command)) {
- out = consoleEngine().commandDescription(command);
- } else if (isLocalCommand(command)) {
- out = localCommandDescription(command);
- }
- return out;
- }
-
- @Override
- public Widgets.CmdDesc commandDescription(Widgets.CmdLine line) {
- Widgets.CmdDesc out = null;
- switch (line.getDescriptionType()) {
- case COMMAND:
- String cmd = parser.getCommand(line.getArgs().get(0));
- if (isCommandOrScript(cmd) && !hasPipes(line.getArgs())) {
- out = commandDescription(cmd);
- }
- break;
- case METHOD:
- case SYNTAX:
- // TODO
- break;
- }
- return out;
- }
-
- @Override
- public Object invoke(String command, Object... args) throws Exception {
- Object out = null;
- command = ConsoleEngine.plainCommand(command);
- int id = registryId(command);
- if (id > -1) {
- out = commandRegistries[id].invoke(commandSession(), command, args);
- } else if (isLocalCommand(command)) {
- String[] _args = new String[args.length];
- for (int i = 0; i < args.length; i++) {
- if (!(args[i] instanceof String)) {
- throw new IllegalArgumentException();
- }
- _args[i] = args[i].toString();
- }
- out = localExecute(command, _args);
- } else if (consoleId != null) {
- out = consoleEngine().invoke(commandSession(), command, args);
- }
- return out;
- }
-
- @Override
- public Object execute(String command, String[] args) throws Exception {
- Object out = null;
- int id = registryId(command);
- if (id > -1) {
- out = commandRegistries[id].execute(commandSession(), command, args);
- } else if (isLocalCommand(command)) {
- out = localExecute(command, args);
- }
- return out;
- }
-
- public Object localExecute(String command, String[] args) throws Exception {
- if (!isLocalCommand(command)) {
- throw new IllegalArgumentException();
- }
- Object out = commandExecute.get(command(command)).executeFunction()
- .apply(new Builtins.CommandInput(args, commandSession()));
- if (exception != null) {
- throw exception;
- }
- return out;
- }
-
- public Terminal terminal() {
- return commandSession().terminal();
- }
-
- private CommandSession commandSession() {
- return outputStream.getCommandSession();
- }
-
- private static class CommandOutputStream {
- private PrintStream origOut;
- private PrintStream origErr;
- private Terminal origTerminal;
- private ByteArrayOutputStream byteOutputStream;
- private FileOutputStream fileOutputStream;
- private PrintStream out;
- private InputStream in;
- private Terminal terminal;
- private String output;
- private CommandRegistry.CommandSession commandSession;
- private boolean redirecting = false;
-
- public CommandOutputStream(Terminal terminal) {
- this.origOut = System.out;
- this.origErr = System.err;
- this.origTerminal = terminal;
- this.terminal = terminal;
- PrintStream ps = new PrintStream(terminal.output());
- this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), ps, ps);
- }
-
- public void redirect() throws IOException {
- byteOutputStream = new ByteArrayOutputStream();
- }
-
- public void redirect(File file, boolean append) throws IOException {
- if (!file.exists()){
- try {
- file.createNewFile();
- } catch(IOException e){
- (new File(file.getParent())).mkdirs();
- file.createNewFile();
- }
- }
- fileOutputStream = new FileOutputStream(file, append);
- }
-
- public void open() throws IOException {
- if (redirecting || (byteOutputStream == null && fileOutputStream == null)) {
- return;
- }
- OutputStream outputStream = byteOutputStream != null ? byteOutputStream : fileOutputStream;
- out = new PrintStream(outputStream);
- System.setOut(out);
- System.setErr(out);
- String input = ctrl('X') + "q";
- in = new ByteArrayInputStream( input.getBytes() );
- Attributes attrs = new Attributes();
- if (OSUtils.IS_WINDOWS) {
- attrs.setInputFlag(InputFlag.IGNCR, true);
- }
- terminal = TerminalBuilder.builder()
- .streams(in, outputStream)
- .attributes(attrs)
- .type(Terminal.TYPE_DUMB).build();
- this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), out, out);
- redirecting = true;
- }
-
- public void flush() {
- if (out == null) {
- return;
- }
- try {
- out.flush();
- if (byteOutputStream != null) {
- byteOutputStream.flush();
- output = byteOutputStream.toString();
- } else if (fileOutputStream != null) {
- fileOutputStream.flush();
- }
- } catch (Exception e) {
-
- }
- }
-
- public void close() {
- if (out == null) {
- return;
- }
- try {
- in.close();
- flush();
- if (byteOutputStream != null) {
- byteOutputStream.close();
- byteOutputStream = null;
- } else if (fileOutputStream != null) {
- fileOutputStream.close();
- fileOutputStream = null;
- }
- out.close();
- out = null;
- } catch (Exception e) {
-
- }
- }
-
- public CommandRegistry.CommandSession getCommandSession() {
- return commandSession;
- }
-
- public String getOutput() {
- return output;
- }
-
- public boolean isRedirecting() {
- return redirecting;
- }
-
- public boolean isByteStream() {
- return redirecting && byteOutputStream != null;
- }
-
- public void reset() {
- if (redirecting) {
- out = null;
- byteOutputStream = null;
- fileOutputStream = null;
- output = null;
- System.setOut(origOut);
- System.setErr(origErr);
- terminal = null;
- terminal = origTerminal;
- PrintStream ps = new PrintStream(terminal.output());
- this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), ps, ps);
- redirecting = false;
- }
- }
-
- public void closeAndReset() {
- close();
- reset();
- }
- }
-
- private boolean isPipe(String arg) {
- Map> customPipes = consoleId != null ? consoleEngine().getPipes() : new HashMap<>();
- return isPipe(arg, customPipes.keySet());
- }
-
- private boolean hasPipes(Collection args) {
- Map> customPipes = consoleId != null ? consoleEngine().getPipes() : new HashMap<>();
- for (String a : args) {
- if (isPipe(a, customPipes.keySet()) || a.contains(">") || a.contains(">>")) {
- return true;
- }
- }
- return false;
- }
-
- private boolean isPipe(String arg, Set pipes) {
- return pipeName.containsValue(arg) || pipes.contains(arg);
- }
-
- private boolean isCommandAlias(String command) {
- if (consoleId == null || !parser.validCommandName(command) || !consoleEngine().hasAlias(command)) {
- return false;
- }
- String value = consoleEngine().getAlias(command).split("\\s+")[0];
- return !isPipe(value);
- }
-
- private String replaceCommandAlias(String variable, String command, String rawLine) {
- return variable == null ? rawLine.replaceFirst(command + "(\\b|$)", consoleEngine().getAlias(command))
- : rawLine.replaceFirst("=" + command + "(\\b|$)", "=" + consoleEngine().getAlias(command));
- }
-
- private List compileCommandLine(String commandLine) {
- List out = new ArrayList<>();
- ArgsParser ap = new ArgsParser();
- ap.parse(parser, commandLine);
- //
- // manage pipe aliases
- //
- List ws = ap.args();
- Map> customPipes = consoleId != null ? consoleEngine().getPipes() : new HashMap<>();
- if (consoleId != null && ws.contains(pipeName.get(Pipe.NAMED))) {
- StringBuilder sb = new StringBuilder();
- boolean trace = false;
- for (int i = 0 ; i < ws.size(); i++) {
- if (ws.get(i).equals(pipeName.get(Pipe.NAMED))) {
- if (i + 1 < ws.size() && consoleEngine().hasAlias(ws.get(i + 1))) {
- trace = true;
- List args = new ArrayList<>();
- String pipeAlias = consoleEngine().getAlias(ws.get(++i));
- while (i < ws.size() - 1 && !isPipe(ws.get(i + 1), customPipes.keySet())) {
- args.add(ws.get(++i));
- }
- for (int j = 0; j < args.size(); j++) {
- pipeAlias = pipeAlias.replaceAll("\\s\\$" + j + "\\b", " " + args.get(j));
- pipeAlias = pipeAlias.replaceAll("\\$\\{" + j + "(|:-.*)\\}", args.get(j));
- }
- pipeAlias = pipeAlias.replaceAll("\\s+\\$\\d\\b", "");
- pipeAlias = pipeAlias.replaceAll("\\s+\\$\\{\\d+\\}", "");
- pipeAlias = pipeAlias.replaceAll("\\$\\{\\d+\\}", "");
- Matcher matcher = Pattern.compile("\\$\\{\\d+:-(.*?)\\}").matcher(pipeAlias);
- if (matcher.find()) {
- pipeAlias = matcher.replaceAll("$1");
- }
- sb.append(pipeAlias);
- } else {
- sb.append(ws.get(i));
- }
- } else {
- sb.append(ws.get(i));
- }
- sb.append(" ");
- }
- ap.parse(parser, sb.toString());
- if (trace) {
- consoleEngine().trace(ap.line());
- }
- }
- List words = ap.args();
- String nextRawLine = ap.line();
- int first = 0;
- int last = words.size();
- List pipes = new ArrayList<>();
- String pipeSource = null;
- String rawLine = null;
- String pipeResult = null;
- if (!hasPipes(words)) {
- if (isCommandAlias(ap.command())) {
- nextRawLine = replaceCommandAlias(ap.variable(), ap.command(), nextRawLine);
- }
- out.add(new CommandData(parser, false, nextRawLine, ap.variable(), null, false,""));
- } else {
- //
- // compile pipe line
- //
- do {
- String rawCommand = parser.getCommand(words.get(first));
- String command = ConsoleEngine.plainCommand(rawCommand);
- String variable = parser.getVariable(words.get(first));
- if (isCommandAlias(command)) {
- ap.parse(parser, replaceCommandAlias(variable, command, nextRawLine));
- rawCommand = ap.rawCommand();
- command = ap.command();
- words = ap.args();
- first = 0;
- }
- if (scriptStore.isConsoleScript(command) && !rawCommand.startsWith(":") ) {
- throw new IllegalArgumentException("Commands must be used in pipes with colon prefix!");
- }
- last = words.size();
- File file = null;
- boolean append = false;
- boolean pipeStart = false;
- boolean skipPipe = false;
- List _words = new ArrayList<>();
- //
- // find next pipe
- //
- for (int i = first; i < last; i++) {
- if (words.get(i).equals(">") || words.get(i).equals(">>")) {
- pipes.add(words.get(i));
- append = words.get(i).equals(">>");
- if (i + 1 >= last) {
- throw new IllegalArgumentException();
- }
- file = redirectFile(words.get(i + 1));
- last = i + 1;
- break;
- } else if (words.get(i).equals(pipeName.get(Pipe.FLIP))) {
- if (variable != null || file != null || pipeResult != null || consoleId == null) {
- throw new IllegalArgumentException();
- }
- pipes.add(words.get(i));
- last = i;
- variable = "_pipe" + (pipes.size() - 1);
- break;
- } else if (words.get(i).equals(pipeName.get(Pipe.NAMED))
- || (words.get(i).matches("^.*[^a-zA-Z0-9 ].*$") && customPipes.containsKey(words.get(i)))) {
- String pipe = words.get(i);
- if (pipe.equals(pipeName.get(Pipe.NAMED))) {
- if (i + 1 >= last) {
- throw new IllegalArgumentException("Pipe is NULL!");
- }
- pipe = words.get(i + 1);
- if (!pipe.matches("\\w+") || !customPipes.containsKey(pipe)) {
- throw new IllegalArgumentException("Unknown or illegal pipe name: " + pipe);
- }
- }
- pipes.add(pipe);
- last = i;
- if (pipeSource == null) {
- pipeSource = "_pipe" + (pipes.size() - 1);
- pipeResult = variable;
- variable = pipeSource;
- pipeStart = true;
- }
- break;
- } else if (words.get(i).equals(pipeName.get(Pipe.OR))
- || words.get(i).equals(pipeName.get(Pipe.AND))) {
- if (variable != null || pipeSource != null) {
- pipes.add(words.get(i));
- } else if (pipes.size() > 0 && (pipes.get(pipes.size() - 1).equals(">")
- || pipes.get(pipes.size() - 1).equals(">>"))) {
- pipes.remove(pipes.size() - 1);
- out.get(out.size() - 1).setPipe(words.get(i));
- skipPipe = true;
- } else {
- pipes.add(words.get(i));
- pipeSource = "_pipe" + (pipes.size() - 1);
- pipeResult = variable;
- variable = pipeSource;
- pipeStart = true;
- }
- last = i;
- break;
- } else {
- _words.add(words.get(i));
- }
- }
- if (last == words.size()) {
- pipes.add("END_PIPE");
- } else if (skipPipe) {
- first = last + 1;
- continue;
- }
- //
- // compose pipe command
- //
- String subLine = last < words.size() || first > 0 ? _words.stream().collect(Collectors.joining(" "))
- : ap.line();
- if (last + 1 < words.size()) {
- nextRawLine = words.subList(last + 1, words.size()).stream().collect(Collectors.joining(" "));
- }
- boolean done = true;
- boolean statement = false;
- List arglist = new ArrayList<>();
- if (_words.size() > 0) {
- arglist.addAll(_words.subList(1, _words.size()));
- }
- if (rawLine != null || (pipes.size() > 1 && customPipes.containsKey(pipes.get(pipes.size() - 2)))) {
- done = false;
- if (rawLine == null) {
- rawLine = pipeSource;
- }
- if (customPipes.containsKey(pipes.get(pipes.size() - 2))) {
- List fixes = customPipes.get(pipes.get(pipes.size() - 2));
- if (pipes.get(pipes.size() - 2).matches("\\w+")) {
- int idx = subLine.indexOf(" ");
- subLine = idx > 0 ? subLine.substring(idx + 1) : "";
- }
- rawLine += fixes.get(0)
- + (consoleId != null ? consoleEngine().expandCommandLine(subLine) : subLine)
- + fixes.get(1);
- statement = true;
- }
- if (pipes.get(pipes.size() - 1).equals(pipeName.get(Pipe.FLIP))
- || pipes.get(pipes.size() - 1).equals(pipeName.get(Pipe.AND))
- || pipes.get(pipes.size() - 1).equals(pipeName.get(Pipe.OR))) {
- done = true;
- pipeSource = null;
- if (variable != null) {
- rawLine = variable + " = " + rawLine;
- }
- }
- if (last + 1 >= words.size() || file != null) {
- done = true;
- pipeSource = null;
- if (pipeResult != null) {
- rawLine = pipeResult + " = " + rawLine;
- }
- }
- } else if (pipes.get(pipes.size() - 1).equals(pipeName.get(Pipe.FLIP)) || pipeStart) {
- if (pipeStart && pipeResult != null) {
- subLine = subLine.substring(subLine.indexOf("=") + 1);
- }
- rawLine = flipArgument(command, subLine, pipes, arglist);
- rawLine = variable + "=" + rawLine;
- } else {
- rawLine = flipArgument(command, subLine, pipes, arglist);
- }
- if (done) {
- //
- // add composed command to return list
- //
- out.add(new CommandData(parser, statement, rawLine, variable, file, append, pipes.get(pipes.size() - 1)));
- if (pipes.get(pipes.size() - 1).equals(pipeName.get(Pipe.AND))
- || pipes.get(pipes.size() - 1).equals(pipeName.get(Pipe.OR))) {
- pipeSource = null;
- pipeResult = null;
- }
- rawLine = null;
- }
- first = last + 1;
- } while (first < words.size());
- }
- return out;
- }
-
- private File redirectFile(String name) {
- File out = null;
- if (name.equals("null")) {
- out = OSUtils.IS_WINDOWS ? new File("NUL") : new File("/dev/null");
- } else {
- out = new File(name);
- }
- return out;
- }
-
- private static class ArgsParser {
- private int round = 0;
- private int curly = 0;
- private int square = 0;
- private boolean quoted;
- private boolean doubleQuoted;
- private String line;
- private String command;
- private String variable;
- private List args;
-
- public ArgsParser() {}
-
- private void reset() {
- round = 0;
- curly = 0;
- square = 0;
- quoted = false;
- doubleQuoted = false;
- }
-
- private void next(String arg) {
- char prevChar = ' ';
- for (int i = 0; i < arg.length(); i++) {
- char c = arg.charAt(i);
- if (prevChar != '\\') {
- if (!quoted && !doubleQuoted) {
- if (c == '(') {
- round++;
- } else if (c == ')') {
- round--;
- } else if (c == '{') {
- curly++;
- } else if (c == '}') {
- curly--;
- } else if (c == '[') {
- square++;
- } else if (c == ']') {
- square--;
- } else if (c == '"') {
- doubleQuoted = true;
- } else if (c == '\'') {
- quoted = true;
- }
- } else if (quoted && c == '\'') {
- quoted = false;
- } else if (doubleQuoted && c == '"') {
- doubleQuoted = false;
- }
- }
- prevChar = c;
- }
- }
-
- private boolean isEnclosed() {
- return round == 0 && curly == 0 && square == 0 && !quoted && !doubleQuoted;
- }
-
- private void enclosedArgs(List words) {
- args = new ArrayList<>();
- reset();
- boolean first = true;
- StringBuilder sb = new StringBuilder();
- for (String a : words) {
- next(a);
- if (!first) {
- sb.append(" ");
- }
- if (isEnclosed()) {
- sb.append(a);
- args.add(sb.toString());
- sb = new StringBuilder();
- first = true;
- } else {
- sb.append(a);
- first = false;
- }
- }
- if (!first) {
- args.add(sb.toString());
- }
- }
-
- public void parse(Parser parser, String line) {
- this.line = line;
- ParsedLine pl = parser.parse(line, 0, ParseContext.SPLIT_LINE);
- enclosedArgs(pl.words());
- this.command = parser.getCommand(args.get(0));
- if (!parser.validCommandName(command)) {
- this.command = "";
- }
- this.variable = parser.getVariable(args.get(0));
- }
-
- public String line() {
- return line;
- }
-
- public String command() {
- return ConsoleEngine.plainCommand(command);
- }
-
- public String rawCommand() {
- return command;
- }
-
- public String variable() {
- return variable;
- }
-
- public List args() {
- return args;
- }
-
- }
-
- private String flipArgument(final String command, final String subLine, final List pipes, List arglist) {
- String out = null;
- if (pipes.size() > 1 && pipes.get(pipes.size() - 2).equals(pipeName.get(Pipe.FLIP))) {
- String s = isCommandOrScript(command) ? "$" : "";
- out = subLine + " " + s + "_pipe" + (pipes.size() - 2);
- if (!command.isEmpty()) {
- arglist.add(s + "_pipe" + (pipes.size() - 2));
- }
- } else {
- out = subLine;
- }
- return out;
- }
-
- protected static class CommandData {
- private String rawLine;
- private String command;
- private String[] args;
- private File file;
- private boolean append;
- private String variable;
- private String pipe;
-
- public CommandData (Parser parser, boolean statement, String rawLine, String variable, File file, boolean append, String pipe) {
- this.rawLine = rawLine;
- this.variable = variable;
- this.file = file;
- this.append = append;
- this.pipe = pipe;
- this.args = new String[] {};
- if (!statement) {
- ParsedLine plf = parser.parse(rawLine, 0, ParseContext.ACCEPT_LINE);
- if (plf.words().size() > 1) {
- this.args = plf.words().subList(1, plf.words().size()).toArray(new String[0]);
- }
- this.command = ConsoleEngine.plainCommand(parser.getCommand(plf.words().get(0)));
- } else {
- this.args = new String[] {};
- this.command = "";
- }
- }
-
- public void setPipe(String pipe) {
- this.pipe = pipe;
- }
-
- public File file() {
- return file;
- }
-
- public boolean append() {
- return append;
- }
-
- public String variable() {
- return variable;
- }
-
- public String command() {
- return command;
- }
-
- public String[] args() {
- return args;
- }
-
- public String rawLine() {
- return rawLine;
- }
-
- public String pipe() {
- return pipe;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("[");
- sb.append("rawLine:").append(rawLine);
- sb.append(", ");
- sb.append("command:").append(command);
- sb.append(", ");
- sb.append("args:").append(Arrays.asList(args));
- sb.append(", ");
- sb.append("variable:").append(variable);
- sb.append(", ");
- sb.append("file:").append(file);
- sb.append(", ");
- sb.append("append:").append(append);
- sb.append(", ");
- sb.append("pipe:").append(pipe);
- sb.append("]");
- return sb.toString();
- }
-
- }
-
- private static class ScriptStore {
- ConsoleEngine engine;
- Map scripts = new HashMap<>();
-
- public ScriptStore() {}
-
- public ScriptStore(ConsoleEngine engine) {
- this.engine = engine;
- }
-
- public void refresh() {
- if (engine != null) {
- scripts = engine.scripts();
- }
- }
-
- public boolean hasScript(String name) {
- return scripts.containsKey(name);
- }
-
- public boolean isConsoleScript(String name) {
- return scripts.getOrDefault(name, false);
- }
-
- public Set getScripts() {
- return scripts.keySet();
- }
-
- }
-
- @Override
- public Object execute(String line) throws Exception {
- if (line.isEmpty() || line.trim().startsWith("#")) {
- return null;
- }
- Object out = null;
- boolean statement = false;
- boolean postProcessed = false;
- scriptStore.refresh();
- List cmds = compileCommandLine(line);
- for (int i = 0; i < cmds.size(); i++) {
- CommandData cmd = cmds.get(i);
- if (cmd.file() != null && scriptStore.isConsoleScript(cmd.command())) {
- throw new IllegalArgumentException("Console script output cannot be redirected!");
- }
- try {
- outputStream.closeAndReset();
- if (consoleId != null && !consoleEngine().isExecuting()) {
- trace(cmd);
- }
- exception = null;
- statement = false;
- postProcessed = false;
- if (cmd.variable() != null || cmd.file() != null) {
- if (cmd.file() != null) {
- outputStream.redirect(cmd.file(), cmd.append());
- } else if (consoleId != null) {
- outputStream.redirect();
- }
- outputStream.open();
- }
- boolean consoleScript = false;
- if (parser.validCommandName(cmd.command())) {
- if (isLocalCommand(cmd.command())) {
- out = localExecute(cmd.command(), cmd.args());
- } else {
- int id = registryId(cmd.command());
- if (id > -1) {
- if (consoleId != null) {
- out = commandRegistries[id].invoke(outputStream.getCommandSession(), cmd.command(),
- consoleEngine().expandParameters(cmd.args()));
- } else {
- out = commandRegistries[id].execute(outputStream.getCommandSession(), cmd.command(),
- cmd.args());
- }
- } else {
- consoleScript = true;
- }
- }
- } else {
- consoleScript = true;
- }
- if (consoleId != null) {
- if (consoleScript) {
- if (cmd.command().isEmpty() || !scriptStore.hasScript(cmd.command())) {
- statement = true;
- }
- if (statement && outputStream.isByteStream()) {
- outputStream.closeAndReset();
- }
- out = consoleEngine().execute(cmd.command(), cmd.rawLine(), cmd.args());
- }
- if (cmd.pipe().equals(pipeName.get(Pipe.OR)) || cmd.pipe().equals(pipeName.get(Pipe.AND))) {
- ExecutionResult er = postProcess(cmd, statement, out);
- postProcessed = true;
- consoleEngine().println(er.result());
- out = null;
- boolean success = er.status() == 0 ? true : false;
- if ( (cmd.pipe().equals(pipeName.get(Pipe.OR)) && success)
- || (cmd.pipe().equals(pipeName.get(Pipe.AND)) && !success)) {
- break;
- }
- }
- }
- } catch (HelpException e) {
- trace(e);
- } catch (Exception e) {
- if (cmd.pipe().equals(pipeName.get(Pipe.OR))) {
- trace(e);
- postProcessed = true;
- } else {
- throw e;
- }
- } finally {
- if (!postProcessed && consoleId != null) {
- out = postProcess(cmd, statement, out).result();
- }
- }
- }
- return out;
- }
-
- private ExecutionResult postProcess(CommandData cmd, boolean statement, Object result) {
- ExecutionResult out = new ExecutionResult(result != null ? 0 : 1, result);
- if (cmd.file() != null) {
- int status = 1;
- if (cmd.file().exists()) {
- long delta = new Date().getTime() - cmd.file().lastModified();
- status = delta < 100 ? 0 : 1;
- }
- out = new ExecutionResult(status, result);
- } else if (!statement) {
- outputStream.flush();
- outputStream.close();
- out = consoleEngine().postProcess(cmd.rawLine(), result, outputStream.getOutput());
- outputStream.reset();
- } else if (cmd.variable() != null) {
- if (consoleEngine().hasVariable(cmd.variable())) {
- out = consoleEngine().postProcess(consoleEngine().getVariable(cmd.variable()));
- } else {
- out = consoleEngine().postProcess(result);
- }
- if (!cmd.variable().startsWith("_")) {
- out = new ExecutionResult(out.status(), null);
- }
- } else {
- out = consoleEngine().postProcess(result);
- }
- return out;
- }
-
- public void cleanUp() {
- if (outputStream.isRedirecting()) {
- outputStream.closeAndReset();
- }
- if (consoleId != null) {
- consoleEngine().purge();
- }
- }
-
- private void trace(CommandData commandData) {
- if (consoleId != null) {
- consoleEngine().trace(commandData);
- } else {
- AttributedStringBuilder asb = new AttributedStringBuilder();
- asb.append(commandData.rawLine(), AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW)).println(terminal());
- }
- }
-
- @Override
- public void trace(Exception exception) {
- if (outputStream.isRedirecting()) {
- outputStream.closeAndReset();
- }
- if (consoleId != null) {
- consoleEngine().putVariable("exception", exception);
- consoleEngine().trace(exception);
- } else {
- trace(false, exception);
- }
- }
-
- @Override
- public void trace(boolean stack, Exception exception) {
- if (exception instanceof Options.HelpException) {
- Options.HelpException.highlight((exception).getMessage(), Options.HelpException.defaultStyle()).print(terminal());
- } else if (stack) {
- exception.printStackTrace();
- } else {
- String message = exception.getMessage();
- AttributedStringBuilder asb = new AttributedStringBuilder();
- if (message != null) {
- asb.append(message, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
- } else {
- asb.append("Caught exception: ", AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
- asb.append(exception.getClass().getCanonicalName(), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
- }
- asb.toAttributedString().println(terminal());
- }
- }
-
- private ConsoleEngine consoleEngine() {
- return consoleId != null ? (ConsoleEngine) commandRegistries[consoleId] : null;
- }
-
- private boolean isBuiltinRegistry(CommandRegistry registry) {
- for (Class> c : BUILTIN_REGISTERIES) {
- if (c == registry.getClass()) {
- return true;
- }
- }
- return false;
- }
-
- private void printHeader(String header) {
- AttributedStringBuilder asb = new AttributedStringBuilder().tabs(2);
- asb.append("\t");
- asb.append(header, HelpException.defaultStyle().resolve(".ti"));
- asb.append(":");
- asb.toAttributedString().println(terminal());
- }
-
- private void printCommandInfo(String command, String info, int max) {
- AttributedStringBuilder asb = new AttributedStringBuilder().tabs(Arrays.asList(4, max + 4));
- asb.append("\t");
- asb.append(command, HelpException.defaultStyle().resolve(".co"));
- asb.append("\t");
- asb.append(info);
- asb.setLength(terminal().getWidth());
- asb.toAttributedString().println(terminal());
- }
-
- private void printCommands(Collection commands, int max) {
- AttributedStringBuilder asb = new AttributedStringBuilder().tabs(Arrays.asList(4, max + 4));
- int col = 0;
- asb.append("\t");
- col += 4;
- boolean done = false;
- for (String c : commands) {
- asb.append(c, HelpException.defaultStyle().resolve(".co"));
- asb.append("\t");
- col += max;
- if (col + max > terminal().getWidth()) {
- asb.toAttributedString().println(terminal());
- asb = new AttributedStringBuilder().tabs(Arrays.asList(4, max + 4));
- col = 0;
- asb.append("\t");
- col += 4;
- done = true;
- } else {
- done = false;
- }
- }
- if (!done) {
- asb.toAttributedString().println(terminal());
- }
- terminal().flush();
- }
-
- private String doCommandInfo(List info) {
- return info.size() > 0 ? info.get(0) : " ";
- }
-
- private boolean isInArgs(List args, String name) {
- return args.isEmpty() || args.contains(name);
- }
-
- private Object help(Builtins.CommandInput input) {
- final String[] usage = { "help - command help"
- , "Usage: help [TOPIC...]",
- " -? --help Displays command help", };
- Options opt = Options.compile(usage).parse(input.args());
- if (opt.isSet("help")) {
- exception = new HelpException(opt.usage());
- return null;
- }
-
- Set commands = commandNames();
- commands.addAll(scriptStore.getScripts());
- boolean withInfo = commands.size() < terminal().getHeight() || !opt.args().isEmpty() ? true : false;
- int max = Collections.max(commands, Comparator.comparing(String::length)).length() + 1;
- TreeMap builtinCommands = new TreeMap<>();
- for (CommandRegistry r : commandRegistries) {
- if (isBuiltinRegistry(r)) {
- for (String c : r.commandNames()) {
- builtinCommands.put(c, doCommandInfo(commandInfo(c)));
- }
- }
- }
- for (String c : localCommandNames()) {
- builtinCommands.put(c, doCommandInfo(commandInfo(c)));
- exception = null;
- }
- if (isInArgs(opt.args(), "Builtins")) {
- printHeader("Builtins");
- if (withInfo) {
- for (Map.Entry entry : builtinCommands.entrySet()) {
- printCommandInfo(entry.getKey(), entry.getValue(), max);
- }
- } else {
- printCommands(builtinCommands.keySet(), max);
- }
- }
- for (CommandRegistry r : commandRegistries) {
- if (isBuiltinRegistry(r) || !isInArgs(opt.args(), r.name())) {
- continue;
- }
- TreeSet cmds = new TreeSet<>(r.commandNames());
- printHeader(r.name());
- if (withInfo) {
- for (String c : cmds) {
- printCommandInfo(c, doCommandInfo(commandInfo(c)), max);
- }
- } else {
- printCommands(cmds, max);
- }
- }
- if (consoleId != null && isInArgs(opt.args(), "Scripts")) {
- printHeader("Scripts");
- if (withInfo) {
- for (String c : scriptStore.getScripts()) {
- printCommandInfo(c, doCommandInfo(commandInfo(c)), max);
- }
- } else {
- printCommands(scriptStore.getScripts(), max);
- }
- }
- terminal().flush();
- return null;
- }
-
- private Object exit(Builtins.CommandInput input) {
- final String[] usage = { "exit - exit from app/script"
- , "Usage: exit [OBJECT]"
- , " -? --help Displays command help"
- };
- Options opt = Options.compile(usage).parse(input.args());
- if (opt.isSet("help")) {
- exception = new HelpException(opt.usage());
- } else {
- if (!opt.args().isEmpty() && consoleId != null) {
- try {
- Object[] ret = consoleEngine().expandParameters(opt.args().toArray(new String[0]));
- consoleEngine().putVariable("_return", ret.length == 1 ? ret[0] : ret);
- } catch (Exception e) {
- trace(e);
- }
- }
- exception = new EndOfFileException();
- }
- return null;
- }
-
- private List commandOptions(String command) {
- try {
- localExecute(command, new String[] { "--help" });
- } catch (HelpException e) {
- exception = null;
- return Builtins.compileCommandOptions(e.getMessage());
- } catch (Exception e) {
- trace(e);
- }
- return null;
- }
-
- private List registryNames() {
- List out = new ArrayList<>();
- out.add("Builtins");
- if (consoleId != null) {
- out.add("Scripts");
- }
- for (CommandRegistry r : commandRegistries) {
- if (isBuiltinRegistry(r)) {
- continue;
- }
- out.add(r.name());
- }
- return out;
- }
-
- private List helpCompleter(String command) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE,
- new OptionCompleter(new StringsCompleter(this::registryNames), this::commandOptions, 1)));
- return completers;
- }
-
- private List exitCompleter(String command) {
- List completers = new ArrayList<>();
- completers.add(new ArgumentCompleter(NullCompleter.INSTANCE,
- new OptionCompleter(NullCompleter.INSTANCE, this::commandOptions, 1)));
- return completers;
- }
-
- private int registryId(String command) {
- for (int i = 0; i < commandRegistries.length; i++) {
- if (commandRegistries[i].hasCommand(command)) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/builtins/src/main/java/org/jline/builtins/TTop.java b/builtins/src/main/java/org/jline/builtins/TTop.java
index 3d30ac0db..616eae1df 100644
--- a/builtins/src/main/java/org/jline/builtins/TTop.java
+++ b/builtins/src/main/java/org/jline/builtins/TTop.java
@@ -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.
@@ -147,7 +147,7 @@ public void run() throws IOException, InterruptedException {
comparator = buildComparator(sort);
delay = delay > 0 ? Math.max(delay, 100) : 1000;
if (stats == null || stats.isEmpty()) {
- stats = Arrays.asList(STAT_TID, STAT_NAME, STAT_STATE, STAT_CPU_TIME, STAT_LOCK_OWNER_ID);
+ stats = new ArrayList<>(Arrays.asList(STAT_TID, STAT_NAME, STAT_STATE, STAT_CPU_TIME, STAT_LOCK_OWNER_ID));
}
Boolean isThreadContentionMonitoringEnabled = null;
@@ -229,6 +229,9 @@ public void run() throws IOException, InterruptedException {
} while (op != Operation.EXIT);
} catch (InterruptedException ie) {
// Do nothing
+ } catch (Error err) {
+ Log.info("Error: ", err);
+ return;
} finally {
terminal.setAttributes(attr);
if (prevHandler != null) {
diff --git a/builtins/src/main/java/org/jline/builtins/Widgets.java b/builtins/src/main/java/org/jline/builtins/Widgets.java
deleted file mode 100644
index 486b90bf2..000000000
--- a/builtins/src/main/java/org/jline/builtins/Widgets.java
+++ /dev/null
@@ -1,1460 +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 org.jline.builtins;
-
-import static org.jline.keymap.KeyMap.ctrl;
-import static org.jline.keymap.KeyMap.alt;
-
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.function.Function;
-import java.util.regex.Pattern;
-
-import org.jline.builtins.Options.HelpException;
-import org.jline.keymap.KeyMap;
-import org.jline.reader.Binding;
-import org.jline.reader.Buffer;
-import org.jline.reader.LineReader;
-import org.jline.reader.Parser;
-import org.jline.reader.LineReader.SuggestionType;
-import org.jline.reader.Parser.ParseContext;
-import org.jline.reader.Reference;
-import org.jline.reader.Widget;
-import org.jline.reader.impl.BufferImpl;
-import org.jline.utils.AttributedString;
-import org.jline.utils.AttributedStringBuilder;
-import org.jline.utils.AttributedStyle;
-import org.jline.utils.Status;
-
-/**
- * Brackets/quotes autopairing and command autosuggestion widgets for jline applications.
- *
- * @author Matti Rinta-Nikkola
- */
-public abstract class Widgets {
- protected static final String AP_TOGGLE = "autopair-toggle";
- protected static final String AP_INSERT = "_autopair-insert";
- protected static final String AP_BACKWARD_DELETE_CHAR = "_autopair-backward-delete-char";
- protected static final String TT_TOGGLE = "tailtip-toggle";
- protected static final String TT_ACCEPT_LINE = "_tailtip-accept-line";
-
- private final LineReader reader;
-
- public Widgets(LineReader reader) {
- this.reader = reader;
- }
-
- public void addWidget(String name, Widget widget) {
- reader.getWidgets().put(name, namedWidget(name, widget));
- }
-
- private Widget namedWidget(final String name, final Widget widget) {
- return new Widget() {
- @Override
- public String toString() {
- return name;
- }
- @Override
- public boolean apply() {
- return widget.apply();
- }
- };
- }
-
- public void callWidget(String name) {
- if (!name.startsWith("_") && !name.endsWith("-toggle")) {
- name = "." + name;
- }
- reader.callWidget(name);
- }
-
- public void executeWidget(String name) {
- // WORK-AROUND
- getKeyMap().bind(new Reference(name), alt(ctrl('X')));
- reader.runMacro(alt(ctrl('X')));
- // The line below should be executed inside readLine()!!!
- // Create LineReader method executeWidget() maybe???
- //
- // widget(name).apply();
- }
-
- public void aliasWidget(String orig, String alias) {
- reader.getWidgets().put(alias, widget(orig));
- }
-
- public String getWidget(String name) {
- return widget(name).toString();
- }
-
- public boolean existsWidget(String name) {
- try {
- widget(name);
- return true;
- } catch(Exception e) {
- }
- return false;
- }
-
- private Widget widget(String name) {
- Widget out = null;
- if (name.startsWith(".")) {
- out = reader.getBuiltinWidgets().get(name.substring(1));
- } else {
- out = reader.getWidgets().get(name);
- }
- if (out == null) {
- throw new InvalidParameterException("widget: no such widget " + name);
- }
- return out;
- }
-
- public Parser parser() {
- return reader.getParser();
- }
-
- public KeyMap getKeyMap() {
- return reader.getKeyMaps().get(LineReader.MAIN);
- }
-
- public Buffer buffer() {
- return reader.getBuffer();
- }
-
- public void replaceBuffer(Buffer buffer) {
- reader.getBuffer().copyFrom(buffer);
- }
-
- public List args() {
- return reader.getParser().parse(buffer().toString(), 0, ParseContext.COMPLETE).words();
- }
-
- public String prevChar() {
- return String.valueOf((char)reader.getBuffer().prevChar());
- }
-
- public String currChar() {
- return String.valueOf((char)reader.getBuffer().currChar());
- }
-
- public String lastBinding() {
- return reader.getLastBinding();
- }
-
- public void putString(String string) {
- reader.getBuffer().write(string);
- }
-
- public String tailTip() {
- return reader.getTailTip();
- }
-
- public void setTailTip(String tailTip) {
- reader.setTailTip(tailTip);
- }
-
- public void setErrorPattern(Pattern errorPattern) {
- reader.getHighlighter().setErrorPattern(errorPattern);
- }
-
- public void setErrorIndex(int errorIndex) {
- reader.getHighlighter().setErrorIndex(errorIndex);
- }
-
- public void clearTailTip() {
- reader.setTailTip("");
- }
-
- public void setSuggestionType(SuggestionType type) {
- reader.setAutosuggestion(type);
- }
-
- public void addDescription(List desc) {
- Status.getStatus(reader.getTerminal()).update(desc);
- }
-
- public void clearDescription() {
- initDescription(0);
- }
-
- public void initDescription(int size) {
- Status status = Status.getStatus(reader.getTerminal(), false);
- if (size > 0) {
- if (status == null) {
- status = Status.getStatus(reader.getTerminal());
- }
- status.setBorder(true);
- List as = new ArrayList<>();
- for (int i = 0; i < size; i++) {
- as.add(new AttributedString(""));
- }
- addDescription(as);
- executeWidget(LineReader.REDRAW_LINE);
- } else if (status != null) {
- if (size < 0) {
- status.update(null);
- executeWidget(LineReader.REDRAW_LINE);
- } else {
- status.clear();
- }
- }
- }
-
- public void destroyDescription() {
- initDescription(-1);
- }
-
- /**
- * Creates and manages widgets that auto-closes, deletes and skips over matching delimiters intelligently.
- */
- public static class AutopairWidgets extends Widgets {
- /*
- * Inspired by zsh-autopair
- * https://github.com/hlissner/zsh-autopair
- */
- private static final Map LBOUNDS;
- private static final Map RBOUNDS;
- private final Map pairs;
- private final Map defaultBindings = new HashMap<>();
- private boolean enabled;
-
- {
- pairs = new HashMap<>();
- pairs.put("`", "`");
- pairs.put("'", "'");
- pairs.put("\"", "\"");
- pairs.put("[", "]");
- pairs.put("(", ")");
- pairs.put(" ", " ");
- }
- static {
- LBOUNDS = new HashMap<>();
- LBOUNDS.put("all", "[.:/\\!]");
- LBOUNDS.put("quotes", "[\\]})a-zA-Z0-9]");
- LBOUNDS.put("spaces", "[^{(\\[]");
- LBOUNDS.put("braces", "");
- LBOUNDS.put("`", "`");
- LBOUNDS.put("\"", "\"");
- LBOUNDS.put("'", "'");
- RBOUNDS = new HashMap<>();
- RBOUNDS.put("all", "[\\[{(<,.:?/%$!a-zA-Z0-9]");
- RBOUNDS.put("quotes", "[a-zA-Z0-9]");
- RBOUNDS.put("spaces", "[^\\]})]");
- RBOUNDS.put("braces", "");
- RBOUNDS.put("`", "");
- RBOUNDS.put("\"", "");
- RBOUNDS.put("'", "");
- }
-
- public AutopairWidgets(LineReader reader) {
- this(reader, false);
- }
-
- public AutopairWidgets(LineReader reader, boolean addCurlyBrackets) {
- super(reader);
- if (existsWidget(AP_INSERT)) {
- throw new IllegalStateException("AutopairWidgets already created!");
- }
- if (addCurlyBrackets) {
- pairs.put("{", "}");
- }
- addWidget(AP_INSERT, this::autopairInsert);
- addWidget("_autopair-close", this::autopairClose);
- addWidget(AP_BACKWARD_DELETE_CHAR, this::autopairDelete);
- addWidget(AP_TOGGLE, this::toggleKeyBindings);
-
- KeyMap map = getKeyMap();
- for (Map.Entry p: pairs.entrySet()) {
- defaultBindings.put(p.getKey(), map.getBound(p.getKey()));
- if (!p.getKey().equals(p.getValue())) {
- defaultBindings.put(p.getValue(), map.getBound(p.getValue()));
- }
- }
- }
-
- public void enable() {
- if (!enabled) {
- executeWidget(AP_TOGGLE);
- }
- }
-
- public void disable() {
- if (enabled) {
- executeWidget(AP_TOGGLE);
- }
- }
-
- public boolean toggle() {
- boolean before = enabled;
- executeWidget(AP_TOGGLE);
- return !before;
- }
-
- /*
- * Widgets
- */
- public boolean autopairInsert() {
- if (pairs.containsKey(lastBinding())) {
- if (canSkip(lastBinding())) {
- callWidget(LineReader.FORWARD_CHAR);
- } else if (canPair(lastBinding())) {
- callWidget(LineReader.SELF_INSERT);
- putString(pairs.get(lastBinding()));
- callWidget(LineReader.BACKWARD_CHAR);
- } else {
- callWidget(LineReader.SELF_INSERT);
- }
- } else {
- callWidget(LineReader.SELF_INSERT);
- }
- return true;
- }
-
- public boolean autopairClose() {
- if (pairs.containsValue(lastBinding())
- && currChar().equals(lastBinding())) {
- callWidget(LineReader.FORWARD_CHAR);
- } else {
- callWidget(LineReader.SELF_INSERT);
- }
- return true;
- }
-
- public boolean autopairDelete() {
- if (pairs.containsKey(prevChar()) && pairs.get(prevChar()).equals(currChar())
- && canDelete(prevChar())) {
- callWidget(LineReader.DELETE_CHAR);
- }
- callWidget(LineReader.BACKWARD_DELETE_CHAR);
- return true;
- }
-
- public boolean toggleKeyBindings() {
- if (enabled) {
- defaultBindings();
- } else {
- customBindings();
- }
- return enabled;
- }
- /*
- * key bindings...
- *
- */
- private void customBindings() {
- boolean ttActive = tailtipEnabled();
- if (ttActive) {
- callWidget(TT_TOGGLE);
- }
- KeyMap map = getKeyMap();
- for (Map.Entry p: pairs.entrySet()) {
- map.bind(new Reference(AP_INSERT), p.getKey());
- if (!p.getKey().equals(p.getValue())) {
- map.bind(new Reference("_autopair-close"), p.getValue());
- }
- }
- aliasWidget(AP_BACKWARD_DELETE_CHAR, LineReader.BACKWARD_DELETE_CHAR);
- if (ttActive) {
- callWidget(TT_TOGGLE);
- }
- enabled = true;
- }
-
- private void defaultBindings() {
- KeyMap map = getKeyMap();
- for (Map.Entry p: pairs.entrySet()) {
- map.bind(defaultBindings.get(p.getKey()), p.getKey());
- if (!p.getKey().equals(p.getValue())) {
- map.bind(defaultBindings.get(p.getValue()), p.getValue());
- }
- }
- aliasWidget("." + LineReader.BACKWARD_DELETE_CHAR, LineReader.BACKWARD_DELETE_CHAR);
- if (tailtipEnabled()) {
- callWidget(TT_TOGGLE);
- callWidget(TT_TOGGLE);
- }
- enabled = false;
- }
- /*
- * helpers
- */
- private boolean tailtipEnabled() {
- return getWidget(LineReader.ACCEPT_LINE).equals(TT_ACCEPT_LINE);
- }
-
- private boolean canPair(String d) {
- if (balanced(d) && !nexToBoundary(d)) {
- if (d.equals(" ") && (prevChar().equals(" ") || currChar().equals(" "))) {
- return false;
- } else {
- return true;
- }
- }
- return false;
- }
-
- private boolean canSkip(String d) {
- if (pairs.get(d).equals(d) && d.charAt(0) != ' ' && currChar().equals(d)
- && balanced(d)) {
- return true;
- }
- return false;
- }
-
- private boolean canDelete(String d) {
- if (balanced(d)) {
- return true;
- }
- return false;
- }
-
- private boolean balanced(String d) {
- boolean out = false;
- Buffer buf = buffer();
- String lbuf = buf.upToCursor();
- String rbuf = buf.substring(lbuf.length());
- String regx1 = pairs.get(d).equals(d)? d : "\\"+d;
- String regx2 = pairs.get(d).equals(d)? pairs.get(d) : "\\"+pairs.get(d);
- int llen = lbuf.length() - lbuf.replaceAll(regx1, "").length();
- int rlen = rbuf.length() - rbuf.replaceAll(regx2, "").length();
- if (llen == 0 && rlen == 0) {
- out = true;
- } else if (d.charAt(0) == ' ') {
- out = true;
- } else if (pairs.get(d).equals(d)) {
- if ( llen == rlen || (llen + rlen) % 2 == 0 ) {
- out = true;
- }
- } else {
- int l2len = lbuf.length() - lbuf.replaceAll(regx2, "").length();
- int r2len = rbuf.length() - rbuf.replaceAll(regx1, "").length();
- int ltotal = llen - l2len;
- int rtotal = rlen - r2len;
- if (ltotal < 0) {
- ltotal = 0;
- }
- if (ltotal >= rtotal) {
- out = true;
- }
- }
- return out;
- }
-
- private boolean boundary(String lb, String rb) {
- if ((lb.length() > 0 && prevChar().matches(lb))
- ||
- (rb.length() > 0 && currChar().matches(rb))) {
- return true;
- }
- return false;
- }
-
- private boolean nexToBoundary(String d) {
- List bk = new ArrayList<>();
- bk.add("all");
- if (d.matches("['\"`]")) {
- bk.add("quotes");
- } else if (d.matches("[{\\[(<]")) {
- bk.add("braces");
- } else if (d.charAt(0) == ' ') {
- bk.add("spaces");
- }
- if (LBOUNDS.containsKey(d) && RBOUNDS.containsKey(d)) {
- bk.add(d);
- }
- for (String k: bk) {
- if (boundary(LBOUNDS.get(k), RBOUNDS.get(k))) {
- return true;
- }
- }
- return false;
- }
- }
-
- /**
- * Creates and manages widgets for as you type command line suggestions.
- * Suggestions are created using a using command history.
- */
- public static class AutosuggestionWidgets extends Widgets {
- private boolean enabled = false;
-
- public AutosuggestionWidgets(LineReader reader) {
- super(reader);
- if (existsWidget("_autosuggest-forward-char")) {
- throw new IllegalStateException("AutosuggestionWidgets already created!");
- }
- addWidget("_autosuggest-forward-char", this::autosuggestForwardChar);
- addWidget("_autosuggest-end-of-line", this::autosuggestEndOfLine);
- addWidget("_autosuggest-forward-word", this::partialAccept);
- addWidget("autosuggest-toggle", this::toggleKeyBindings);
- }
-
- public void disable() {
- defaultBindings();
- }
-
- public void enable() {
- customBindings();
- }
- /*
- * Widgets
- */
- public boolean partialAccept() {
- Buffer buffer = buffer();
- if (buffer.cursor() == buffer.length()) {
- int curPos = buffer.cursor();
- buffer.write(tailTip());
- buffer.cursor(curPos);
- replaceBuffer(buffer);
- callWidget(LineReader.FORWARD_WORD);
- Buffer newBuf = new BufferImpl();
- newBuf.write(buffer().substring(0, buffer().cursor()));
- replaceBuffer(newBuf);
- } else {
- callWidget(LineReader.FORWARD_WORD);
- }
- return true;
- }
-
- public boolean autosuggestForwardChar() {
- return accept(LineReader.FORWARD_CHAR);
- }
-
- public boolean autosuggestEndOfLine() {
- return accept(LineReader.END_OF_LINE);
- }
-
- public boolean toggleKeyBindings() {
- if (enabled) {
- defaultBindings();
- } else {
- customBindings();
- }
- return enabled;
- }
-
-
- private boolean accept(String widget) {
- Buffer buffer = buffer();
- if (buffer.cursor() == buffer.length()) {
- putString(tailTip());
- } else {
- callWidget(widget);
- }
- return true;
- }
- /*
- * key bindings...
- *
- */
- private void customBindings() {
- if (enabled) {
- return;
- }
- aliasWidget("_autosuggest-forward-char", LineReader.FORWARD_CHAR);
- aliasWidget("_autosuggest-end-of-line", LineReader.END_OF_LINE);
- aliasWidget("_autosuggest-forward-word", LineReader.FORWARD_WORD);
- enabled = true;
- setSuggestionType(SuggestionType.HISTORY);
- }
-
- private void defaultBindings() {
- if (!enabled) {
- return;
- }
- aliasWidget("." + LineReader.FORWARD_CHAR, LineReader.FORWARD_CHAR);
- aliasWidget("." + LineReader.END_OF_LINE, LineReader.END_OF_LINE);
- aliasWidget("." + LineReader.FORWARD_WORD, LineReader.FORWARD_WORD);
- enabled = false;
- setSuggestionType(SuggestionType.NONE);
- }
- }
-
- /**
- * Creates and manages widgets for as you type command line suggestions.
- * Suggestions are created using a command completer data and/or positional argument descriptions.
- */
- public static class TailTipWidgets extends Widgets {
- public enum TipType {
- /**
- * Prepare command line suggestions using a command positional argument descriptions.
- */
- TAIL_TIP,
- /**
- * Prepare command line suggestions using a command completer data.
- */
- COMPLETER,
- /**
- * Prepare command line suggestions using either a command positional argument descriptions if exists
- * or command completers data.
- */
- COMBINED
- }
- private boolean enabled = false;
- private CommandDescriptions cmdDescs;
- private TipType tipType;
- private int descriptionSize = 0;
- private boolean descriptionEnabled = true;
- private boolean descriptionCache = true;
-
- /**
- * Creates tailtip widgets used in command line suggestions. Suggestions are created using a command
- * positional argument names. If argument descriptions do not exists command completer data will be used.
- * Status bar for argument descriptions will not be created.
- *
- * @param reader LineReader.
- * @param tailTips Commands options and positional argument descriptions.
- * @throws IllegalStateException If widgets are already created.
- */
- public TailTipWidgets(LineReader reader, Map tailTips) {
- this(reader, tailTips, 0, TipType.COMBINED);
- }
-
- /**
- * Creates tailtip widgets used in command line suggestions.
- * Status bar for argument descriptions will not be created.
- *
- * @param reader LineReader.
- * @param tailTips Commands options and positional argument descriptions.
- * @param tipType Defines which data will be used for suggestions.
- * @throws IllegalStateException If widgets are already created.
- */
- public TailTipWidgets(LineReader reader, Map tailTips, TipType tipType) {
- this(reader, tailTips, 0, tipType);
- }
-
- /**
- * Creates tailtip widgets used in command line suggestions. Suggestions are created using a command
- * positional argument names. If argument descriptions do not exists command completer data will be used.
- *
- * @param reader LineReader.
- * @param tailTips Commands options and positional argument descriptions.
- * @param descriptionSize Size of the status bar.
- * @throws IllegalStateException If widgets are already created.
- */
- public TailTipWidgets(LineReader reader, Map tailTips, int descriptionSize) {
- this(reader, tailTips, descriptionSize, TipType.COMBINED);
- }
-
- /**
- * Creates tailtip widgets used in command line suggestions.
- *
- * @param reader LineReader.
- * @param tailTips Commands options and positional argument descriptions.
- * @param descriptionSize Size of the status bar.
- * @param tipType Defines which data will be used for suggestions.
- * @throws IllegalStateException If widgets are already created.
- */
- public TailTipWidgets(LineReader reader, Map tailTips, int descriptionSize, TipType tipType) {
- this(reader, tailTips, descriptionSize, tipType, null);
- }
-
- /**
- * Creates tailtip widgets used in command line suggestions.
- *
- * @param reader LineReader.
- * @param descFun Function that returns command description.
- * @param descriptionSize Size of the status bar.
- * @param tipType Defines which data will be used for suggestions.
- * @throws IllegalStateException If widgets are already created.
- */
- public TailTipWidgets(LineReader reader, Function descFun, int descriptionSize, TipType tipType) {
- this(reader, null, descriptionSize, tipType, descFun);
- }
-
- private TailTipWidgets(LineReader reader
- , Map tailTips
- , int descriptionSize, TipType tipType, Function descFun) {
- super(reader);
- if (existsWidget(TT_ACCEPT_LINE)) {
- throw new IllegalStateException("TailTipWidgets already created!");
- }
- this.cmdDescs = tailTips != null ? new CommandDescriptions(tailTips) : new CommandDescriptions(descFun);
- this.descriptionSize = descriptionSize;
- this.tipType = tipType;
- addWidget(TT_ACCEPT_LINE, this::tailtipAcceptLine);
- addWidget("_tailtip-self-insert", this::tailtipInsert);
- addWidget("_tailtip-backward-delete-char", this::tailtipBackwardDelete);
- addWidget("_tailtip-delete-char", this::tailtipDelete);
- addWidget("_tailtip-expand-or-complete", this::tailtipComplete);
- addWidget("_tailtip-redisplay", this::tailtipUpdateStatus);
- addWidget("tailtip-window", this::toggleWindow);
- addWidget(TT_TOGGLE, this::toggleKeyBindings);
- }
-
- public void setTailTips(Map tailTips) {
- cmdDescs.setDescriptions(tailTips);
- }
-
- public void setDescriptionSize(int descriptionSize) {
- this.descriptionSize = descriptionSize;
- initDescription(descriptionSize);
- }
-
- public int getDescriptionSize() {
- return descriptionSize;
- }
-
- public void setTipType(TipType type) {
- this.tipType = type;
- if (tipType == TipType.TAIL_TIP) {
- setSuggestionType(SuggestionType.TAIL_TIP);
- } else {
- setSuggestionType(SuggestionType.COMPLETER);
- }
- }
-
- public TipType getTipType() {
- return tipType;
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public void disable() {
- if (enabled) {
- executeWidget(Widgets.TT_TOGGLE);
- }
- }
-
- public void enable() {
- if (!enabled) {
- toggleKeyBindings();
- }
- }
-
- public void setDescriptionCache(boolean cache) {
- this.descriptionCache = cache;
- }
-
- /*
- * widgets
- */
- public boolean tailtipComplete() {
- return doTailTip(LineReader.EXPAND_OR_COMPLETE);
- }
-
- public boolean tailtipAcceptLine() {
- if (tipType != TipType.TAIL_TIP) {
- setSuggestionType(SuggestionType.COMPLETER);
- }
- clearDescription();
- setErrorPattern(null);
- setErrorIndex(-1);
- cmdDescs.clearTemporaryDescs();
- return clearTailTip(LineReader.ACCEPT_LINE);
- }
-
- public boolean tailtipBackwardDelete() {
- return doTailTip(autopairEnabled() ? AP_BACKWARD_DELETE_CHAR : LineReader.BACKWARD_DELETE_CHAR);
- }
-
- private boolean clearTailTip(String widget) {
- clearTailTip();
- callWidget(widget);
- return true;
- }
-
- public boolean tailtipDelete() {
- clearTailTip();
- return doTailTip(LineReader.DELETE_CHAR);
- }
-
- public boolean tailtipInsert() {
- return doTailTip(autopairEnabled() ? AP_INSERT : LineReader.SELF_INSERT);
- }
-
- public boolean tailtipUpdateStatus() {
- return doTailTip(LineReader.REDISPLAY);
- }
-
- private boolean doTailTip(String widget) {
- Buffer buffer = buffer();
- callWidget(widget);
- Pair cmdkey = null;
- List args = args();
- if (buffer.length() == buffer.cursor()) {
- cmdkey = cmdDescs.evaluateCommandLine(buffer.toString(), args);
- } else {
- cmdkey = cmdDescs.evaluateCommandLine(buffer.toString(), buffer.cursor());
- }
- CmdDesc cmdDesc = cmdDescs.getDescription(cmdkey.getU());
- if (cmdDesc == null) {
- setErrorPattern(null);
- setErrorIndex(-1);
- clearDescription();
- resetTailTip();
- } else if (cmdDesc.isValid()) {
- if (cmdkey.getV()) {
- if (cmdDesc.isCommand() && buffer.length() == buffer.cursor()) {
- doCommandTailTip(widget, cmdDesc, args);
- }
- } else {
- doDescription(cmdDesc.getMainDescription(descriptionSize));
- setErrorPattern(cmdDesc.getErrorPattern());
- setErrorIndex(cmdDesc.getErrorIndex());
- }
- }
- return true;
- }
-
- private void doCommandTailTip(String widget, CmdDesc cmdDesc, List args) {
- int argnum = 0;
- String prevArg = "";
- for (String a : args) {
- if (!a.startsWith("-")) {
- if (!prevArg.matches("-[a-zA-Z]{1}") || !cmdDesc.optionWithValue(prevArg)) {
- argnum++;
- }
- }
- prevArg = a;
- }
- String lastArg = "";
- prevArg = args.get(args.size() - 1);
- if (!prevChar().equals(" ") && args.size() > 1) {
- lastArg = args.get(args.size() - 1);
- prevArg = args.get(args.size() - 2);
- }
- int bpsize = argnum;
- boolean doTailTip = true;
- boolean noCompleters = false;
- if (widget.endsWith(LineReader.BACKWARD_DELETE_CHAR)) {
- setSuggestionType(SuggestionType.TAIL_TIP);
- noCompleters = true;
- if (!lastArg.startsWith("-")) {
- if (!prevArg.matches("-[a-zA-Z]{1}") || !cmdDesc.optionWithValue(prevArg)) {
- bpsize--;
- }
- }
- if (prevChar().equals(" ")) {
- bpsize++;
- }
- } else if (!prevChar().equals(" ")) {
- doTailTip = false;
- }
- if (cmdDesc != null) {
- if (lastArg.startsWith("-")) {
- if (lastArg.matches("-[a-zA-Z]{1}[a-zA-Z0-9]+")) {
- if (cmdDesc.optionWithValue(lastArg.substring(0,2))) {
- doDescription(cmdDesc.getOptionDescription(lastArg.substring(0,2), descriptionSize));
- setTipType(tipType);
- } else {
- doDescription(cmdDesc.getOptionDescription("-" + lastArg.substring(lastArg.length() - 1), descriptionSize));
- setSuggestionType(SuggestionType.TAIL_TIP);
- noCompleters = true;
- }
- } else {
- doDescription(cmdDesc.getOptionDescription(lastArg, descriptionSize));
- if (!lastArg.contains("=")) {
- setSuggestionType(SuggestionType.TAIL_TIP);
- noCompleters = true;
- } else {
- setTipType(tipType);
- }
- }
- } else if (!widget.endsWith(LineReader.BACKWARD_DELETE_CHAR)){
- setTipType(tipType);
- }
- if (bpsize > 0 && doTailTip) {
- List params = cmdDesc.getArgsDesc();
- if (!noCompleters) {
- setSuggestionType(tipType == TipType.COMPLETER ? SuggestionType.COMPLETER : SuggestionType.TAIL_TIP);
- }
- if (bpsize - 1 < params.size()) {
- if (!lastArg.startsWith("-")) {
- List d = null;
- if (!prevArg.matches("-[a-zA-Z]{1}") || !cmdDesc.optionWithValue(prevArg)) {
- d = params.get(bpsize - 1).getDescription();
- } else {
- d = cmdDesc.getOptionDescription(prevArg, descriptionSize);
- }
- if (d == null || d.isEmpty()) {
- d = cmdDesc.getMainDescription(descriptionSize);
- }
- doDescription(d);
- }
- StringBuilder tip = new StringBuilder();
- for (int i = bpsize - 1; i < params.size(); i++) {
- tip.append(params.get(i).getName());
- tip.append(" ");
- }
- setTailTip(tip.toString());
- } else if (!params.isEmpty() && params.get(params.size() - 1).getName().startsWith("[")) {
- setTailTip(params.get(params.size() - 1).getName());
- doDescription(params.get(params.size() - 1).getDescription());
- }
- } else if (doTailTip) {
- resetTailTip();
- }
- } else {
- clearDescription();
- resetTailTip();
- }
- }
-
- private void resetTailTip() {
- setTailTip("");
- if (tipType != TipType.TAIL_TIP) {
- setSuggestionType(SuggestionType.COMPLETER);
- }
- }
-
- private void doDescription(List desc) {
- if (descriptionSize == 0 || !descriptionEnabled) {
- return;
- }
- if (desc.isEmpty()) {
- clearDescription();
- } else if (desc.size() == descriptionSize) {
- addDescription(desc);
- } else if (desc.size() > descriptionSize) {
- AttributedStringBuilder asb = new AttributedStringBuilder();
- asb.append(desc.get(descriptionSize - 1)).append("...", new AttributedStyle(AttributedStyle.INVERSE));
- List mod = new ArrayList<>(desc.subList(0, descriptionSize-1));
- mod.add(asb.toAttributedString());
- addDescription(mod);
- } else if (desc.size() < descriptionSize) {
- while (desc.size() != descriptionSize) {
- desc.add(new AttributedString(""));
- }
- addDescription(desc);
- }
- }
-
- private boolean autopairEnabled() {
- Binding binding = getKeyMap().getBound("(");
- if (binding instanceof Reference && ((Reference)binding).name().equals(AP_INSERT)) {
- return true;
- }
- return false;
- }
-
- public boolean toggleWindow() {
- descriptionEnabled = !descriptionEnabled;
- if (descriptionEnabled) {
- initDescription(descriptionSize);
- } else {
- destroyDescription();
- }
- return true;
- }
-
- public boolean toggleKeyBindings() {
- if (enabled) {
- defaultBindings();
- destroyDescription();
- } else {
- customBindings();
- if (descriptionEnabled) {
- initDescription(descriptionSize);
- }
- }
- return enabled;
- }
-
- /*
- * key bindings...
- *
- */
- private boolean defaultBindings() {
- if (!enabled) {
- return false;
- }
- aliasWidget("." + LineReader.ACCEPT_LINE, LineReader.ACCEPT_LINE);
- aliasWidget("." + LineReader.BACKWARD_DELETE_CHAR, LineReader.BACKWARD_DELETE_CHAR);
- aliasWidget("." + LineReader.DELETE_CHAR, LineReader.DELETE_CHAR);
- aliasWidget("." + LineReader.EXPAND_OR_COMPLETE, LineReader.EXPAND_OR_COMPLETE);
- aliasWidget("." + LineReader.SELF_INSERT, LineReader.SELF_INSERT);
- aliasWidget("." + LineReader.REDISPLAY, LineReader.REDISPLAY);
- KeyMap map = getKeyMap();
- map.bind(new Reference(LineReader.INSERT_CLOSE_PAREN), ")");
-
- setSuggestionType(SuggestionType.NONE);
- if (autopairEnabled()) {
- callWidget(AP_TOGGLE);
- callWidget(AP_TOGGLE);
- }
- enabled = false;
- return true;
- }
-
- private void customBindings() {
- if (enabled) {
- return;
- }
- aliasWidget(TT_ACCEPT_LINE, LineReader.ACCEPT_LINE);
- aliasWidget("_tailtip-backward-delete-char", LineReader.BACKWARD_DELETE_CHAR);
- aliasWidget("_tailtip-delete-char", LineReader.DELETE_CHAR);
- aliasWidget("_tailtip-expand-or-complete", LineReader.EXPAND_OR_COMPLETE);
- aliasWidget("_tailtip-self-insert", LineReader.SELF_INSERT);
- aliasWidget("_tailtip-redisplay", LineReader.REDISPLAY);
- KeyMap map = getKeyMap();
- map.bind(new Reference("_tailtip-self-insert"), ")");
-
- if (tipType != TipType.TAIL_TIP) {
- setSuggestionType(SuggestionType.COMPLETER);
- } else {
- setSuggestionType(SuggestionType.TAIL_TIP);
- }
- enabled = true;
- }
-
- private class CommandDescriptions {
- Map descriptions = new HashMap<>();
- Map temporaryDescs = new HashMap<>();
- Map volatileDescs = new HashMap<>();
- Function descFun;
-
- public CommandDescriptions(Map descriptions) {
- this.descriptions = new HashMap<>(descriptions);
- }
-
- public CommandDescriptions(Function descFun) {
- this.descFun = descFun;
- }
-
- public void setDescriptions(Map descriptions) {
- this.descriptions = new HashMap<>(descriptions);
- }
-
- public Pair evaluateCommandLine(String line, int curPos) {
- return evaluateCommandLine(line, args(), curPos);
- }
-
- public Pair evaluateCommandLine(String line, List args) {
- return evaluateCommandLine(line, args, line.length());
- }
-
- private Pair evaluateCommandLine(String line, List args, int curPos) {
- String cmd = null;
- CmdLine.DescriptionType descType = CmdLine.DescriptionType.METHOD;
- String head = line.substring(0, curPos);
- String tail = line.substring(curPos);
- if (prevChar().equals(")")) {
- descType = CmdLine.DescriptionType.SYNTAX;
- cmd = head;
- } else {
- if (line.length() == curPos) {
- cmd = args != null && (args.size() > 1 || (args.size() == 1
- && line.endsWith(" "))) ? parser().getCommand(args.get(0)) : null;
- descType = CmdLine.DescriptionType.COMMAND;
- }
- int brackets = 0;
- for (int i = head.length() - 1; i >= 0; i--) {
- if (head.charAt(i) == ')') {
- brackets++;
- } else if (head.charAt(i) == '(') {
- brackets--;
- }
- if (brackets < 0) {
- descType = CmdLine.DescriptionType.METHOD;
- head = head.substring(0, i);
- cmd = head;
- break;
- }
- }
- if (descType == CmdLine.DescriptionType.METHOD) {
- brackets = 0;
- for (int i = 0; i < tail.length(); i++) {
- if (tail.charAt(i) == ')') {
- brackets++;
- } else if (tail.charAt(i) == '(') {
- brackets--;
- }
- if (brackets > 0) {
- tail = tail.substring(i + 1);
- break;
- }
- }
- }
- }
- if (cmd != null && descFun != null) {
- if (!descriptionCache && descType == CmdLine.DescriptionType.COMMAND) {
- CmdDesc c = descFun.apply(new CmdLine(line, head, tail, args, descType));
- volatileDescs.put(cmd, c);
- } else if (!descriptions.containsKey(cmd) && !temporaryDescs.containsKey(cmd)) {
- if (descType == CmdLine.DescriptionType.COMMAND) {
- CmdDesc c = descFun.apply(new CmdLine(line, head, tail, args, descType));
- if (c != null) {
- descriptions.put(cmd, c);
- } else {
- temporaryDescs.put(cmd, c);
- }
- } else if (descType == CmdLine.DescriptionType.METHOD) {
- temporaryDescs.put(cmd, descFun.apply(new CmdLine(line, head, tail, args, descType)));
- } else {
- temporaryDescs.put(cmd, descFun.apply(new CmdLine(line, head, tail, args, descType)));
- }
- }
- }
- return new Pair(cmd, descType == CmdLine.DescriptionType.COMMAND ? true : false);
- }
-
- public CmdDesc getDescription(String command) {
- CmdDesc out = null;
- if (descriptions.containsKey(command)) {
- out = descriptions.get(command);
- } else if (temporaryDescs.containsKey(command)) {
- out = temporaryDescs.get(command);
- } else if (volatileDescs.containsKey(command)) {
- out = volatileDescs.get(command);
- volatileDescs.remove(command);
- }
- return out;
- }
-
- public void clearTemporaryDescs() {
- temporaryDescs = new HashMap<>();
- }
-
- }
-
- }
-
- public static class CmdLine {
- public enum DescriptionType {
- /**
- * Cursor is at the end of line. The args[0] is completed, the line does not have unclosed opening parenthesis
- * and does not end to the closing parenthesis.
- */
- COMMAND,
- /**
- * The part of the line from beginning til cursor has unclosed opening parenthesis.
- */
- METHOD,
- /**
- * The part of the line from beginning til cursor ends to the closing parenthesis.
- */
- SYNTAX};
- private String line;
- private String head;
- private String tail;
- private List args;
- private DescriptionType descType;
-
- /**
- * CmdLine class constructor.
- * @param line Command line
- * @param head Command line til cursor, method parameters and opening parenthesis before the cursor are removed.
- * @param tail Command line after cursor, method parameters and closing parenthesis after the cursor are removed.
- * @param args Parsed command line arguments.
- * @param descType Request COMMAND, METHOD or SYNTAX description
- */
- public CmdLine(String line, String head, String tail, List args, DescriptionType descType) {
- this.line = line;
- this.head = head;
- this.tail = tail;
- this.args = new ArrayList<>(args);
- this.descType = descType;
- }
-
- public String getLine() {
- return line;
- }
-
- public String getHead() {
- return head;
- }
-
- public String getTail() {
- return tail;
- }
-
- public List getArgs() {
- return args;
- }
-
- public DescriptionType getDescriptionType() {
- return descType;
- }
- }
-
- public static class ArgDesc {
- private String name;
- private List description = new ArrayList();
-
- public ArgDesc(String name) {
- this(name, new ArrayList());
- }
-
- public ArgDesc(String name, List description) {
- this.name = name;
- this.description = new ArrayList<>(description);
- }
-
- public String getName() {
- return name;
- }
-
- public List getDescription() {
- return description;
- }
-
- public static List doArgNames(List names) {
- List out = new ArrayList<>();
- for (String n: names) {
- out.add(new ArgDesc(n));
- }
- return out;
- }
- }
-
- public static class CmdDesc {
- private List mainDesc;
- private List argsDesc;
- private TreeMap> optsDesc;
- private Pattern errorPattern;
- private int errorIndex = -1;
- private boolean valid = true;
- private boolean command = false;
-
- public CmdDesc() {
- command = false;
- }
-
- public CmdDesc(boolean valid) {
- this.valid = valid;
- }
-
- public CmdDesc(List argsDesc) {
- this(new ArrayList<>(), argsDesc, new HashMap<>());
- }
-
- public CmdDesc(List argsDesc, Map> optsDesc) {
- this(new ArrayList<>(), argsDesc, optsDesc);
- }
-
- public CmdDesc(List mainDesc, List argsDesc, Map> optsDesc) {
- this.argsDesc = new ArrayList<>(argsDesc);
- this.optsDesc = new TreeMap<>(optsDesc);
- if (mainDesc.isEmpty() && optsDesc.containsKey("main")) {
- this.mainDesc = new ArrayList<>(optsDesc.get("main"));
- this.optsDesc.remove("main");
- } else {
- this.mainDesc = new ArrayList<>(mainDesc);
- }
- this.command = true;
- }
-
- protected boolean isValid() {
- return valid;
- }
-
- protected boolean isCommand() {
- return command;
- }
-
- public CmdDesc mainDesc(List mainDesc) {
- this.mainDesc = new ArrayList<>(mainDesc);
- return this;
- }
-
- public void setMainDesc(List mainDesc) {
- this.mainDesc = new ArrayList<>(mainDesc);
- }
-
- public void setErrorPattern(Pattern errorPattern) {
- this.errorPattern = errorPattern;
- }
-
- public Pattern getErrorPattern() {
- return errorPattern;
- }
-
- public void setErrorIndex(int errorIndex) {
- this.errorIndex = errorIndex;
- }
-
- public int getErrorIndex() {
- return errorIndex;
- }
-
- protected List getArgsDesc() {
- return argsDesc;
- }
-
- protected List getMainDescription(int descriptionSize) {
- List out = new ArrayList<>();
- if (mainDesc == null) {
- // do nothing
- } else if (mainDesc.size() <= descriptionSize) {
- out = mainDesc;
- } else {
- int tabs = 0;
- int row = 0;
- for (AttributedString as: mainDesc) {
- if (as.columnLength() >= tabs) {
- tabs = as.columnLength() + 2;
- }
- row++;
- }
- row = 0;
- List descList = new ArrayList<>();
- for (int i = 0; i < descriptionSize; i++) {
- descList.add(new AttributedString(""));
- }
- for (AttributedString as: mainDesc) {
- AttributedStringBuilder asb = new AttributedStringBuilder().tabs(tabs);
- asb.append(descList.get(row));
- asb.append(as);
- asb.append("\t");
- descList.remove(row);
- descList.add(row, asb.toAttributedString());
- row++;
- if (row >= descriptionSize) {
- row = 0;
- }
- }
- out = new ArrayList<>(descList);
- }
- return out;
- }
-
- protected List