8182270: JShell API: Tools need snippet information without evaluating snippet
8166334: jshell tool: shortcut: expression/statement to method
Reviewed-by: jlahoda
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java Tue Aug 15 13:16:32 2017 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java Wed Aug 16 18:42:11 2017 -0700
@@ -64,6 +64,11 @@
import jdk.internal.jshell.tool.StopDetectingInputStream.State;
import jdk.internal.misc.Signal;
import jdk.internal.misc.Signal.Handler;
+import jdk.jshell.ExpressionSnippet;
+import jdk.jshell.Snippet;
+import jdk.jshell.Snippet.SubKind;
+import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
+import jdk.jshell.VarSnippet;
class ConsoleIOContext extends IOContext {
@@ -916,6 +921,111 @@
return new FixResult(fixes, null);
}
},
+ new FixComputer('m', false) { //compute "Introduce method" Fix:
+ private void performToMethod(ConsoleReader in, String type, String code) throws IOException {
+ in.redrawLine();
+ if (!code.trim().endsWith(";")) {
+ in.putString(";");
+ }
+ in.putString(" }");
+ in.setCursorPosition(0);
+ String afterCursor = type.equals("void")
+ ? "() { "
+ : "() { return ";
+ in.putString(type + " " + afterCursor);
+ // position the cursor where the method name should be entered (before parens)
+ in.setCursorPosition(in.getCursorBuffer().cursor - afterCursor.length());
+ in.flush();
+ }
+
+ private FixResult reject(JShellTool repl, String messageKey) {
+ return new FixResult(Collections.emptyList(), repl.messageFormat(messageKey));
+ }
+
+ @Override
+ public FixResult compute(JShellTool repl, String code, int cursor) {
+ final String codeToCursor = code.substring(0, cursor);
+ final String type;
+ final CompletionInfo ci = repl.analysis.analyzeCompletion(codeToCursor);
+ if (!ci.remaining().isEmpty()) {
+ return reject(repl, "jshell.console.exprstmt");
+ }
+ switch (ci.completeness()) {
+ case COMPLETE:
+ case COMPLETE_WITH_SEMI:
+ case CONSIDERED_INCOMPLETE:
+ break;
+ case EMPTY:
+ return reject(repl, "jshell.console.empty");
+ case DEFINITELY_INCOMPLETE:
+ case UNKNOWN:
+ default:
+ return reject(repl, "jshell.console.erroneous");
+ }
+ List<Snippet> snl = repl.analysis.sourceToSnippets(ci.source());
+ if (snl.size() != 1) {
+ return reject(repl, "jshell.console.erroneous");
+ }
+ Snippet sn = snl.get(0);
+ switch (sn.kind()) {
+ case EXPRESSION:
+ type = ((ExpressionSnippet) sn).typeName();
+ break;
+ case STATEMENT:
+ type = "void";
+ break;
+ case VAR:
+ if (sn.subKind() != SubKind.TEMP_VAR_EXPRESSION_SUBKIND) {
+ // only valid var is an expression turned into a temp var
+ return reject(repl, "jshell.console.exprstmt");
+ }
+ type = ((VarSnippet) sn).typeName();
+ break;
+ case IMPORT:
+ case METHOD:
+ case TYPE_DECL:
+ return reject(repl, "jshell.console.exprstmt");
+ case ERRONEOUS:
+ default:
+ return reject(repl, "jshell.console.erroneous");
+ }
+ List<Fix> fixes = new ArrayList<>();
+ fixes.add(new Fix() {
+ @Override
+ public String displayName() {
+ return repl.messageFormat("jshell.console.create.method");
+ }
+
+ @Override
+ public void perform(ConsoleReader in) throws IOException {
+ performToMethod(in, type, codeToCursor);
+ }
+ });
+ int idx = type.lastIndexOf(".");
+ if (idx > 0) {
+ String stype = type.substring(idx + 1);
+ QualifiedNames res = repl.analysis.listQualifiedNames(stype, stype.length());
+ if (res.isUpToDate() && res.getNames().contains(type)
+ && !res.isResolvable()) {
+ fixes.add(new Fix() {
+ @Override
+ public String displayName() {
+ return "import: " + type + ". " +
+ repl.messageFormat("jshell.console.create.method");
+ }
+
+ @Override
+ public void perform(ConsoleReader in) throws IOException {
+ repl.processCompleteSource("import " + type + ";");
+ in.println("Imported: " + type);
+ performToMethod(in, stype, codeToCursor);
+ }
+ });
+ }
+ }
+ return new FixResult(fixes, null);
+ }
+ },
new FixComputer('i', true) { //compute "Add import" Fixes:
@Override
public FixResult compute(JShellTool repl, String code, int cursor) {
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties Tue Aug 15 13:16:32 2017 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties Wed Aug 16 18:42:11 2017 -0700
@@ -174,10 +174,19 @@
jshell.console.choice = Choice: \
jshell.console.create.variable = Create variable
+jshell.console.create.method = Create method
jshell.console.resolvable = \nThe identifier is resolvable in this context.
jshell.console.no.candidate = \nNo candidate fully qualified names found to import.
jshell.console.incomplete = \nResults may be incomplete; try again later for complete results.
+jshell.console.erroneous = \nIncomplete or erroneous. A single valid expression or statement must proceed Shift-<tab> m.
+jshell.console.exprstmt = \nA single valid expression or statement must proceed Shift-<tab> m.
+jshell.console.empty = \nEmpty entry. A single valid expression or statement must proceed Shift-<tab> m..
+jshell.fix.wrong.shortcut =\
+Unexpected character after Shift-Tab.\n\
+Use "i" for auto-import, "v" for variable creation, or "m" for method creation.\n\
+For more information see:\n\
+ /help shortcuts
help.usage = \
Usage: jshell <options> <load files>\n\
@@ -549,6 +558,11 @@
After a complete expression, hold down <shift> while pressing <tab>,\n\t\t\
then release and press "v", the expression will be converted to\n\t\t\
a variable declaration whose type is based on the type of the expression.\n\n\
+Shift-<tab> m\n\t\t\
+ After a complete expression or statement, hold down <shift> while pressing <tab>,\n\t\t\
+ then release and press "m", the expression or statement will be converted to\n\t\t\
+ a method declaration. If an expression, the return type is based on the type\n\t\t\
+ of the expression.\n\n\
Shift-<tab> i\n\t\t\
After an unresolvable identifier, hold down <shift> while pressing <tab>,\n\t\t\
then release and press "i", and jshell will propose possible imports\n\t\t\
@@ -1021,7 +1035,3 @@
/set format silent errorpre '| ' \n\
/set format silent errorpost '%n' \n\
/set format silent display '' \n
-
-jshell.fix.wrong.shortcut =\
-Unexpected character after Shift-Tab. Use "i" for auto-import or "v" for variable creation. For more information see:\n\
- /help shortcuts
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Tue Aug 15 13:16:32 2017 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Wed Aug 16 18:42:11 2017 -0700
@@ -91,6 +91,9 @@
private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\p{javaWhitespace}+(?<static>static\\p{javaWhitespace}+)?(?<fullname>[\\p{L}\\p{N}_\\$\\.]+\\.(?<name>[\\p{L}\\p{N}_\\$]+|\\*))");
+ // for uses that should not change state -- non-evaluations
+ private boolean preserveState = false;
+
private int varNumber = 0;
private final JShell state;
@@ -145,6 +148,23 @@
/**
* Converts the user source of a snippet into a Snippet object (or list of
* objects in the case of: int x, y, z;). Does not install the Snippets
+ * or execute them. Does not change any state.
+ *
+ * @param userSource the source of the snippet
+ * @return usually a singleton list of Snippet, but may be empty or multiple
+ */
+ List<Snippet> toScratchSnippets(String userSource) {
+ try {
+ preserveState = true;
+ return sourceToSnippets(userSource);
+ } finally {
+ preserveState = false;
+ }
+ }
+
+ /**
+ * Converts the user source of a snippet into a Snippet object (or list of
+ * objects in the case of: int x, y, z;). Does not install the Snippets
* or execute them.
*
* @param userSource the source of the snippet
@@ -316,11 +336,15 @@
subkind = SubKind.OTHER_EXPRESSION_SUBKIND;
}
if (shouldGenTempVar(subkind)) {
- if (state.tempVariableNameGenerator != null) {
- name = state.tempVariableNameGenerator.get();
- }
- while (name == null || state.keyMap.doesVariableNameExist(name)) {
- name = "$" + ++varNumber;
+ if (preserveState) {
+ name = "$$";
+ } else {
+ if (state.tempVariableNameGenerator != null) {
+ name = state.tempVariableNameGenerator.get();
+ }
+ while (name == null || state.keyMap.doesVariableNameExist(name)) {
+ name = "$" + ++varNumber;
+ }
}
guts = Wrap.tempVarWrap(compileSource, typeName, name);
Collection<String> declareReferences = null; //TODO
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Tue Aug 15 13:16:32 2017 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Wed Aug 16 18:42:11 2017 -0700
@@ -862,7 +862,7 @@
* Check if this JShell has been closed
* @throws IllegalStateException if it is closed
*/
- private void checkIfAlive() throws IllegalStateException {
+ void checkIfAlive() throws IllegalStateException {
if (closed) {
throw new IllegalStateException(messageFormat("jshell.exc.closed", this));
}
@@ -879,8 +879,8 @@
if (sn == null) {
throw new NullPointerException(messageFormat("jshell.exc.null"));
} else {
- if (sn.key().state() != this) {
- throw new IllegalArgumentException(messageFormat("jshell.exc.alien"));
+ if (sn.key().state() != this || sn.id() == Snippet.UNASSOCIATED_ID) {
+ throw new IllegalArgumentException(messageFormat("jshell.exc.alien", sn.toString()));
}
return sn;
}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java Tue Aug 15 13:16:32 2017 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java Wed Aug 16 18:42:11 2017 -0700
@@ -552,6 +552,8 @@
}
}
+ static final String UNASSOCIATED_ID = "*UNASSOCIATED*";
+
private final Key key;
private final String source;
private final Wrap guts;
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java Tue Aug 15 13:16:32 2017 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java Wed Aug 16 18:42:11 2017 -0700
@@ -133,6 +133,34 @@
public abstract List<SnippetWrapper> wrappers(String input);
/**
+ * Converts the source code of a snippet into a {@link Snippet} object (or
+ * list of {@code Snippet} objects in the case of some var declarations,
+ * e.g.: int x, y, z;).
+ * Does not install the snippets: declarations are not
+ * accessible by other snippets; imports are not added.
+ * Does not execute the snippets.
+ * <p>
+ * Queries may be done on the {@code Snippet} object. The {@link Snippet#id()}
+ * will be {@code "*UNASSOCIATED*"}.
+ * The returned snippets are not associated with the
+ * {@link JShell} instance, so attempts to pass them to {@code JShell}
+ * methods will throw an {@code IllegalArgumentException}.
+ * They will not appear in queries for snippets --
+ * for example, {@link JShell#snippets() }.
+ * <p>
+ * Restrictions on the input are as in {@link JShell#eval}.
+ * <p>
+ * Only preliminary compilation is performed, sufficient to build the
+ * {@code Snippet}. Snippets known to be erroneous, are returned as
+ * {@link ErroneousSnippet}, other snippets may or may not be in error.
+ * <p>
+ * @param input The input String to convert
+ * @return usually a singleton list of Snippet, but may be empty or multiple
+ * @throws IllegalStateException if the {@code JShell} instance is closed.
+ */
+ public abstract List<Snippet> sourceToSnippets(String input);
+
+ /**
* Returns a collection of {@code Snippet}s which might need updating if the
* given {@code Snippet} is updated. The returned collection is designed to
* be inclusive and may include many false positives.
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java Tue Aug 15 13:16:32 2017 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java Wed Aug 16 18:42:11 2017 -0700
@@ -533,6 +533,16 @@
}
@Override
+ public List<Snippet> sourceToSnippets(String input) {
+ proc.checkIfAlive();
+ List<Snippet> snl = proc.eval.toScratchSnippets(input);
+ for (Snippet sn : snl) {
+ sn.setId(Snippet.UNASSOCIATED_ID);
+ }
+ return snl;
+ }
+
+ @Override
public Collection<Snippet> dependents(Snippet snippet) {
return proc.maps.getDependents(snippet);
}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/resources/l10n.properties Tue Aug 15 13:16:32 2017 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/resources/l10n.properties Wed Aug 16 18:42:11 2017 -0700
@@ -29,6 +29,6 @@
jshell.diag.modifier.single.ignore = Modifier {0} not permitted in top-level declarations, ignored
jshell.exc.null = Snippet must not be null
-jshell.exc.alien = Snippet not from this JShell
+jshell.exc.alien = Snippet not from this JShell: {0}
jshell.exc.closed = JShell ({0}) has been closed.
jshell.exc.var.not.valid = Snippet parameter of varValue() {0} must be VALID, it is: {1}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/AnalyzeSnippetTest.java Wed Aug 16 18:42:11 2017 -0700
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8182270
+ * @summary test non-eval Snippet analysis
+ * @build KullaTesting TestingInputStream
+ * @run testng AnalyzeSnippetTest
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.List;
+import jdk.jshell.Snippet;
+import jdk.jshell.DeclarationSnippet;
+import org.testng.annotations.Test;
+
+import jdk.jshell.JShell;
+import jdk.jshell.MethodSnippet;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import jdk.jshell.ErroneousSnippet;
+import jdk.jshell.ExpressionSnippet;
+import jdk.jshell.ImportSnippet;
+import jdk.jshell.Snippet.SubKind;
+import jdk.jshell.SourceCodeAnalysis;
+import jdk.jshell.StatementSnippet;
+import jdk.jshell.TypeDeclSnippet;
+import jdk.jshell.VarSnippet;
+import static jdk.jshell.Snippet.SubKind.*;
+
+@Test
+public class AnalyzeSnippetTest {
+
+ JShell state;
+ SourceCodeAnalysis sca;
+
+ @BeforeMethod
+ public void setUp() {
+ state = JShell.builder()
+ .out(new PrintStream(new ByteArrayOutputStream()))
+ .err(new PrintStream(new ByteArrayOutputStream()))
+ .build();
+ sca = state.sourceCodeAnalysis();
+ }
+
+ @AfterMethod
+ public void tearDown() {
+ if (state != null) {
+ state.close();
+ }
+ state = null;
+ sca = null;
+ }
+
+ public void testImport() {
+ ImportSnippet sn = (ImportSnippet) assertSnippet("import java.util.List;",
+ SubKind.SINGLE_TYPE_IMPORT_SUBKIND);
+ assertEquals(sn.name(), "List");
+ sn = (ImportSnippet) assertSnippet("import static java.nio.file.StandardOpenOption.CREATE;",
+ SubKind.SINGLE_STATIC_IMPORT_SUBKIND);
+ assertTrue(sn.isStatic());
+ }
+
+ public void testClass() {
+ TypeDeclSnippet sn = (TypeDeclSnippet) assertSnippet("class C {}",
+ SubKind.CLASS_SUBKIND);
+ assertEquals(sn.name(), "C");
+ sn = (TypeDeclSnippet) assertSnippet("enum EE {A, B , C}",
+ SubKind.ENUM_SUBKIND);
+ }
+
+ public void testMethod() {
+ MethodSnippet sn = (MethodSnippet) assertSnippet("int m(int x) { return x + x; }",
+ SubKind.METHOD_SUBKIND);
+ assertEquals(sn.name(), "m");
+ assertEquals(sn.signature(), "(int)int");
+ }
+
+ public void testVar() {
+ VarSnippet sn = (VarSnippet) assertSnippet("int i;",
+ SubKind.VAR_DECLARATION_SUBKIND);
+ assertEquals(sn.name(), "i");
+ assertEquals(sn.typeName(), "int");
+ sn = (VarSnippet) assertSnippet("int jj = 6;",
+ SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND);
+ sn = (VarSnippet) assertSnippet("2 + 2",
+ SubKind.TEMP_VAR_EXPRESSION_SUBKIND);
+ }
+
+ public void testExpression() {
+ state.eval("int aa = 10;");
+ ExpressionSnippet sn = (ExpressionSnippet) assertSnippet("aa",
+ SubKind.VAR_VALUE_SUBKIND);
+ assertEquals(sn.name(), "aa");
+ assertEquals(sn.typeName(), "int");
+ sn = (ExpressionSnippet) assertSnippet("aa;",
+ SubKind.VAR_VALUE_SUBKIND);
+ assertEquals(sn.name(), "aa");
+ assertEquals(sn.typeName(), "int");
+ sn = (ExpressionSnippet) assertSnippet("aa = 99",
+ SubKind.ASSIGNMENT_SUBKIND);
+ }
+
+ public void testStatement() {
+ StatementSnippet sn = (StatementSnippet) assertSnippet("System.out.println(33)",
+ SubKind.STATEMENT_SUBKIND);
+ sn = (StatementSnippet) assertSnippet("if (true) System.out.println(33);",
+ SubKind.STATEMENT_SUBKIND);
+ }
+
+ public void testErroneous() {
+ ErroneousSnippet sn = (ErroneousSnippet) assertSnippet("+++",
+ SubKind.UNKNOWN_SUBKIND);
+ sn = (ErroneousSnippet) assertSnippet("abc",
+ SubKind.UNKNOWN_SUBKIND);
+ }
+
+ public void testNoStateChange() {
+ assertSnippet("int a = 5;", SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND);
+ assertSnippet("a", SubKind.UNKNOWN_SUBKIND);
+ VarSnippet vsn = (VarSnippet) state.eval("int aa = 10;").get(0).snippet();
+ assertSnippet("++aa;", SubKind.TEMP_VAR_EXPRESSION_SUBKIND);
+ assertEquals(state.varValue(vsn), "10");
+ assertSnippet("class CC {}", SubKind.CLASS_SUBKIND);
+ assertSnippet("new CC();", SubKind.UNKNOWN_SUBKIND);
+ }
+
+ private Snippet assertSnippet(String input, SubKind sk) {
+ List<Snippet> sns = sca.sourceToSnippets(input);
+ assertEquals(sns.size(), 1, "snippet count");
+ Snippet sn = sns.get(0);
+ assertEquals(sn.id(), "*UNASSOCIATED*");
+ assertEquals(sn.subKind(), sk);
+ return sn;
+ }
+}
--- a/langtools/test/jdk/jshell/MergedTabShiftTabCommandTest.java Tue Aug 15 13:16:32 2017 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/**
- * @test
- * @bug 8177076
- * @modules
- * jdk.compiler/com.sun.tools.javac.api
- * jdk.compiler/com.sun.tools.javac.main
- * jdk.jshell/jdk.internal.jshell.tool.resources:open
- * jdk.jshell/jdk.jshell:open
- * @library /tools/lib
- * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
- * @build Compiler UITesting
- * @build MergedTabShiftTabCommandTest
- * @run testng MergedTabShiftTabCommandTest
- */
-
-import java.util.regex.Pattern;
-
-import org.testng.annotations.Test;
-
-@Test
-public class MergedTabShiftTabCommandTest extends UITesting {
-
- public void testCommand() throws Exception {
- // set terminal height so that help output won't hit page breaks
- System.setProperty("test.terminal.height", "1000000");
-
- doRunTest((inputSink, out) -> {
- inputSink.write("1\n");
- waitOutput(out, "\u0005");
- inputSink.write("/\011");
- waitOutput(out, ".*/edit.*/list.*\n\n" + Pattern.quote(getResource("jshell.console.see.synopsis")) + "\n\r\u0005/");
- inputSink.write("\011");
- waitOutput(out, ".*\n/edit\n" + Pattern.quote(getResource("help.edit.summary")) +
- "\n.*\n/list\n" + Pattern.quote(getResource("help.list.summary")) +
- ".*\n\n" + Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n\r\u0005/");
- inputSink.write("\011");
- waitOutput(out, "/!\n" +
- Pattern.quote(getResource("help.bang")) + "\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.next.command.doc")) + "\n" +
- "\r\u0005/");
- inputSink.write("\011");
- waitOutput(out, "/-<n>\n" +
- Pattern.quote(getResource("help.previous")) + "\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.next.command.doc")) + "\n" +
- "\r\u0005/");
-
- inputSink.write("ed\011");
- waitOutput(out, "edit $");
-
- inputSink.write("\011");
- waitOutput(out, ".*-all.*" +
- "\n\n" + Pattern.quote(getResource("jshell.console.see.synopsis")) + "\n\r\u0005/");
- inputSink.write("\011");
- waitOutput(out, Pattern.quote(getResource("help.edit.summary")) + "\n\n" +
- Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n\r\u0005/edit ");
- inputSink.write("\011");
- waitOutput(out, Pattern.quote(getResource("help.edit").replaceAll("\t", " ")));
-
- inputSink.write("\u0003/env \011");
- waitOutput(out, "\u0005/env -\n" +
- "-add-exports -add-modules -class-path -module-path \n" +
- "\r\u0005/env -");
-
- inputSink.write("\011");
- waitOutput(out, "-add-exports -add-modules -class-path -module-path \n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.synopsis")) + "\n" +
- "\r\u0005/env -");
-
- inputSink.write("\011");
- waitOutput(out, Pattern.quote(getResource("help.env.summary")) + "\n\n" +
- Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n" +
- "\r\u0005/env -");
-
- inputSink.write("\011");
- waitOutput(out, Pattern.quote(getResource("help.env").replaceAll("\t", " ")) + "\n" +
- "\r\u0005/env -");
-
- inputSink.write("\011");
- waitOutput(out, "-add-exports -add-modules -class-path -module-path \n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.synopsis")) + "\n" +
- "\r\u0005/env -");
-
- inputSink.write("\u0003/exit \011");
- waitOutput(out, Pattern.quote(getResource("help.exit.summary")) + "\n\n" +
- Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n\r\u0005/exit ");
- inputSink.write("\011");
- waitOutput(out, Pattern.quote(getResource("help.exit")) + "\n" +
- "\r\u0005/exit ");
- inputSink.write("\011");
- waitOutput(out, Pattern.quote(getResource("help.exit.summary")) + "\n\n" +
- Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n\r\u0005/exit ");
- inputSink.write("\u0003/doesnotexist\011");
- waitOutput(out, "\u0005/doesnotexist\n" +
- Pattern.quote(getResource("jshell.console.no.such.command")) + "\n" +
- "\n" +
- "\r\u0005/doesnotexist");
- });
- }
-
-}
--- a/langtools/test/jdk/jshell/MergedTabShiftTabExpressionTest.java Tue Aug 15 13:16:32 2017 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,250 +0,0 @@
-/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/**
- * @test
- * @bug 8177076
- * @modules
- * jdk.compiler/com.sun.tools.javac.api
- * jdk.compiler/com.sun.tools.javac.main
- * jdk.jshell/jdk.internal.jshell.tool.resources:open
- * jdk.jshell/jdk.jshell:open
- * @library /tools/lib
- * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
- * @build Compiler UITesting
- * @build MergedTabShiftTabExpressionTest
- * @run testng/timeout=300 MergedTabShiftTabExpressionTest
- */
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
-import java.util.regex.Pattern;
-
-import org.testng.annotations.Test;
-
-@Test
-public class MergedTabShiftTabExpressionTest extends UITesting {
-
- public void testExpression() throws Exception {
- Path classes = prepareZip();
- doRunTest((inputSink, out) -> {
- inputSink.write("/env -class-path " + classes.toString() + "\n");
- waitOutput(out, Pattern.quote(getResource("jshell.msg.set.restore")) + "\n\u0005");
- inputSink.write("import jshelltest.*;\n");
- waitOutput(out, "\n\u0005");
-
- //-> <tab>
- inputSink.write("\011");
- waitOutput(out, getMessage("jshell.console.completion.all.completions.number", "[0-9]+"));
- inputSink.write("\011");
- waitOutput(out, ".*String.*StringBuilder.*\n\r\u0005");
-
- //new JShellTes<tab>
- inputSink.write("new JShellTes\011");
- waitOutput(out, "t\nJShellTest\\( JShellTestAux\\( \n\r\u0005new JShellTest");
-
- //new JShellTest<tab>
- inputSink.write("\011");
- waitOutput(out, "JShellTest\\( JShellTestAux\\( \n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
- "jshelltest.JShellTest\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
- "\r\u0005new JShellTest");
- inputSink.write("\011");
- waitOutput(out, "jshelltest.JShellTest\n" +
- "JShellTest 0\n" +
- "\r\u0005new JShellTest");
- inputSink.write("\011");
- waitOutput(out, "JShellTest\\( JShellTestAux\\( \n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
- "jshelltest.JShellTest\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
- "\r\u0005new JShellTest");
-
- //new JShellTest(<tab>
- inputSink.write("(\011");
- waitOutput(out, "\\(\n" +
- Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
- "JShellTest\\(String str\\)\n" +
- "JShellTest\\(String str, int i\\)\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
- "\r\u0005new JShellTest\\(");
- inputSink.write("\011");
- waitOutput(out, "JShellTest\\(String str\\)\n" +
- "JShellTest 1\n" +
- "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.next.page")) + "\n" +
- "\r\u0005new JShellTest\\(");
- inputSink.write("\011");
- waitOutput(out, "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.next.javadoc")) + "\n" +
- "\r\u0005new JShellTest\\(");
- inputSink.write("\011");
- waitOutput(out, "JShellTest\\(String str, int i\\)\n" +
- "JShellTest 2\n" +
- "\n" +
- getMessage("jshell.console.completion.all.completions.number", "[0-9]+") + "\n" +
- "\r\u0005new JShellTest\\(");
- inputSink.write("\011");
- waitOutput(out, ".*String.*StringBuilder.*\n\r\u0005new JShellTest\\(");
-
- inputSink.write("\u0003String str = \"\";\nnew JShellTest(");
- waitOutput(out, "\u0005new JShellTest\\(");
-
- inputSink.write("\011");
- waitOutput(out, "\n" +
- "str \n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
- "JShellTest\\(String str\\)\n" +
- "JShellTest\\(String str, int i\\)\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
- "\r\u0005new JShellTest\\(");
- inputSink.write("\011");
- waitOutput(out, "JShellTest\\(String str\\)\n" +
- "JShellTest 1\n" +
- "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.next.page")) + "\n" +
- "\r\u0005new JShellTest\\(");
- inputSink.write("\011");
- waitOutput(out, "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.next.javadoc")) + "\n" +
- "\r\u0005new JShellTest\\(");
- inputSink.write("\011");
- waitOutput(out, "JShellTest\\(String str, int i\\)\n" +
- "JShellTest 2\n" +
- "\n" +
- getMessage("jshell.console.completion.all.completions.number", "[0-9]+") + "\n" +
- "\r\u0005new JShellTest\\(");
- inputSink.write("\011");
- waitOutput(out, ".*String.*StringBuilder.*\n\r\u0005new JShellTest\\(");
-
- inputSink.write("\u0003JShellTest t = new JShellTest\011");
- waitOutput(out, "\u0005JShellTest t = new JShellTest\n" +
- "JShellTest\\( \n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
- "jshelltest.JShellTest\n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.completion.all.completions")) + "\n" +
- "\r\u0005JShellTest t = new JShellTest");
- inputSink.write("\011");
- waitOutput(out, "JShellTest\\( JShellTestAux\\( \n" +
- "\n" +
- Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
- "\r\u0005JShellTest t = new JShellTest");
-
- inputSink.write("\u0003JShellTest t = new \011");
- waitOutput(out, "\u0005JShellTest t = new \n" +
- "JShellTest\\( \n" +
- "\n" +
- getMessage("jshell.console.completion.all.completions.number", "[0-9]+") + "\n" +
- "\r\u0005JShellTest t = new ");
- inputSink.write("\011");
- waitOutput(out, ".*String.*StringBuilder.*\n\r\u0005JShellTest t = new ");
-
- inputSink.write("\u0003class JShelX{}\n");
- inputSink.write("new JShel\011");
- waitOutput(out, "\u0005new JShel\n" +
- "JShelX\\(\\) JShellTest\\( JShellTestAux\\( \n" +
- "\r\u0005new JShel");
-
- //no crash:
- inputSink.write("\u0003new Stringbuil\011");
- waitOutput(out, "\u0005new Stringbuil\u0007");
- });
- }
-
- private Path prepareZip() {
- String clazz1 =
- "package jshelltest;\n" +
- "/**JShellTest 0" +
- " */\n" +
- "public class JShellTest {\n" +
- " /**JShellTest 1\n" +
- " * <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1\n" +
- " * <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1\n" +
- " * <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1\n" +
- " */\n" +
- " public JShellTest(String str) {}\n" +
- " /**JShellTest 2" +
- " */\n" +
- " public JShellTest(String str, int i) {}\n" +
- "}\n";
-
- String clazz2 =
- "package jshelltest;\n" +
- "/**JShellTestAux 0" +
- " */\n" +
- "public class JShellTestAux {\n" +
- " /**JShellTest 1" +
- " */\n" +
- " public JShellTestAux(String str) { }\n" +
- " /**JShellTest 2" +
- " */\n" +
- " public JShellTestAux(String str, int i) { }\n" +
- "}\n";
-
- Path srcZip = Paths.get("src.zip");
-
- try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(srcZip))) {
- out.putNextEntry(new JarEntry("jshelltest/JShellTest.java"));
- out.write(clazz1.getBytes());
- out.putNextEntry(new JarEntry("jshelltest/JShellTestAux.java"));
- out.write(clazz2.getBytes());
- } catch (IOException ex) {
- throw new IllegalStateException(ex);
- }
-
- compiler.compile(clazz1, clazz2);
-
- try {
- Field availableSources = Class.forName("jdk.jshell.SourceCodeAnalysisImpl").getDeclaredField("availableSourcesOverride");
- availableSources.setAccessible(true);
- availableSources.set(null, Arrays.asList(srcZip));
- } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | ClassNotFoundException ex) {
- throw new IllegalStateException(ex);
- }
-
- return compiler.getClassDir();
- }
- //where:
- private final Compiler compiler = new Compiler();
-
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ToolShiftTabTest.java Wed Aug 16 18:42:11 2017 -0700
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8166334
+ * @summary test shift-tab shortcuts "fixes"
+ * @modules
+ * jdk.jshell/jdk.internal.jshell.tool.resources:open
+ * jdk.jshell/jdk.jshell:open
+ * @build UITesting
+ * @build ToolShiftTabTest
+ * @run testng/timeout=300 ToolShiftTabTest
+ */
+
+import java.util.regex.Pattern;
+import org.testng.annotations.Test;
+
+@Test
+public class ToolShiftTabTest extends UITesting {
+
+ // Shift-tab as escape sequence
+ private String FIX = "\033\133\132";
+
+ public void testFixVariable() throws Exception {
+ doRunTest((inputSink, out) -> {
+ inputSink.write("3+4");
+ inputSink.write(FIX + "v");
+ inputSink.write("jj\n");
+ waitOutput(out, "jj ==> 7");
+ inputSink.write("jj\n");
+ waitOutput(out, "jj ==> 7");
+ });
+ }
+
+ public void testFixMethod() throws Exception {
+ doRunTest((inputSink, out) -> {
+ inputSink.write("5.5 >= 3.1415926535");
+ inputSink.write(FIX + "m");
+ waitOutput(out, "boolean ");
+ inputSink.write("mm\n");
+ waitOutput(out, "| created method mm()");
+ inputSink.write("mm()\n");
+ waitOutput(out, "==> true");
+ inputSink.write("/method\n");
+ waitOutput(out, "boolean mm()");
+ });
+ }
+
+ public void testFixMethodVoid() throws Exception {
+ doRunTest((inputSink, out) -> {
+ inputSink.write("System.out.println(\"Testing\")");
+ inputSink.write(FIX + "m");
+ inputSink.write("p\n");
+ waitOutput(out, "| created method p()");
+ inputSink.write("p()\n");
+ waitOutput(out, "Testing");
+ inputSink.write("/method\n");
+ waitOutput(out, "void p()");
+ });
+ }
+
+ public void testFixMethodNoLeaks() throws Exception {
+ doRunTest((inputSink, out) -> {
+ inputSink.write("4");
+ inputSink.write(FIX + "m");
+ inputSink.write("\u0003 55");
+ inputSink.write(FIX + "m");
+ inputSink.write("\u0003 55");
+ inputSink.write(FIX + "m");
+ inputSink.write("\u0003 55");
+ inputSink.write(FIX + "m");
+ inputSink.write("\u0003 55");
+ inputSink.write(FIX + "m");
+ inputSink.write("\u0003'X'");
+ inputSink.write(FIX + "m");
+ inputSink.write("nl\n");
+ waitOutput(out, "| created method nl()");
+ inputSink.write("/list\n");
+ waitOutput(out, Pattern.quote("1 : char nl() { return 'X'; }"));
+ inputSink.write("true\n");
+ waitOutput(out, Pattern.quote("$2 ==> true"));
+ inputSink.write("/list\n");
+ waitOutput(out, "2 : true");
+ });
+ }
+
+ public void testFixImport() throws Exception {
+ doRunTest((inputSink, out) -> {
+ inputSink.write("Frame");
+ inputSink.write(FIX + "i");
+ inputSink.write("1");
+ inputSink.write(".WIDTH\n");
+ waitOutput(out, "==> 1");
+ inputSink.write("/import\n");
+ waitOutput(out, "| import java.awt.Frame");
+
+ inputSink.write("Object");
+ inputSink.write(FIX + "i");
+ waitOutput(out, "The identifier is resolvable in this context");
+ });
+ }
+
+ public void testFixBad() throws Exception {
+ doRunTest((inputSink, out) -> {
+ inputSink.write("123");
+ inputSink.write(FIX + "z");
+ waitOutput(out, "Unexpected character after Shift-Tab");
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ToolTabCommandTest.java Wed Aug 16 18:42:11 2017 -0700
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8177076
+ * @modules
+ * jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.jshell/jdk.internal.jshell.tool.resources:open
+ * jdk.jshell/jdk.jshell:open
+ * @library /tools/lib
+ * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
+ * @build Compiler UITesting
+ * @build ToolTabCommandTest
+ * @run testng ToolTabCommandTest
+ */
+
+import java.util.regex.Pattern;
+
+import org.testng.annotations.Test;
+
+@Test
+public class ToolTabCommandTest extends UITesting {
+
+ public void testCommand() throws Exception {
+ // set terminal height so that help output won't hit page breaks
+ System.setProperty("test.terminal.height", "1000000");
+
+ doRunTest((inputSink, out) -> {
+ inputSink.write("1\n");
+ waitOutput(out, "\u0005");
+ inputSink.write("/\011");
+ waitOutput(out, ".*/edit.*/list.*\n\n" + Pattern.quote(getResource("jshell.console.see.synopsis")) + "\n\r\u0005/");
+ inputSink.write("\011");
+ waitOutput(out, ".*\n/edit\n" + Pattern.quote(getResource("help.edit.summary")) +
+ "\n.*\n/list\n" + Pattern.quote(getResource("help.list.summary")) +
+ ".*\n\n" + Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n\r\u0005/");
+ inputSink.write("\011");
+ waitOutput(out, "/!\n" +
+ Pattern.quote(getResource("help.bang")) + "\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.next.command.doc")) + "\n" +
+ "\r\u0005/");
+ inputSink.write("\011");
+ waitOutput(out, "/-<n>\n" +
+ Pattern.quote(getResource("help.previous")) + "\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.next.command.doc")) + "\n" +
+ "\r\u0005/");
+
+ inputSink.write("ed\011");
+ waitOutput(out, "edit $");
+
+ inputSink.write("\011");
+ waitOutput(out, ".*-all.*" +
+ "\n\n" + Pattern.quote(getResource("jshell.console.see.synopsis")) + "\n\r\u0005/");
+ inputSink.write("\011");
+ waitOutput(out, Pattern.quote(getResource("help.edit.summary")) + "\n\n" +
+ Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n\r\u0005/edit ");
+ inputSink.write("\011");
+ waitOutput(out, Pattern.quote(getResource("help.edit").replaceAll("\t", " ")));
+
+ inputSink.write("\u0003/env \011");
+ waitOutput(out, "\u0005/env -\n" +
+ "-add-exports -add-modules -class-path -module-path \n" +
+ "\r\u0005/env -");
+
+ inputSink.write("\011");
+ waitOutput(out, "-add-exports -add-modules -class-path -module-path \n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.synopsis")) + "\n" +
+ "\r\u0005/env -");
+
+ inputSink.write("\011");
+ waitOutput(out, Pattern.quote(getResource("help.env.summary")) + "\n\n" +
+ Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n" +
+ "\r\u0005/env -");
+
+ inputSink.write("\011");
+ waitOutput(out, Pattern.quote(getResource("help.env").replaceAll("\t", " ")) + "\n" +
+ "\r\u0005/env -");
+
+ inputSink.write("\011");
+ waitOutput(out, "-add-exports -add-modules -class-path -module-path \n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.synopsis")) + "\n" +
+ "\r\u0005/env -");
+
+ inputSink.write("\u0003/exit \011");
+ waitOutput(out, Pattern.quote(getResource("help.exit.summary")) + "\n\n" +
+ Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n\r\u0005/exit ");
+ inputSink.write("\011");
+ waitOutput(out, Pattern.quote(getResource("help.exit")) + "\n" +
+ "\r\u0005/exit ");
+ inputSink.write("\011");
+ waitOutput(out, Pattern.quote(getResource("help.exit.summary")) + "\n\n" +
+ Pattern.quote(getResource("jshell.console.see.full.documentation")) + "\n\r\u0005/exit ");
+ inputSink.write("\u0003/doesnotexist\011");
+ waitOutput(out, "\u0005/doesnotexist\n" +
+ Pattern.quote(getResource("jshell.console.no.such.command")) + "\n" +
+ "\n" +
+ "\r\u0005/doesnotexist");
+ });
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ToolTabSnippetTest.java Wed Aug 16 18:42:11 2017 -0700
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8177076
+ * @modules
+ * jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.jshell/jdk.internal.jshell.tool.resources:open
+ * jdk.jshell/jdk.jshell:open
+ * @library /tools/lib
+ * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
+ * @build Compiler UITesting
+ * @build ToolTabSnippetTest
+ * @run testng/timeout=300 ToolTabSnippetTest
+ */
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.regex.Pattern;
+
+import org.testng.annotations.Test;
+
+@Test
+public class ToolTabSnippetTest extends UITesting {
+
+ public void testExpression() throws Exception {
+ Path classes = prepareZip();
+ doRunTest((inputSink, out) -> {
+ inputSink.write("/env -class-path " + classes.toString() + "\n");
+ waitOutput(out, Pattern.quote(getResource("jshell.msg.set.restore")) + "\n\u0005");
+ inputSink.write("import jshelltest.*;\n");
+ waitOutput(out, "\n\u0005");
+
+ //-> <tab>
+ inputSink.write("\011");
+ waitOutput(out, getMessage("jshell.console.completion.all.completions.number", "[0-9]+"));
+ inputSink.write("\011");
+ waitOutput(out, ".*String.*StringBuilder.*\n\r\u0005");
+
+ //new JShellTes<tab>
+ inputSink.write("new JShellTes\011");
+ waitOutput(out, "t\nJShellTest\\( JShellTestAux\\( \n\r\u0005new JShellTest");
+
+ //new JShellTest<tab>
+ inputSink.write("\011");
+ waitOutput(out, "JShellTest\\( JShellTestAux\\( \n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
+ "jshelltest.JShellTest\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
+ "\r\u0005new JShellTest");
+ inputSink.write("\011");
+ waitOutput(out, "jshelltest.JShellTest\n" +
+ "JShellTest 0\n" +
+ "\r\u0005new JShellTest");
+ inputSink.write("\011");
+ waitOutput(out, "JShellTest\\( JShellTestAux\\( \n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
+ "jshelltest.JShellTest\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
+ "\r\u0005new JShellTest");
+
+ //new JShellTest(<tab>
+ inputSink.write("(\011");
+ waitOutput(out, "\\(\n" +
+ Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
+ "JShellTest\\(String str\\)\n" +
+ "JShellTest\\(String str, int i\\)\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
+ "\r\u0005new JShellTest\\(");
+ inputSink.write("\011");
+ waitOutput(out, "JShellTest\\(String str\\)\n" +
+ "JShellTest 1\n" +
+ "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.next.page")) + "\n" +
+ "\r\u0005new JShellTest\\(");
+ inputSink.write("\011");
+ waitOutput(out, "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.next.javadoc")) + "\n" +
+ "\r\u0005new JShellTest\\(");
+ inputSink.write("\011");
+ waitOutput(out, "JShellTest\\(String str, int i\\)\n" +
+ "JShellTest 2\n" +
+ "\n" +
+ getMessage("jshell.console.completion.all.completions.number", "[0-9]+") + "\n" +
+ "\r\u0005new JShellTest\\(");
+ inputSink.write("\011");
+ waitOutput(out, ".*String.*StringBuilder.*\n\r\u0005new JShellTest\\(");
+
+ inputSink.write("\u0003String str = \"\";\nnew JShellTest(");
+ waitOutput(out, "\u0005new JShellTest\\(");
+
+ inputSink.write("\011");
+ waitOutput(out, "\n" +
+ "str \n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
+ "JShellTest\\(String str\\)\n" +
+ "JShellTest\\(String str, int i\\)\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
+ "\r\u0005new JShellTest\\(");
+ inputSink.write("\011");
+ waitOutput(out, "JShellTest\\(String str\\)\n" +
+ "JShellTest 1\n" +
+ "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.next.page")) + "\n" +
+ "\r\u0005new JShellTest\\(");
+ inputSink.write("\011");
+ waitOutput(out, "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.next.javadoc")) + "\n" +
+ "\r\u0005new JShellTest\\(");
+ inputSink.write("\011");
+ waitOutput(out, "JShellTest\\(String str, int i\\)\n" +
+ "JShellTest 2\n" +
+ "\n" +
+ getMessage("jshell.console.completion.all.completions.number", "[0-9]+") + "\n" +
+ "\r\u0005new JShellTest\\(");
+ inputSink.write("\011");
+ waitOutput(out, ".*String.*StringBuilder.*\n\r\u0005new JShellTest\\(");
+
+ inputSink.write("\u0003JShellTest t = new JShellTest\011");
+ waitOutput(out, "\u0005JShellTest t = new JShellTest\n" +
+ "JShellTest\\( \n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.completion.current.signatures")) + "\n" +
+ "jshelltest.JShellTest\n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.completion.all.completions")) + "\n" +
+ "\r\u0005JShellTest t = new JShellTest");
+ inputSink.write("\011");
+ waitOutput(out, "JShellTest\\( JShellTestAux\\( \n" +
+ "\n" +
+ Pattern.quote(getResource("jshell.console.see.documentation")) + "\n" +
+ "\r\u0005JShellTest t = new JShellTest");
+
+ inputSink.write("\u0003JShellTest t = new \011");
+ waitOutput(out, "\u0005JShellTest t = new \n" +
+ "JShellTest\\( \n" +
+ "\n" +
+ getMessage("jshell.console.completion.all.completions.number", "[0-9]+") + "\n" +
+ "\r\u0005JShellTest t = new ");
+ inputSink.write("\011");
+ waitOutput(out, ".*String.*StringBuilder.*\n\r\u0005JShellTest t = new ");
+
+ inputSink.write("\u0003class JShelX{}\n");
+ inputSink.write("new JShel\011");
+ waitOutput(out, "\u0005new JShel\n" +
+ "JShelX\\(\\) JShellTest\\( JShellTestAux\\( \n" +
+ "\r\u0005new JShel");
+
+ //no crash:
+ inputSink.write("\u0003new Stringbuil\011");
+ waitOutput(out, "\u0005new Stringbuil\u0007");
+ });
+ }
+
+ private Path prepareZip() {
+ String clazz1 =
+ "package jshelltest;\n" +
+ "/**JShellTest 0" +
+ " */\n" +
+ "public class JShellTest {\n" +
+ " /**JShellTest 1\n" +
+ " * <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1\n" +
+ " * <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1\n" +
+ " * <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1 <p>1\n" +
+ " */\n" +
+ " public JShellTest(String str) {}\n" +
+ " /**JShellTest 2" +
+ " */\n" +
+ " public JShellTest(String str, int i) {}\n" +
+ "}\n";
+
+ String clazz2 =
+ "package jshelltest;\n" +
+ "/**JShellTestAux 0" +
+ " */\n" +
+ "public class JShellTestAux {\n" +
+ " /**JShellTest 1" +
+ " */\n" +
+ " public JShellTestAux(String str) { }\n" +
+ " /**JShellTest 2" +
+ " */\n" +
+ " public JShellTestAux(String str, int i) { }\n" +
+ "}\n";
+
+ Path srcZip = Paths.get("src.zip");
+
+ try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(srcZip))) {
+ out.putNextEntry(new JarEntry("jshelltest/JShellTest.java"));
+ out.write(clazz1.getBytes());
+ out.putNextEntry(new JarEntry("jshelltest/JShellTestAux.java"));
+ out.write(clazz2.getBytes());
+ } catch (IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+
+ compiler.compile(clazz1, clazz2);
+
+ try {
+ Field availableSources = Class.forName("jdk.jshell.SourceCodeAnalysisImpl").getDeclaredField("availableSourcesOverride");
+ availableSources.setAccessible(true);
+ availableSources.set(null, Arrays.asList(srcZip));
+ } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | ClassNotFoundException ex) {
+ throw new IllegalStateException(ex);
+ }
+
+ return compiler.getClassDir();
+ }
+ //where:
+ private final Compiler compiler = new Compiler();
+
+}