diff --git a/console/src/main/java/org/jline/console/SystemRegistry.java b/console/src/main/java/org/jline/console/SystemRegistry.java index 95611419a..c79382a09 100644 --- a/console/src/main/java/org/jline/console/SystemRegistry.java +++ b/console/src/main/java/org/jline/console/SystemRegistry.java @@ -26,8 +26,8 @@ public interface SystemRegistry extends CommandRegistry, ConsoleOptionGetter { /** - * Set command registeries - * @param commandRegistries command registeries used by the application + * Set command registries + * @param commandRegistries command registries used by the application */ void setCommandRegistries(CommandRegistry... commandRegistries); @@ -117,6 +117,12 @@ public interface SystemRegistry extends CommandRegistry, ConsoleOptionGetter { * @return true if the specified line has a command registered */ boolean isCommandOrScript(ParsedLine line); + /** + * Returns whether a line contains command/script that is known to this registry. + * @param command the command to test + * @return true if the specified line has a command registered + */ + boolean isCommandOrScript(String command); /** * Orderly close SystemRegistry. diff --git a/console/src/main/java/org/jline/console/impl/SystemHighlighter.java b/console/src/main/java/org/jline/console/impl/SystemHighlighter.java new file mode 100644 index 000000000..c925a1393 --- /dev/null +++ b/console/src/main/java/org/jline/console/impl/SystemHighlighter.java @@ -0,0 +1,110 @@ +/* + * 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.console.impl; + +import org.jline.builtins.Nano.SyntaxHighlighter; +import org.jline.console.SystemRegistry; +import org.jline.reader.LineReader; +import org.jline.reader.Parser; +import org.jline.reader.impl.DefaultHighlighter; +import org.jline.utils.AttributedString; +import org.jline.utils.AttributedStringBuilder; + +import java.util.regex.Pattern; + +/** + * Highlight command and language syntax using nanorc highlighter. + * + * @author Matti Rinta-Nikkola + */ +public class SystemHighlighter extends DefaultHighlighter { + private final SyntaxHighlighter commandHighlighter; + private final SyntaxHighlighter argsHighlighter; + private final SyntaxHighlighter langHighlighter; + private Pattern errorPattern; + private int errorIndex = -1; + + public SystemHighlighter(SyntaxHighlighter commandHighlighter, SyntaxHighlighter argsHighlighter + , SyntaxHighlighter langHighlighter) { + this.commandHighlighter = commandHighlighter; + this.argsHighlighter = argsHighlighter; + this.langHighlighter = langHighlighter; + } + + @Override + public void setErrorPattern(Pattern errorPattern) { + this.errorPattern = errorPattern; + super.setErrorPattern(errorPattern); + } + + @Override + public void setErrorIndex(int errorIndex) { + this.errorIndex = errorIndex; + super.setErrorIndex(errorIndex); + } + + @Override + public AttributedString highlight(LineReader reader, String buffer) { + return doDefaultHighlight(reader) ? super.highlight(reader, buffer) : systemHighlight(reader.getParser(), buffer); + } + + private boolean doDefaultHighlight(LineReader reader) { + String search = reader.getSearchTerm(); + return ((search != null && search.length() > 0) || reader.getRegionActive() != LineReader.RegionType.NONE + || errorIndex > -1 || errorPattern != null); + } + + private AttributedString systemHighlight(Parser parser, String buffer) { + AttributedString out; + if (buffer.trim().isEmpty()) { + out = new AttributedStringBuilder().append(buffer).toAttributedString(); + } else if (SystemRegistry.get().isCommandOrScript(parser.getCommand(buffer.trim().split("\\s+")[0]))) { + if (commandHighlighter != null || argsHighlighter != null) { + int idx = -1; + boolean cmdFound = false; + for (int i = 0; i < buffer.length(); i++) { + char c = buffer.charAt(i); + if (c != ' ') { + cmdFound = true; + } else if (cmdFound) { + idx = i; + break; + } + } + AttributedStringBuilder asb = new AttributedStringBuilder(); + if (idx < 0) { + highlightCommand(buffer, asb); + } else { + highlightCommand(buffer.substring(0, idx), asb); + if (argsHighlighter != null) { + asb.append(argsHighlighter.highlight(buffer.substring(idx))); + } else { + asb.append(buffer.substring(idx)); + } + } + out = asb.toAttributedString(); + } else { + out = new AttributedStringBuilder().append(buffer).toAttributedString(); + } + } else if (langHighlighter != null) { + out = langHighlighter.highlight(buffer); + } else { + out = new AttributedStringBuilder().append(buffer).toAttributedString(); + } + return out; + } + + private void highlightCommand(String command, AttributedStringBuilder asb) { + if (commandHighlighter != null) { + asb.append(commandHighlighter.highlight(command)); + } else { + asb.append(command); + } + } +} diff --git a/console/src/main/java/org/jline/console/impl/SystemRegistryImpl.java b/console/src/main/java/org/jline/console/impl/SystemRegistryImpl.java index 9415aa05e..924b659c1 100644 --- a/console/src/main/java/org/jline/console/impl/SystemRegistryImpl.java +++ b/console/src/main/java/org/jline/console/impl/SystemRegistryImpl.java @@ -233,7 +233,8 @@ public boolean isCommandOrScript(ParsedLine line) { return isCommandOrScript(parser.getCommand(line.words().get(0))); } - private boolean isCommandOrScript(String command) { + @Override + public boolean isCommandOrScript(String command) { if (hasCommand(command)) { return true; } diff --git a/demo/src/main/java/org/jline/demo/Repl.java b/demo/src/main/java/org/jline/demo/Repl.java index cecada8d4..8d8927366 100644 --- a/demo/src/main/java/org/jline/demo/Repl.java +++ b/demo/src/main/java/org/jline/demo/Repl.java @@ -8,10 +8,7 @@ */ package org.jline.demo; -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -24,17 +21,14 @@ import java.util.function.Supplier; import org.jline.builtins.*; +import org.jline.builtins.Nano.SyntaxHighlighter; import org.jline.builtins.Completers.OptionCompleter; -import org.jline.console.impl.Builtins; -import org.jline.console.impl.ConsoleEngineImpl; -import org.jline.console.impl.DefaultPrinter; -import org.jline.console.impl.SystemRegistryImpl; +import org.jline.console.impl.*; import org.jline.console.CommandInput; import org.jline.console.CommandMethods; import org.jline.console.CommandRegistry; import org.jline.console.ConsoleEngine; import org.jline.console.Printer; -import org.jline.console.impl.JlineCommandRegistry; import org.jline.keymap.KeyMap; import org.jline.reader.*; import org.jline.reader.LineReader.Option; @@ -265,10 +259,19 @@ public static void main(String[] args) { Thread executeThread = Thread.currentThread(); terminal.handle(Signal.INT, signal -> executeThread.interrupt()); // - // ScriptEngine and command registeries + // Create jnanorc config file for demo // File file = new File(Repl.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); String root = file.getCanonicalPath().replace("classes", "").replaceAll("\\\\", "/"); // forward slashes works better also in windows! + File jnanorcFile = Paths.get(root, "jnanorc").toFile(); + if (!jnanorcFile.exists()) { + FileWriter fw = new FileWriter(jnanorcFile); + fw.write("include " + root + "nanorc/*.nanorc\n"); + fw.close(); + } + // + // ScriptEngine and command registries + // GroovyEngine scriptEngine = new GroovyEngine(); scriptEngine.put("ROOT", root); ConfigurationPath configPath = new ConfigurationPath(Paths.get(root), Paths.get(root)); @@ -286,10 +289,15 @@ public static void main(String[] args) { // // LineReader // + Path jnanorc = configPath.getConfig("jnanorc"); + SyntaxHighlighter commandHighlighter = SyntaxHighlighter.build(jnanorc,"COMMAND"); + SyntaxHighlighter argsHighlighter = SyntaxHighlighter.build(jnanorc,"ARGS"); + SyntaxHighlighter groovyHighlighter = SyntaxHighlighter.build(jnanorc,"Groovy"); LineReader reader = LineReaderBuilder.builder() .terminal(terminal) .completer(systemRegistry.completer()) .parser(parser) + .highlighter(new SystemHighlighter(commandHighlighter, argsHighlighter, groovyHighlighter)) .variable(LineReader.SECONDARY_PROMPT_PATTERN, "%M%P > ") .variable(LineReader.INDENTATION, 2) .variable(LineReader.LIST_MAX, 100) @@ -303,7 +311,7 @@ public static void main(String[] args) { reader.setVariable(LineReader.BLINK_MATCHING_PAREN, 0); // if enabled cursor remains in begin parenthesis (gitbash) } // - // complete command registeries + // complete command registries // consoleEngine.setLineReader(reader); builtins.setLineReader(reader); diff --git a/demo/src/main/scripts/args.nanorc b/demo/src/main/scripts/args.nanorc new file mode 100644 index 000000000..e94e6c5b5 --- /dev/null +++ b/demo/src/main/scripts/args.nanorc @@ -0,0 +1,11 @@ +syntax "ARGS" + +color brightblue "\<[-]?[0-9]*([Ee][+-]?[0-9]+)?\>" "\<[-]?[0](\.[0-9]+)?\>" +color yellow ""(\\.|[^"])*"|'(\\.|[^'])*'|[a-zA-Z]+[a-zA-Z0-9]*" +color green "\<(console|grab|inspect)\>" +color cyan "\" +color brightcyan "\<(true|false)\>" +color brightyellow "\"(\\"|[^"])*\"\s*:" "'(\'|[^'])*'\s*:" "(\[|,)\s*[a-zA-Z0-9]*\s*:" +color white "(:|\[|,|\])" +color magenta "\\u[0-9a-fA-F]{4}|\\[bfnrt'"/\\]" +color ,red " + +| + +" \ No newline at end of file diff --git a/demo/src/main/scripts/command.nanorc b/demo/src/main/scripts/command.nanorc new file mode 100644 index 000000000..2f48d612b --- /dev/null +++ b/demo/src/main/scripts/command.nanorc @@ -0,0 +1,5 @@ +syntax "COMMAND" + +color green "[a-zA-Z]+[a-zA-Z0-9]*" +color yellow ".*=" +color white "(\"|'|\.|=|:|\[|,|\])" diff --git a/demo/src/main/scripts/groovy.nanorc b/demo/src/main/scripts/groovy.nanorc new file mode 100644 index 000000000..175942388 --- /dev/null +++ b/demo/src/main/scripts/groovy.nanorc @@ -0,0 +1,17 @@ +## Here is an example for Groovy. +## +syntax "Groovy" "\.groovy$" +color green "\<(boolean|byte|char|double|float|int|long|new|short|this|transient|void|def|it)\>" +color red "\<(break|case|catch|continue|default|do|else|finally|for|if|return|switch|throw|try|while)\>" +color green,,faint "(([a-z]{2,}[.]{1}){2,10}([a-z]{2,}){0,1})" +color green,,faint "\<(print|println|sleep)\>" +color green "\<[A-Z]{0,2}([A-Z]{1}[a-z]+){1,}\>" +color cyan "\<(abstract|class|extends|final|implements|import|instanceof|interface|native|package|private|protected|public|static|strictfp|super|synchronized|throws|volatile)\>" +color red ""[^"]*"" +color yellow "\<(true|false|null)\>" +color yellow "\<[A-Z]+([_]{1}[A-Z]+){0,}\>" +icolor yellow "\b(([1-9][0-9]+)|0+)\.[0-9]+\b" "\b[1-9][0-9]*\b" "\b0[0-7]*\b" "\b0x[1-9a-f][0-9a-f]*\b" +color blue "//.*" +color blue start="/\*" end="\*/" +color brightblue start="/\*\*" end="\*/" +color brightwhite,yellow "(FIXME|TODO|XXX)" diff --git a/demo/src/main/scripts/init.jline b/demo/src/main/scripts/init.jline index 52de82a1c..34866ca27 100644 --- a/demo/src/main/scripts/init.jline +++ b/demo/src/main/scripts/init.jline @@ -6,12 +6,6 @@ import java.util.regex.* import org.jline.utils.* PATH = [ROOT + 'scripts'] - -# -# create jnanorc configuration file -# -_jnanorc = Paths.get(ROOT, 'jnanorc').toFile() -_jnanorc << 'include ' + ROOT + 'nanorc/*.nanorc\n' > null # # console options #