8182270: JShell API: Tools need snippet information without evaluating snippet
8166334: jshell tool: shortcut: expression/statement to method Reviewed-by: jlahoda
This commit is contained in:
parent
1dd6fa2fb6
commit
6573d94b58
@ -64,6 +64,11 @@ import jdk.internal.jline.internal.NonBlockingInputStream;
|
||||
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 @@ class ConsoleIOContext extends IOContext {
|
||||
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) {
|
||||
|
@ -174,10 +174,19 @@ jshell.console.do.nothing = Do nothing
|
||||
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 @@ Shift-<tab> v\n\t\t\
|
||||
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 @@ startup.feedback = \
|
||||
/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
|
||||
|
@ -91,6 +91,9 @@ class Eval {
|
||||
|
||||
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;
|
||||
@ -142,6 +145,23 @@ class Eval {
|
||||
return snippets;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -316,11 +336,15 @@ class Eval {
|
||||
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
|
||||
|
@ -862,7 +862,7 @@ public class JShell implements AutoCloseable {
|
||||
* 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 @@ public class JShell implements AutoCloseable {
|
||||
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;
|
||||
}
|
||||
|
@ -552,6 +552,8 @@ public abstract class Snippet {
|
||||
}
|
||||
}
|
||||
|
||||
static final String UNASSOCIATED_ID = "*UNASSOCIATED*";
|
||||
|
||||
private final Key key;
|
||||
private final String source;
|
||||
private final Wrap guts;
|
||||
|
@ -132,6 +132,34 @@ public abstract class SourceCodeAnalysis {
|
||||
*/
|
||||
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
|
||||
|
@ -532,6 +532,16 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
@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);
|
||||
|
@ -29,6 +29,6 @@ jshell.diag.modifier.single.fatal = Modifier {0} not permitted in top-level decl
|
||||
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}
|
||||
|
161
langtools/test/jdk/jshell/AnalyzeSnippetTest.java
Normal file
161
langtools/test/jdk/jshell/AnalyzeSnippetTest.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
131
langtools/test/jdk/jshell/ToolShiftTabTest.java
Normal file
131
langtools/test/jdk/jshell/ToolShiftTabTest.java
Normal file
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
@ -32,8 +32,8 @@
|
||||
* @library /tools/lib
|
||||
* @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
|
||||
* @build Compiler UITesting
|
||||
* @build MergedTabShiftTabCommandTest
|
||||
* @run testng MergedTabShiftTabCommandTest
|
||||
* @build ToolTabCommandTest
|
||||
* @run testng ToolTabCommandTest
|
||||
*/
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
@ -41,7 +41,7 @@ import java.util.regex.Pattern;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class MergedTabShiftTabCommandTest extends UITesting {
|
||||
public class ToolTabCommandTest extends UITesting {
|
||||
|
||||
public void testCommand() throws Exception {
|
||||
// set terminal height so that help output won't hit page breaks
|
@ -32,8 +32,8 @@
|
||||
* @library /tools/lib
|
||||
* @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
|
||||
* @build Compiler UITesting
|
||||
* @build MergedTabShiftTabExpressionTest
|
||||
* @run testng/timeout=300 MergedTabShiftTabExpressionTest
|
||||
* @build ToolTabSnippetTest
|
||||
* @run testng/timeout=300 ToolTabSnippetTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
@ -49,7 +49,7 @@ import java.util.regex.Pattern;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class MergedTabShiftTabExpressionTest extends UITesting {
|
||||
public class ToolTabSnippetTest extends UITesting {
|
||||
|
||||
public void testExpression() throws Exception {
|
||||
Path classes = prepareZip();
|
Loading…
Reference in New Issue
Block a user