8185426: Jshell crashing on autocompletion
Properly canceling completion on <tab> if needed. Reviewed-by: rfield
This commit is contained in:
parent
701dfdd390
commit
698882e467
@ -55,7 +55,6 @@ import jdk.internal.jline.TerminalSupport;
|
||||
import jdk.internal.jline.WindowsTerminal;
|
||||
import jdk.internal.jline.console.ConsoleReader;
|
||||
import jdk.internal.jline.console.KeyMap;
|
||||
import jdk.internal.jline.console.Operation;
|
||||
import jdk.internal.jline.console.UserInterruptException;
|
||||
import jdk.internal.jline.console.history.History;
|
||||
import jdk.internal.jline.console.history.MemoryHistory;
|
||||
@ -94,14 +93,14 @@ class ConsoleIOContext extends IOContext {
|
||||
term = new JShellUnixTerminal(input);
|
||||
}
|
||||
term.init();
|
||||
List<CompletionTask> completionTODO = new ArrayList<>();
|
||||
CompletionState completionState = new CompletionState();
|
||||
in = new ConsoleReader(cmdin, cmdout, term) {
|
||||
@Override public KeyMap getKeys() {
|
||||
return new CheckCompletionKeyMap(super.getKeys(), completionTODO);
|
||||
return new CheckCompletionKeyMap(super.getKeys(), completionState);
|
||||
}
|
||||
@Override
|
||||
protected boolean complete() throws IOException {
|
||||
return ConsoleIOContext.this.complete(completionTODO);
|
||||
return ConsoleIOContext.this.complete(completionState);
|
||||
}
|
||||
};
|
||||
in.setExpandEvents(false);
|
||||
@ -201,15 +200,19 @@ class ConsoleIOContext extends IOContext {
|
||||
private static final String LINE_SEPARATORS2 = LINE_SEPARATOR + LINE_SEPARATOR;
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
private boolean complete(List<CompletionTask> todo) {
|
||||
private boolean complete(CompletionState completionState) {
|
||||
//The completion has multiple states (invoked by subsequent presses of <tab>).
|
||||
//On the first invocation in a given sequence, all steps are precomputed
|
||||
//and placed into the todo list. The todo list is then followed on both the first
|
||||
//and subsequent <tab> presses:
|
||||
//and placed into the todo list (completionState.todo). The todo list is
|
||||
//then followed on both the first and subsequent completion invocations:
|
||||
try {
|
||||
String text = in.getCursorBuffer().toString();
|
||||
int cursor = in.getCursorBuffer().cursor;
|
||||
if (todo.isEmpty()) {
|
||||
|
||||
List<CompletionTask> todo = completionState.todo;
|
||||
|
||||
if (todo.isEmpty() || completionState.actionCount != 1) {
|
||||
ConsoleIOContextTestSupport.willComputeCompletion();
|
||||
int[] anchor = new int[] {-1};
|
||||
List<Suggestion> suggestions;
|
||||
List<String> doc;
|
||||
@ -237,6 +240,8 @@ class ConsoleIOContext extends IOContext {
|
||||
CompletionTask ordinaryCompletion = new OrdinaryCompletionTask(suggestions, anchor[0], !command && !doc.isEmpty(), hasSmart);
|
||||
CompletionTask allCompletion = new AllSuggestionsCompletionTask(suggestions, anchor[0]);
|
||||
|
||||
todo = new ArrayList<>();
|
||||
|
||||
//the main decission tree:
|
||||
if (command) {
|
||||
CompletionTask shortDocumentation = new CommandSynopsisTask(doc);
|
||||
@ -310,6 +315,9 @@ class ConsoleIOContext extends IOContext {
|
||||
}
|
||||
}
|
||||
|
||||
completionState.actionCount = 0;
|
||||
completionState.todo = todo;
|
||||
|
||||
if (repaint) {
|
||||
in.redrawLine();
|
||||
in.flush();
|
||||
@ -1203,12 +1211,12 @@ class ConsoleIOContext extends IOContext {
|
||||
private static final class CheckCompletionKeyMap extends KeyMap {
|
||||
|
||||
private final KeyMap del;
|
||||
private final List<CompletionTask> completionTODO;
|
||||
private final CompletionState completionState;
|
||||
|
||||
public CheckCompletionKeyMap(KeyMap del, List<CompletionTask> completionTODO) {
|
||||
public CheckCompletionKeyMap(KeyMap del, CompletionState completionState) {
|
||||
super(del.getName(), del.isViKeyMap());
|
||||
this.del = del;
|
||||
this.completionTODO = completionTODO;
|
||||
this.completionState = completionState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1233,13 +1241,9 @@ class ConsoleIOContext extends IOContext {
|
||||
|
||||
@Override
|
||||
public Object getBound(CharSequence keySeq) {
|
||||
Object res = del.getBound(keySeq);
|
||||
this.completionState.actionCount++;
|
||||
|
||||
if (res != Operation.COMPLETE) {
|
||||
completionTODO.clear();
|
||||
}
|
||||
|
||||
return res;
|
||||
return del.getBound(keySeq);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1252,4 +1256,12 @@ class ConsoleIOContext extends IOContext {
|
||||
return "check: " + del.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CompletionState {
|
||||
/**The number of actions since the last completion invocation. Will be 1 when completion is
|
||||
* invoked immediately after the last completion invocation.*/
|
||||
public int actionCount;
|
||||
/**Precomputed completion actions. Should only be reused if actionCount == 1.*/
|
||||
public List<CompletionTask> todo = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package jdk.internal.jshell.tool;
|
||||
|
||||
/**A support class to the ConsoleIOContext, containing callbacks called
|
||||
* on important points in ConsoleIOContext.
|
||||
*/
|
||||
public abstract class ConsoleIOContextTestSupport {
|
||||
|
||||
public static ConsoleIOContextTestSupport IMPL;
|
||||
|
||||
public static void willComputeCompletion() {
|
||||
if (IMPL != null)
|
||||
IMPL.willComputeCompletionCallback();
|
||||
}
|
||||
|
||||
protected abstract void willComputeCompletionCallback();
|
||||
|
||||
}
|
@ -23,10 +23,11 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8177076
|
||||
* @bug 8177076 8185426
|
||||
* @modules
|
||||
* jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.jshell/jdk.internal.jshell.tool
|
||||
* jdk.jshell/jdk.internal.jshell.tool.resources:open
|
||||
* jdk.jshell/jdk.jshell:open
|
||||
* @library /tools/lib
|
||||
@ -42,10 +43,12 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.internal.jshell.tool.ConsoleIOContextTestSupport;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
@ -191,6 +194,39 @@ public class ToolTabSnippetTest extends UITesting {
|
||||
});
|
||||
}
|
||||
|
||||
public void testCleaningCompletionTODO() throws Exception {
|
||||
doRunTest((inputSink, out) -> {
|
||||
CountDownLatch testCompleteComputationStarted = new CountDownLatch(1);
|
||||
CountDownLatch testCompleteComputationContinue = new CountDownLatch(1);
|
||||
ConsoleIOContextTestSupport.IMPL = new ConsoleIOContextTestSupport() {
|
||||
@Override
|
||||
protected void willComputeCompletionCallback() {
|
||||
if (testCompleteComputationStarted != null) {
|
||||
testCompleteComputationStarted.countDown();
|
||||
}
|
||||
if (testCompleteComputationContinue != null) {
|
||||
try {
|
||||
testCompleteComputationContinue.await();
|
||||
} catch (InterruptedException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
//-> <tab>
|
||||
inputSink.write("\011");
|
||||
testCompleteComputationStarted.await();
|
||||
//-> <tab><tab>
|
||||
inputSink.write("\011\011");
|
||||
testCompleteComputationContinue.countDown();
|
||||
waitOutput(out, "\u0005");
|
||||
//-> <tab>
|
||||
inputSink.write("\011");
|
||||
waitOutput(out, "\u0005");
|
||||
ConsoleIOContextTestSupport.IMPL = null;
|
||||
});
|
||||
}
|
||||
|
||||
private Path prepareZip() {
|
||||
String clazz1 =
|
||||
"package jshelltest;\n" +
|
||||
|
Loading…
Reference in New Issue
Block a user