# HG changeset patch # User rfield # Date 1479490911 28800 # Node ID b36ad5a64e753ea7ae39a5507eea0e46b51800e2 # Parent 64435520b580afb940bca355ab3613f4299ee897 8153402: jshell tool: completion provider for /help 8169818: jshell tool: completion provider for /vars /methods /types gives -history Reviewed-by: jlahoda diff -r 64435520b580 -r b36ad5a64e75 langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java --- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Thu Nov 17 22:18:50 2016 +0000 +++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Fri Nov 18 09:41:51 2016 -0800 @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -1051,8 +1052,12 @@ } static final CompletionProvider EMPTY_COMPLETION_PROVIDER = new FixedCompletionProvider(); - private static final CompletionProvider KEYWORD_COMPLETION_PROVIDER = new FixedCompletionProvider("-all ", "-start ", "-history "); - private static final CompletionProvider RELOAD_OPTIONS_COMPLETION_PROVIDER = new FixedCompletionProvider("-restore", "-quiet"); + private static final CompletionProvider SNIPPET_HISTORY_OPTION_COMPLETION_PROVIDER = new FixedCompletionProvider("-all", "-start ", "-history"); + private static final CompletionProvider SAVE_OPTION_COMPLETION_PROVIDER = new FixedCompletionProvider("-all ", "-start ", "-history "); + private static final CompletionProvider SNIPPET_OPTION_COMPLETION_PROVIDER = new FixedCompletionProvider("-all", "-start " ); + private static final CompletionProvider RELOAD_OPTIONS_COMPLETION_PROVIDER = new FixedCompletionProvider("-restore ", "-quiet "); + private static final CompletionProvider RESTORE_COMPLETION_PROVIDER = new FixedCompletionProvider("-restore"); + private static final CompletionProvider QUIET_COMPLETION_PROVIDER = new FixedCompletionProvider("-quiet"); private static final CompletionProvider SET_MODE_OPTIONS_COMPLETION_PROVIDER = new FixedCompletionProvider("-command", "-quiet", "-delete"); private static final CompletionProvider FILE_COMPLETION_PROVIDER = fileCompletions(p -> true); private final Map commands = new LinkedHashMap<>(); @@ -1106,24 +1111,62 @@ p.getFileName().toString().endsWith(".jar")); } + // Completion based on snippet supplier private CompletionProvider snippetCompletion(Supplier> snippetsSupplier) { return (prefix, cursor, anchor) -> { anchor[0] = 0; + int space = prefix.lastIndexOf(' '); + Set prior = new HashSet<>(Arrays.asList(prefix.split(" "))); + if (prior.contains("-all") || prior.contains("-history")) { + return Collections.emptyList(); + } + String argPrefix = prefix.substring(space + 1); return snippetsSupplier.get() + .filter(k -> !prior.contains(String.valueOf(k.id())) + && (!(k instanceof DeclarationSnippet) + || !prior.contains(((DeclarationSnippet) k).name()))) .flatMap(k -> (k instanceof DeclarationSnippet) - ? Stream.of(String.valueOf(k.id()), ((DeclarationSnippet) k).name()) - : Stream.of(String.valueOf(k.id()))) - .filter(k -> k.startsWith(prefix)) + ? Stream.of(String.valueOf(k.id()) + " ", ((DeclarationSnippet) k).name() + " ") + : Stream.of(String.valueOf(k.id()) + " ")) + .filter(k -> k.startsWith(argPrefix)) .map(k -> new ArgSuggestion(k)) .collect(Collectors.toList()); }; } - private CompletionProvider snippetKeywordCompletion(Supplier> snippetsSupplier) { + // Completion based on snippet supplier with -all -start (and sometimes -history) options + private CompletionProvider snippetWithOptionCompletion(CompletionProvider optionProvider, + Supplier> snippetsSupplier) { return (code, cursor, anchor) -> { List result = new ArrayList<>(); - result.addAll(KEYWORD_COMPLETION_PROVIDER.completionSuggestions(code, cursor, anchor)); + int pastSpace = code.lastIndexOf(' ') + 1; // zero if no space + if (pastSpace == 0) { + result.addAll(optionProvider.completionSuggestions(code, cursor, anchor)); + } result.addAll(snippetCompletion(snippetsSupplier).completionSuggestions(code, cursor, anchor)); + anchor[0] += pastSpace; + return result; + }; + } + + // Completion of help, commands and subjects + private CompletionProvider helpCompletion() { + return (code, cursor, anchor) -> { + List result; + int pastSpace = code.indexOf(' ') + 1; // zero if no space + if (pastSpace == 0) { + result = new FixedCompletionProvider(commands.values().stream() + .filter(cmd -> cmd.kind.showInHelp || cmd.kind == CommandKind.HELP_SUBJECT) + .map(c -> c.command + " ") + .toArray(size -> new String[size])) + .completionSuggestions(code, cursor, anchor); + } else if (code.startsWith("/se")) { + result = new FixedCompletionProvider(SET_SUBCOMMANDS) + .completionSuggestions(code.substring(pastSpace), cursor - pastSpace, anchor); + } else { + result = Collections.emptyList(); + } + anchor[0] += pastSpace; return result; }; } @@ -1133,7 +1176,7 @@ List result = new ArrayList<>(); int space = code.indexOf(' '); if (space == (-1)) { - result.addAll(KEYWORD_COMPLETION_PROVIDER.completionSuggestions(code, cursor, anchor)); + result.addAll(SAVE_OPTION_COMPLETION_PROVIDER.completionSuggestions(code, cursor, anchor)); } result.addAll(FILE_COMPLETION_PROVIDER.completionSuggestions(code.substring(space + 1), cursor - space - 1, anchor)); anchor[0] += space + 1; @@ -1143,9 +1186,25 @@ private static CompletionProvider reloadCompletion() { return (code, cursor, anchor) -> { - List result = new ArrayList<>(); + CompletionProvider provider; int pastSpace = code.indexOf(' ') + 1; // zero if no space - result.addAll(RELOAD_OPTIONS_COMPLETION_PROVIDER.completionSuggestions(code.substring(pastSpace), cursor - pastSpace, anchor)); + if (pastSpace == 0) { + provider = RELOAD_OPTIONS_COMPLETION_PROVIDER; + } else { + switch (code.substring(0, pastSpace - 1)) { + case "-quiet": + provider = RESTORE_COMPLETION_PROVIDER; + break; + case "-restore": + provider = QUIET_COMPLETION_PROVIDER; + break; + default: + provider = EMPTY_COMPLETION_PROVIDER; + break; + } + } + List result = provider.completionSuggestions( + code.substring(pastSpace), cursor - pastSpace, anchor); anchor[0] += pastSpace; return result; }; @@ -1210,10 +1269,12 @@ { registerCommand(new Command("/list", arg -> cmdList(arg), - snippetKeywordCompletion(this::allSnippets))); + snippetWithOptionCompletion(SNIPPET_HISTORY_OPTION_COMPLETION_PROVIDER, + this::allSnippets))); registerCommand(new Command("/edit", arg -> cmdEdit(arg), - snippetCompletion(this::allSnippets))); + snippetWithOptionCompletion(SNIPPET_OPTION_COMPLETION_PROVIDER, + this::allSnippets))); registerCommand(new Command("/drop", arg -> cmdDrop(arg), snippetCompletion(this::dropableSnippets), @@ -1226,13 +1287,16 @@ FILE_COMPLETION_PROVIDER)); registerCommand(new Command("/vars", arg -> cmdVars(arg), - snippetKeywordCompletion(this::allVarSnippets))); + snippetWithOptionCompletion(SNIPPET_OPTION_COMPLETION_PROVIDER, + this::allVarSnippets))); registerCommand(new Command("/methods", arg -> cmdMethods(arg), - snippetKeywordCompletion(this::allMethodSnippets))); + snippetWithOptionCompletion(SNIPPET_OPTION_COMPLETION_PROVIDER, + this::allMethodSnippets))); registerCommand(new Command("/types", arg -> cmdTypes(arg), - snippetKeywordCompletion(this::allTypeSnippets))); + snippetWithOptionCompletion(SNIPPET_OPTION_COMPLETION_PROVIDER, + this::allTypeSnippets))); registerCommand(new Command("/imports", arg -> cmdImports(), EMPTY_COMPLETION_PROVIDER)); @@ -1258,7 +1322,7 @@ CommandKind.HIDDEN)); registerCommand(new Command("/help", arg -> cmdHelp(arg), - EMPTY_COMPLETION_PROVIDER)); + helpCompletion())); registerCommand(new Command("/set", arg -> cmdSet(arg), new ContinuousCompletionProvider(Map.of( @@ -1276,7 +1340,7 @@ registerCommand(new Command("/?", "help.quest", arg -> cmdHelp(arg), - EMPTY_COMPLETION_PROVIDER, + helpCompletion(), CommandKind.NORMAL)); registerCommand(new Command("/!", "help.bang", diff -r 64435520b580 -r b36ad5a64e75 langtools/test/jdk/jshell/CommandCompletionTest.java --- a/langtools/test/jdk/jshell/CommandCompletionTest.java Thu Nov 17 22:18:50 2016 +0000 +++ b/langtools/test/jdk/jshell/CommandCompletionTest.java Fri Nov 18 09:41:51 2016 -0800 @@ -23,7 +23,7 @@ /* * @test - * @bug 8144095 8164825 + * @bug 8144095 8164825 8169818 8153402 * @summary Test Command Completion * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main @@ -61,12 +61,13 @@ public void testList() { test(false, new String[] {"--no-startup"}, a -> assertCompletion(a, "/l|", false, "/list "), - a -> assertCompletion(a, "/list |", false, "-all ", "-history ", "-start "), - a -> assertCompletion(a, "/list -h|", false, "-history "), + a -> assertCompletion(a, "/list |", false, "-all", "-history", "-start "), + a -> assertCompletion(a, "/list -h|", false, "-history"), a -> assertCompletion(a, "/list q|", false), a -> assertVariable(a, "int", "xray"), - a -> assertCompletion(a, "/list |", false, "-all ", "-history ", "-start ", "1", "xray"), - a -> assertCompletion(a, "/list x|", false, "xray") + a -> assertCompletion(a, "/list |", false, "-all", "-history", "-start ", "1 ", "xray "), + a -> assertCompletion(a, "/list x|", false, "xray "), + a -> assertCompletion(a, "/list xray |", false) ); } @@ -76,8 +77,8 @@ a -> assertClass(a, "class cTest {}", "class", "cTest"), a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), a -> assertVariable(a, "int", "fTest"), - a -> assertCompletion(a, "/drop |", false, "1", "2", "3", "cTest", "fTest", "mTest"), - a -> assertCompletion(a, "/drop f|", false, "fTest") + a -> assertCompletion(a, "/drop |", false, "1 ", "2 ", "3 ", "cTest ", "fTest ", "mTest "), + a -> assertCompletion(a, "/drop f|", false, "fTest ") ); } @@ -88,8 +89,54 @@ a -> assertClass(a, "class cTest {}", "class", "cTest"), a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), a -> assertVariable(a, "int", "fTest"), - a -> assertCompletion(a, "/edit |", false, "1", "2", "3", "cTest", "fTest", "mTest"), - a -> assertCompletion(a, "/edit f|", false, "fTest") + a -> assertCompletion(a, "/edit |", false, + "-all" , "-start " , "1 ", "2 ", "3 ", "cTest ", "fTest ", "mTest "), + a -> assertCompletion(a, "/edit cTest |", false, + "2 ", "3 ", "fTest ", "mTest "), + a -> assertCompletion(a, "/edit 1 fTest |", false, + "2 ", "mTest "), + a -> assertCompletion(a, "/edit f|", false, "fTest "), + a -> assertCompletion(a, "/edit mTest f|", false, "fTest ") + ); + } + + public void testHelp() { + assertCompletion("/help |", false, + "/! ", "/- ", "/ ", "/? ", "/classpath ", "/drop ", + "/edit ", "/exit ", "/help ", "/history ", "/imports ", + "/list ", "/methods ", "/open ", "/reload ", "/reset ", + "/save ", "/set ", "/types ", "/vars ", "intro ", "shortcuts "); + assertCompletion("/? |", false, + "/! ", "/- ", "/ ", "/? ", "/classpath ", "/drop ", + "/edit ", "/exit ", "/help ", "/history ", "/imports ", + "/list ", "/methods ", "/open ", "/reload ", "/reset ", + "/save ", "/set ", "/types ", "/vars ", "intro ", "shortcuts "); + assertCompletion("/help /s|", false, + "/save ", "/set "); + assertCompletion("/help /set |", false, + "editor", "feedback", "format", "mode", "prompt", "start", "truncation"); + assertCompletion("/help /edit |", false); + } + + public void testReload() { + assertCompletion("/reload |", false, "-quiet ", "-restore "); + assertCompletion("/reload -restore |", false, "-quiet"); + assertCompletion("/reload -quiet |", false, "-restore"); + assertCompletion("/reload -restore -quiet |", false); + } + + public void testVarsMethodsTypes() { + test(false, new String[]{"--no-startup"}, + a -> assertCompletion(a, "/v|", false, "/vars "), + a -> assertCompletion(a, "/m|", false, "/methods "), + a -> assertCompletion(a, "/t|", false, "/types "), + a -> assertClass(a, "class cTest {}", "class", "cTest"), + a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"), + a -> assertVariable(a, "int", "fTest"), + a -> assertCompletion(a, "/vars |", false, "-all", "-start ", "3 ", "fTest "), + a -> assertCompletion(a, "/meth |", false, "-all", "-start ", "2 ", "mTest "), + a -> assertCompletion(a, "/typ |", false, "-all", "-start ", "1 ", "cTest "), + a -> assertCompletion(a, "/var f|", false, "fTest ") ); } diff -r 64435520b580 -r b36ad5a64e75 langtools/test/jdk/jshell/ReplToolTesting.java --- a/langtools/test/jdk/jshell/ReplToolTesting.java Thu Nov 17 22:18:50 2016 +0000 +++ b/langtools/test/jdk/jshell/ReplToolTesting.java Fri Nov 18 09:41:51 2016 -0800 @@ -476,7 +476,7 @@ code = code.replace("|", ""); assertTrue(cursor > -1, "'|' not found: " + code); List completions = - js.commandCompletionSuggestions(code, cursor, new int[1]); //XXX: ignoring anchor for now + js.commandCompletionSuggestions(code, cursor, new int[] {-1}); //XXX: ignoring anchor for now return completions.stream() .filter(s -> isSmart == s.matchesType()) .map(s -> s.continuation())