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 709ace8ef..e13a883ca 100644 --- a/console/src/main/java/org/jline/console/impl/SystemRegistryImpl.java +++ b/console/src/main/java/org/jline/console/impl/SystemRegistryImpl.java @@ -21,6 +21,7 @@ import java.nio.file.Path; import java.util.*; import java.util.Map.Entry; +import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -76,6 +77,7 @@ public enum Pipe { private SystemCompleter customSystemCompleter = new SystemCompleter(); private AggregateCompleter customAggregateCompleter = new AggregateCompleter(new ArrayList<>()); private boolean commandGroups = true; + private Function scriptDescription; public SystemRegistryImpl(Parser parser, Terminal terminal, Supplier workDir, ConfigurationPath configPath) { this.parser = parser; @@ -338,6 +340,10 @@ private CmdDesc commandDescription(CommandRegistry subreg) { return new CmdDesc(main, ArgDesc.doArgNames(Arrays.asList("")), options); } + public void setScriptDescription(Function scriptDescription) { + this.scriptDescription = scriptDescription; + } + @Override public CmdDesc commandDescription(CmdLine line) { CmdDesc out = null; @@ -370,7 +376,7 @@ public CmdDesc commandDescription(CmdLine line) { break; case METHOD: case SYNTAX: - // TODO + out = scriptDescription.apply(line); break; } return out; diff --git a/demo/src/main/java/org/jline/demo/Repl.java b/demo/src/main/java/org/jline/demo/Repl.java index 87c9c2697..9eca0d647 100644 --- a/demo/src/main/java/org/jline/demo/Repl.java +++ b/demo/src/main/java/org/jline/demo/Repl.java @@ -279,6 +279,7 @@ public static void main(String[] args) { systemRegistry.register("groovy", new GroovyCommand(scriptEngine, printer)); systemRegistry.setCommandRegistries(consoleEngine, builtins, myCommands); systemRegistry.addCompleter(scriptEngine.getScriptCompleter()); + systemRegistry.setScriptDescription(scriptEngine::scriptDescription); // // LineReader // diff --git a/groovy/src/main/java/org/jline/script/GroovyEngine.java b/groovy/src/main/java/org/jline/script/GroovyEngine.java index bb9e662ad..cc71581b0 100644 --- a/groovy/src/main/java/org/jline/script/GroovyEngine.java +++ b/groovy/src/main/java/org/jline/script/GroovyEngine.java @@ -9,6 +9,7 @@ package org.jline.script; import java.io.File; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -17,6 +18,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jline.builtins.Nano.SyntaxHighlighter; +import org.jline.console.CmdDesc; +import org.jline.console.CmdLine; import org.jline.console.ScriptEngine; import org.jline.groovy.Utils; import org.jline.reader.Candidate; @@ -336,6 +340,11 @@ public Cloner getObjectCloner() { return objectCloner; } + public CmdDesc scriptDescription(CmdLine line) { + return new Inspector(this).scriptDescription(line); + } + + private Completer compileCompleter() { List completers = new ArrayList<>(); completers.add(new ArgumentCompleter(new StringsCompleter("while", "class", "for", "print", "println"), NullCompleter.INSTANCE)); @@ -482,6 +491,54 @@ public static void doCandidates(List candidates, Collection f null, false)); } } + + public static int statementBegin(String buffer, String wordbuffer, Brackets brackets) { + int out = wordbuffer.lastIndexOf('='); + if (brackets.openRound()) { + int idx = buffer.lastIndexOf(wordbuffer); + if (idx > -1) { + out = statementBegin(out + , brackets.lastOpenRound() - idx + , brackets.lastComma() - idx + , brackets.lastOpenCurly() - idx + , brackets.lastCloseCurly() - idx + , brackets.lastSemicolon() -idx + , brackets.lastBlanck() - idx); + } + } + return out; + } + + public static int statementBegin(int eqPos, Brackets brackets) { + return statementBegin(eqPos + , brackets.lastOpenRound() + , brackets.lastComma() + , brackets.lastOpenCurly(), brackets.lastCloseCurly(), brackets.lastSemicolon(), brackets.lastBlanck()); + } + + private static int statementBegin(int eqPos, int openRound, int comma, int openCurly, int closeCurly, int semicolon, int blanck) { + int out = eqPos; + if (openRound > out) { + out = openRound; + } + if (comma > out) { + out = comma; + } + if (openCurly > out) { + out = openCurly; + } + if (closeCurly > out) { + out = closeCurly; + } + if (semicolon > out) { + out = semicolon; + } + if (blanck > out) { + out = blanck; + } + return Math.max(out, -1); + } + } private class PackageCompleter implements Completer { @@ -543,13 +600,7 @@ public void complete(LineReader reader, ParsedLine commandLine, List && brackets.lastCloseRound() > brackets.lastBlanck() && brackets.lastCloseRound() > brackets.lastCloseCurly()) { int varsep = buffer.lastIndexOf('.'); - int eqsep = statementBegin(buffer.indexOf('=') - , brackets.lastOpenRound() - , brackets.lastComma() - , brackets.lastOpenCurly() - , brackets.lastCloseCurly() - , brackets.lastSemicolon() - , brackets.lastBlanck()); + int eqsep = Helpers.statementBegin(buffer.lastIndexOf('='), brackets); if (varsep > 0 && varsep > eqsep) { Class clazz = evaluateClass(inspector, buffer.substring(eqsep + 1, varsep)); int vs = wordbuffer.lastIndexOf('.'); @@ -580,7 +631,7 @@ public void complete(LineReader reader, ParsedLine commandLine, List } } else { int varsep = wordbuffer.lastIndexOf('.'); - int eqsep = statementBegin(buffer, wordbuffer, brackets); + int eqsep = Helpers.statementBegin(buffer, wordbuffer, brackets); String param = wordbuffer.substring(eqsep + 1); if (varsep < 0 || varsep < eqsep) { String curBuf = wordbuffer.substring(0, eqsep + 1); @@ -620,46 +671,6 @@ public void complete(LineReader reader, ParsedLine commandLine, List } } - private int statementBegin(String buffer, String wordbuffer, Brackets brackets) { - int out = wordbuffer.indexOf('='); - if (brackets.openRound()) { - int idx = buffer.lastIndexOf(wordbuffer); - if (idx > -1) { - out = statementBegin(out - , brackets.lastOpenRound() - idx - , brackets.lastComma() - idx - , brackets.lastOpenCurly() - idx - , brackets.lastCloseCurly() - idx - , brackets.lastSemicolon() -idx - , brackets.lastBlanck() - idx); - } - } - return out; - } - - private int statementBegin(int eqPos, int openRound, int comma, int openCurly, int closeCurly, int semicolon, int blanck) { - int out = eqPos; - if (openRound > out) { - out = openRound; - } - if (comma > out) { - out = comma; - } - if (openCurly > out) { - out = openCurly; - } - if (closeCurly > out) { - out = closeCurly; - } - if (semicolon > out) { - out = semicolon; - } - if (blanck > out) { - out = blanck; - } - return Math.max(out, -1); - } - private Set variables(Inspector inspector) { if (inspector == null) { inspector = new Inspector(groovyEngine); @@ -748,12 +759,21 @@ public Inspector(GroovyEngine groovyEngine) { } public Class evaluateClass(String objectStatement) { + Class out = null; try { - return execute(objectStatement).getClass(); + if (objectStatement.contains("(") || objectStatement.contains(")") + || objectStatement.contains("{") || objectStatement.contains("}")) { + out = execute(objectStatement).getClass(); + } else if (!objectStatement.contains(".") ) { + out = (Class)execute(objectStatement + ".class"); + } else { + out = Class.forName(objectStatement); + } + } catch (Exception e) { } - return null; + return out; } private Object execute(String statement) { @@ -808,6 +828,137 @@ public Object getVariable(String name) { return sharedData.hasVariable(name) ? sharedData.getVariable(name) : null; } + public CmdDesc scriptDescription(CmdLine line) { + CmdDesc out = null; + try { + switch (line.getDescriptionType()) { + case COMMAND: + break; + case METHOD: + out = methodDescription(line); + break; + case SYNTAX: + break; + } + } catch (Exception e) { + if (Log.isDebugEnabled()) { + e.printStackTrace(); + } + } + return out; + } + + private String trimName(String name) { + String out = name; + int idx = name.indexOf('('); + if (idx > 0) { + out = name.substring(0, idx); + } + return out; + } + + private CmdDesc methodDescription(CmdLine line) throws Exception { + CmdDesc out = new CmdDesc(); + List args = line.getArgs(); + boolean constructor = false; + Class clazz = null; + String methodName = null; + if ((args.size() == 2 && args.get(0).matches("(new|\\w+=new)")) + || (args.size() > 2 && args.get(args.size() - 2).matches("(new|.*\\(new|.*,new)"))) { + constructor = true; + clazz = evaluateClass(trimName(args.get(args.size() - 1))); + } else { + String buffer = line.getHead(); + String wordbuffer = args.get(args.size() - 1); + Brackets brackets = new Brackets(buffer); + int varsep = wordbuffer.lastIndexOf('.'); + int eqsep = Helpers.statementBegin(buffer, wordbuffer, brackets); + if (varsep > 0 && varsep > eqsep) { + loadStatementVars(buffer); + methodName = trimName(wordbuffer.substring(varsep + 1)); + clazz = evaluateClass(wordbuffer.substring(eqsep + 1, varsep)); + } + } + List mainDesc = new ArrayList<>(); + if (clazz != null) { + SyntaxHighlighter java = SyntaxHighlighter.build("classpath:/org/jline/groovy/java.nanorc"); + mainDesc.add(java.highlight(clazz.toString())); + if (constructor) { + for (Constructor m : clazz.getConstructors()) { + StringBuilder sb = new StringBuilder(); + sb.append(m.getName()); + sb.append("("); + boolean first = true; + for(Class p: m.getParameterTypes()) { + if (!first) { + sb.append(", "); + } + sb.append(p.getTypeName()); + first = false; + } + sb.append(")"); + first = true; + for (Class e: m.getExceptionTypes()) { + if (first) { + sb.append(" throws "); + } else { + sb.append(", "); + } + sb.append(e.getCanonicalName()); + first = false; + } + mainDesc.add(java.highlight(sb.toString().replaceAll("java.lang.", ""))); + } + } else { + List addedMethods = new ArrayList<>(); + do { + for (Method m : clazz.getMethods()) { + if (!m.getName().equals(methodName)) { + continue; + } + StringBuilder sb = new StringBuilder(); + if (Modifier.isFinal(m.getModifiers())) { + sb.append("final "); + } + if (Modifier.isStatic(m.getModifiers())) { + sb.append("static "); + } + sb.append(m.getReturnType().getCanonicalName()); + sb.append(" "); + sb.append(methodName); + sb.append("("); + boolean first = true; + for (Class p : m.getParameterTypes()) { + if (!first) { + sb.append(", "); + } + sb.append(p.getTypeName()); + first = false; + } + sb.append(")"); + first = true; + for (Class e : m.getExceptionTypes()) { + if (first) { + sb.append(" throws "); + } else { + sb.append(", "); + } + sb.append(e.getCanonicalName()); + first = false; + } + if (!addedMethods.contains(sb.toString())) { + addedMethods.add(sb.toString()); + mainDesc.add(java.highlight(sb.toString().replaceAll("java.lang.", ""))); + } + } + clazz = clazz.getSuperclass(); + } while (clazz != null); + } + out.setMainDesc(mainDesc); + } + return out; + } + } private static class ObjectCloner implements Cloner { diff --git a/groovy/src/main/resources/org/jline/groovy/java.nanorc b/groovy/src/main/resources/org/jline/groovy/java.nanorc new file mode 100644 index 000000000..744dc838e --- /dev/null +++ b/groovy/src/main/resources/org/jline/groovy/java.nanorc @@ -0,0 +1,17 @@ +## Here is an example for Java. +## +syntax "Java" "\.java$" +color green "\<(boolean|byte|char|double|float|int|long|new|short|this|transient|void)\>" +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 "\<[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)" +color ,green "[[:space:]]+$"