diff --git a/platform/platform-impl/src/com/intellij/terminal/TerminalConsoleContentHelper.java b/platform/platform-impl/src/com/intellij/terminal/TerminalConsoleContentHelper.java new file mode 100644 index 000000000000..7c3bcb9149ae --- /dev/null +++ b/platform/platform-impl/src/com/intellij/terminal/TerminalConsoleContentHelper.java @@ -0,0 +1,67 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.terminal; + +import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.execution.ui.ObservableConsoleView; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.Alarm; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicBoolean; + +public class TerminalConsoleContentHelper implements Disposable { + + private static final int FLUSH_TIMEOUT = 200; + + private final TerminalExecutionConsole myConsole; + private final Collection myChangeListeners = new CopyOnWriteArraySet<>(); + private final Set myContentTypes = ContainerUtil.newConcurrentSet(); + private final Alarm myAlarm; + private final AtomicBoolean myRequested = new AtomicBoolean(false); + private volatile boolean myDisposed = false; + + public TerminalConsoleContentHelper(@NotNull TerminalExecutionConsole console) { + myConsole = console; + myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD /* won't be disposed to call addRequest safely */); + Disposer.register(console, this); + } + + public void addChangeListener(@NotNull ObservableConsoleView.ChangeListener listener, @NotNull Disposable parent) { + myChangeListeners.add(listener); + Disposer.register(parent, () -> myChangeListeners.remove(listener)); + } + + public void onContentTypePrinted(@NotNull ConsoleViewContentType contentType) { + myContentTypes.add(contentType); + if (myRequested.compareAndSet(false, true) && !myDisposed) { + myAlarm.addRequest(this::flush, FLUSH_TIMEOUT); + } + } + + private void flush() { + if (myDisposed) return; + myRequested.set(false); + List contentTypes = new ArrayList<>(myContentTypes); + myContentTypes.removeAll(contentTypes); + for (ConsoleViewContentType contentType : contentTypes) { + fireContentAdded(contentType); + } + } + + private void fireContentAdded(@NotNull ConsoleViewContentType contentType) { + List contentTypes = Collections.singletonList(contentType); + for (ObservableConsoleView.ChangeListener listener : myChangeListeners) { + listener.contentAdded(contentTypes); + } + } + + @Override + public void dispose() { + myDisposed = true; + myAlarm.cancelAllRequests(); + } +} diff --git a/platform/platform-impl/src/com/intellij/terminal/TerminalExecutionConsole.java b/platform/platform-impl/src/com/intellij/terminal/TerminalExecutionConsole.java index d90c011b477b..4e2654dccb2d 100644 --- a/platform/platform-impl/src/com/intellij/terminal/TerminalExecutionConsole.java +++ b/platform/platform-impl/src/com/intellij/terminal/TerminalExecutionConsole.java @@ -49,10 +49,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -65,9 +61,9 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV private final Project myProject; private final AppendableTerminalDataStream myDataStream; private final AtomicBoolean myAttachedToProcess = new AtomicBoolean(false); - private final Collection myChangeListeners = new CopyOnWriteArraySet<>(); private volatile boolean myLastCR = false; private final PendingTasksRunner myOnResizedRunner; + private final TerminalConsoleContentHelper myContentHelper = new TerminalConsoleContentHelper(this); private final TerminalKeyEncoder myKeyEncoder = new TerminalKeyEncoder(); @@ -102,13 +98,12 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV if (contentType != null) { myDataStream.append((char)CharUtils.ESC + "[39m"); //restore color } - fireContentAdded(ObjectUtils.notNull(contentType, ConsoleViewContentType.NORMAL_OUTPUT)); + myContentHelper.onContentTypePrinted(ObjectUtils.notNull(contentType, ConsoleViewContentType.NORMAL_OUTPUT)); } @Override public void addChangeListener(@NotNull ChangeListener listener, @NotNull Disposable parent) { - myChangeListeners.add(listener); - Disposer.register(parent, () -> myChangeListeners.remove(listener)); + myContentHelper.addChangeListener(listener, parent); } private static String encodeColor(Color color) { @@ -156,13 +151,6 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV return textCRLF; } - private void fireContentAdded(@NotNull ConsoleViewContentType contentType) { - List contentTypes = Collections.singletonList(contentType); - for (ChangeListener listener : myChangeListeners) { - listener.contentAdded(contentTypes); - } - } - /** * Clears history and screen buffers, positions the cursor at the top left corner. */