--- 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<String, Command> commands = new LinkedHashMap<>();
@@ -1106,24 +1111,62 @@
p.getFileName().toString().endsWith(".jar"));
}
+ // Completion based on snippet supplier
private CompletionProvider snippetCompletion(Supplier<Stream<? extends Snippet>> snippetsSupplier) {
return (prefix, cursor, anchor) -> {
anchor[0] = 0;
+ int space = prefix.lastIndexOf(' ');
+ Set<String> 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<Stream<? extends Snippet>> snippetsSupplier) {
+ // Completion based on snippet supplier with -all -start (and sometimes -history) options
+ private CompletionProvider snippetWithOptionCompletion(CompletionProvider optionProvider,
+ Supplier<Stream<? extends Snippet>> snippetsSupplier) {
return (code, cursor, anchor) -> {
List<Suggestion> 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<Suggestion> 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<Suggestion> 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<Suggestion> 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<Suggestion> 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",
--- 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,
+ "/! ", "/-<n> ", "/<id> ", "/? ", "/classpath ", "/drop ",
+ "/edit ", "/exit ", "/help ", "/history ", "/imports ",
+ "/list ", "/methods ", "/open ", "/reload ", "/reset ",
+ "/save ", "/set ", "/types ", "/vars ", "intro ", "shortcuts ");
+ assertCompletion("/? |", false,
+ "/! ", "/-<n> ", "/<id> ", "/? ", "/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 ")
);
}