typing in console was broken

This commit is contained in:
Alexey Kudravtsev
2017-01-11 15:38:13 +03:00
parent e5a9d79dd4
commit 91f176c4a8
3 changed files with 72 additions and 56 deletions
@@ -101,16 +101,15 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
public static final Key<ConsoleViewImpl> CONSOLE_VIEW_IN_EDITOR_VIEW = Key.create("CONSOLE_VIEW_IN_EDITOR_VIEW");
private static final Key<ConsoleViewContentType> CONTENT_TYPE = Key.create("ConsoleViewContentType");
private static final Key<Boolean> USER_INPUT_SENT = Key.create("USER_INPUT_SENT");
private static boolean ourTypedHandlerInitialized;
private final Alarm myFlushUserInputAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this);
private final StringBuilder myUserInputText = new StringBuilder(); // guarded by LOCK
private static final CharMatcher NEW_LINE_MATCHER = CharMatcher.anyOf("\n\r");
private static synchronized void initTypedHandler() {
if (ourTypedHandlerInitialized) return;
final EditorActionManager actionManager = EditorActionManager.getInstance();
final TypedAction typedAction = actionManager.getTypedAction();
TypedAction typedAction = EditorActionManager.getInstance().getTypedAction();
typedAction.setupHandler(new MyTypedHandler(typedAction.getHandler()));
ourTypedHandlerInitialized = true;
}
@@ -521,6 +520,8 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
void waitAllRequests() {
myFlushAlarm.flush();
myFlushUserInputAlarm.flush();
myFlushAlarm.flush();
myFlushUserInputAlarm.flush();
}
protected void disposeEditor() {
@@ -564,7 +565,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
myDeferredBuffer.print(text, contentType, info);
if (contentType == ConsoleViewContentType.USER_INPUT) {
sendUserInput(text);
requestFlushImmediately();
}
if (myEditor != null) {
@@ -574,17 +574,29 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
}
}
private void sendUserInput(@NotNull String text) {
if (myState.isRunning()) {
myUserInputText.append(text);
if (NEW_LINE_MATCHER.indexIn(text) >= 0) {
String textToSend = myUserInputText.toString();
myUserInputText.setLength(0);
private void sendUserInput(@NotNull CharSequence typedText) {
if (myState.isRunning() && NEW_LINE_MATCHER.indexIn(typedText) >= 0) {
StringBuilder textToSend = new StringBuilder();
// compute text input from the console contents:
// all range markers beginning from the caret offset backwards, marked as user input and not marked as already sent
for (RangeMarker marker = findTokenMarker(myEditor.getCaretModel().getOffset());
marker != null;
marker = ((RangeMarkerImpl)marker).findRangeMarkerBefore()) {
ConsoleViewContentType tokenType = getTokenType(marker);
if (tokenType != null) {
if (tokenType != ConsoleViewContentType.USER_INPUT || marker.getUserData(USER_INPUT_SENT) == Boolean.TRUE) {
break;
}
marker.putUserData(USER_INPUT_SENT, true);
textToSend.insert(0, marker.getDocument().getText(TextRange.create(marker)));
}
}
if (textToSend.length() != 0) {
myFlushUserInputAlarm.addRequest(() -> {
if (myState.isRunning()) {
try {
// this may block forever, see IDEA-54340
myState.sendUserInput(textToSend);
myState.sendUserInput(textToSend.toString());
}
catch (IOException ignored) {
}
@@ -713,6 +725,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
if (shouldStickToEnd) {
scrollToEnd();
}
sendUserInput(addedText);
}
private void createTokenRangeMarker(@NotNull Document document,
@@ -1111,7 +1124,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
return marker[0];
}
private static ConsoleViewContentType getTokenType(RangeMarker m) {
private static ConsoleViewContentType getTokenType(@Nullable RangeMarker m) {
return m == null ? null : m.getUserData(CONTENT_TYPE);
}
@@ -1498,6 +1511,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
}
moveScrollRemoveSelection(editor, newEndOffset);
sendUserInput(text);
}
private void replaceUserText(int start, int end, @NotNull String text) {
@@ -1506,8 +1520,10 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
final Document document = editor.getDocument();
document.replaceString(start, end, text);
int offset = start + text.length();
moveScrollRemoveSelection(editor, offset);
sendUserInput(text);
}
private static void moveScrollRemoveSelection(@NotNull Editor editor, int offset) {
@@ -27,9 +27,9 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.openapi.editor.actionSystem.TypedAction;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.LightPlatformCodeInsightTestCase;
@@ -42,13 +42,10 @@ import com.intellij.util.TimeoutUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.assertj.core.api.Assertions.assertThat;
@@ -271,42 +268,9 @@ public class ConsoleViewImplTest extends LightPlatformTestCase {
}
public void testUserInputIsSentToProcessAfterNewLinePressed() {
byte[] buffer = new byte[10000];
BufferExposingByteArrayOutputStream outputStream = new BufferExposingByteArrayOutputStream(buffer);
BufferExposingByteArrayInputStream inputStream = new BufferExposingByteArrayInputStream(buffer);
AtomicBoolean finished = new AtomicBoolean();
Process testProcess = new Process() {
@Override
public OutputStream getOutputStream() {
return outputStream;
}
Process testProcess = AnsiEscapeDecoderTest.createTestProcess();
BufferExposingByteArrayOutputStream outputStream = (BufferExposingByteArrayOutputStream)testProcess.getOutputStream();
@Override
public InputStream getInputStream() {
return inputStream;
}
@Override
public InputStream getErrorStream() {
return inputStream;
}
@Override
public int waitFor() {
while (!finished.get());
return 0;
}
@Override
public int exitValue() {
return 0;
}
@Override
public void destroy() {
finished.set(true);
}
};
AnsiEscapeDecoderTest.withProcessHandlerFrom(testProcess, handler ->
withCycleConsole(100, console -> {
console.attachToProcess(handler);
@@ -323,4 +287,35 @@ public class ConsoleViewImplTest extends LightPlatformTestCase {
assertEquals("IK\n", outputStream.toString());
}));
}
public void testUserTypingIsSentToProcessAfterNewLinePressed() {
Process testProcess = AnsiEscapeDecoderTest.createTestProcess();
BufferExposingByteArrayOutputStream outputStream = (BufferExposingByteArrayOutputStream)testProcess.getOutputStream();
AnsiEscapeDecoderTest.withProcessHandlerFrom(testProcess, handler ->
withCycleConsole(100, console -> {
console.attachToProcess(handler);
outputStream.reset();
Editor editor = console.getEditor();
typeIn(editor, 'X');
console.waitAllRequests();
assertEquals(0, outputStream.size());
typeIn(editor, 'Y');
console.waitAllRequests();
assertEquals(0, outputStream.size());
typeIn(editor, '\n');
console.waitAllRequests();
assertEquals(3, outputStream.size());
assertEquals("XY\n", outputStream.toString());
}));
}
private static void typeIn(Editor editor, char c) {
TypedAction action = EditorActionManager.getInstance().getTypedAction();
DataContext dataContext = ((EditorEx)editor).getDataContext();
action.actionPerformed(editor, c, dataContext);
}
}
@@ -100,12 +100,13 @@ public class AnsiEscapeDecoderTest extends PlatformTestCase {
};
}
public void testPerformance() throws IOException {
byte[] buffer = new byte[100000];
@NotNull
public static Process createTestProcess() {
byte[] buffer = new byte[1000];
BufferExposingByteArrayOutputStream outputStream = new BufferExposingByteArrayOutputStream(buffer);
BufferExposingByteArrayInputStream inputStream = new BufferExposingByteArrayInputStream(buffer);
AtomicBoolean finished = new AtomicBoolean();
Process testProcess = new Process() {
return new Process() {
@Override
public OutputStream getOutputStream() {
return outputStream;
@@ -137,6 +138,10 @@ public class AnsiEscapeDecoderTest extends PlatformTestCase {
finished.set(true);
}
};
}
public void testPerformance() throws IOException {
Process testProcess = createTestProcess();
withProcessHandlerFrom(testProcess, handler -> {
PlatformTestUtil.startPerformanceTest("ansi color", 15000, ()->{
@@ -148,14 +153,14 @@ public class AnsiEscapeDecoderTest extends PlatformTestCase {
});
}
public static void withProcessHandlerFrom(@NotNull Process testProcess, @NotNull Consumer<ProcessHandler> consumer) {
public static void withProcessHandlerFrom(@NotNull Process testProcess, @NotNull Consumer<ProcessHandler> actionToTest) {
KillableColoredProcessHandler handler = new KillableColoredProcessHandler(testProcess, "testProcess");
handler.setShouldDestroyProcessRecursively(false);
handler.startNotify();
handler.notifyTextAvailable("Running stuff...\n", ProcessOutputTypes.STDOUT);
try {
consumer.consume(handler);
actionToTest.consume(handler);
}
finally {
handler.doDestroyProcess();