mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-152620 Move Filter#applyFilter from EDT to the pooled thread
This commit is contained in:
@@ -68,7 +68,6 @@ import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.ui.EditorNotificationPanel;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.util.*;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
@@ -133,14 +132,12 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
|
||||
private boolean myLastStickingToEnd;
|
||||
private boolean myCancelStickToEnd;
|
||||
|
||||
private boolean myTooMuchOfOutput;
|
||||
private boolean myInDocumentUpdate;
|
||||
|
||||
// If true, then a document is being cleared right now.
|
||||
// Should be accessed in EDT only.
|
||||
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
|
||||
private boolean myDocumentClearing;
|
||||
private int myLastAddedTextLength;
|
||||
private int consoleTooMuchTextBufferRatio;
|
||||
|
||||
public Editor getEditor() {
|
||||
@@ -773,39 +770,8 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
|
||||
}
|
||||
}
|
||||
myPsiDisposedCheck.performCheck();
|
||||
myLastAddedTextLength = addedText.length();
|
||||
if (!myTooMuchOfOutput) {
|
||||
if (isTheAmountOfTextTooBig(myLastAddedTextLength)) { // disable hyperlinks and folding until new output arriving slows down again
|
||||
myTooMuchOfOutput = true;
|
||||
final EditorNotificationPanel comp =
|
||||
new EditorNotificationPanel().text("Too much output to process").icon(AllIcons.General.ExclMark);
|
||||
final Alarm tooMuchOutputAlarm = new Alarm();
|
||||
//show the notification with a delay to avoid blinking when "too much output" ceases quickly
|
||||
tooMuchOutputAlarm.addRequest(() -> add(comp, BorderLayout.NORTH), 300);
|
||||
performWhenNoDeferredOutput(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isTheAmountOfTextTooBig(myLastAddedTextLength)) {
|
||||
try {
|
||||
highlightHyperlinksAndFoldings(lastProcessedOutput);
|
||||
}
|
||||
finally {
|
||||
myTooMuchOfOutput = false;
|
||||
remove(comp);
|
||||
tooMuchOutputAlarm.cancelAllRequests();
|
||||
}
|
||||
}
|
||||
else {
|
||||
myLastAddedTextLength = 0;
|
||||
performLaterWhenNoDeferredOutput(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
highlightHyperlinksAndFoldings(lastProcessedOutput);
|
||||
}
|
||||
}
|
||||
|
||||
highlightHyperlinksAndFoldings(lastProcessedOutput);
|
||||
|
||||
if (shouldStickToEnd) {
|
||||
scrollToEnd();
|
||||
@@ -1083,7 +1049,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
|
||||
additionalAttributes);
|
||||
}
|
||||
else {
|
||||
myHyperlinks.highlightHyperlinks(additionalHighlight, myFilters);
|
||||
myHyperlinks.highlightHyperlinks(additionalHighlight, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.execution.impl;
|
||||
|
||||
import com.intellij.execution.filters.Filter;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.NullableComputable;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.util.TimeoutUtil;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import one.util.streamex.IntStreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
class AsyncFilterRunner {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.execution.impl.FilterRunner");
|
||||
private static final ExecutorService ourExecutor = AppExecutorUtil.createBoundedApplicationPoolExecutor("console filters", 1);
|
||||
private final EditorHyperlinkSupport myHyperlinks;
|
||||
private final Editor myEditor;
|
||||
|
||||
AsyncFilterRunner(EditorHyperlinkSupport hyperlinks, Editor editor) {
|
||||
myHyperlinks = hyperlinks;
|
||||
myEditor = editor;
|
||||
}
|
||||
|
||||
void highlightHyperlinks(final Filter customFilter, final int startLine, final int endLine) {
|
||||
Computable<FilterResults> bgComputation = highlightHyperlinksAsync(customFilter, startLine, endLine);
|
||||
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
|
||||
bgComputation.compute().applyHighlights(myHyperlinks);
|
||||
} else {
|
||||
runFiltersInBackground(bgComputation);
|
||||
}
|
||||
}
|
||||
|
||||
private void runFiltersInBackground(Computable<FilterResults> bgComputation) {
|
||||
AtomicBoolean handled = new AtomicBoolean();
|
||||
Future<FilterResults> future = ourExecutor.submit(() -> {
|
||||
FilterResults results = computeWithWritePriority(bgComputation);
|
||||
if (!results.myResults.isEmpty()) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> results.applyHighlights(myHyperlinks), ModalityState.any(), o -> handled.get());
|
||||
}
|
||||
return results;
|
||||
});
|
||||
handleSynchronouslyIfQuick(handled, future);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private FilterResults computeWithWritePriority(Computable<FilterResults> bgComputation) {
|
||||
Ref<FilterResults> applyResults = Ref.create(FilterResults.EMPTY);
|
||||
Runnable computeInReadAction = () -> {
|
||||
if (myEditor.isDisposed()) return;
|
||||
applyResults.set(bgComputation.compute());
|
||||
};
|
||||
while (!ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(computeInReadAction)) {
|
||||
TimeoutUtil.sleep(10);
|
||||
}
|
||||
return applyResults.get();
|
||||
}
|
||||
|
||||
private void handleSynchronouslyIfQuick(AtomicBoolean handled, Future<FilterResults> future) {
|
||||
try {
|
||||
future.get(5, TimeUnit.MILLISECONDS).applyHighlights(myHyperlinks);
|
||||
handled.set(true);
|
||||
}
|
||||
catch (TimeoutException ignored) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Computable<FilterResults> highlightHyperlinksAsync(Filter filter, int startLine, int endLine) {
|
||||
Document document = myEditor.getDocument();
|
||||
int markerOffset = document.getLineEndOffset(endLine);
|
||||
RangeMarker marker = document.createRangeMarker(markerOffset, markerOffset);
|
||||
List<LineHighlighter> tasks = IntStreamEx.rangeClosed(startLine, endLine).mapToObj(line -> processLine(document, filter, line)).toList();
|
||||
return () -> {
|
||||
List<Filter.Result> results = new ArrayList<>();
|
||||
for (LineHighlighter task : tasks) {
|
||||
if (!marker.isValid()) return FilterResults.EMPTY;
|
||||
ContainerUtil.addIfNotNull(results, task.compute());
|
||||
}
|
||||
return new FilterResults(markerOffset, marker, results);
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static LineHighlighter processLine(Document document, Filter filter, int line) {
|
||||
int lineEnd = document.getLineEndOffset(line);
|
||||
int endOffset = lineEnd + (lineEnd < document.getTextLength() ? 1 /* for \n */ : 0);
|
||||
String text = EditorHyperlinkSupport.getLineText(document, line, true);
|
||||
return () -> checkRange(filter, endOffset, filter.applyFilter(text, endOffset));
|
||||
}
|
||||
|
||||
private static Filter.Result checkRange(Filter filter, int endOffset, Filter.Result result) {
|
||||
if (result != null) {
|
||||
for (Filter.ResultItem resultItem : result.getResultItems()) {
|
||||
int start = resultItem.getHighlightStartOffset();
|
||||
int end = resultItem.getHighlightEndOffset();
|
||||
if (end < start || end > endOffset) {
|
||||
LOG.error("Filter returned wrong range: start=" + start + "; end=" + end + "; max=" + endOffset + "; filter=" + filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private interface LineHighlighter extends NullableComputable<Filter.Result> { }
|
||||
|
||||
private static class FilterResults {
|
||||
static final FilterResults EMPTY = new FilterResults(0, null, Collections.emptyList());
|
||||
private int myInitialMarkerOffset;
|
||||
private RangeMarker myMarker;
|
||||
private List<Filter.Result> myResults;
|
||||
|
||||
FilterResults(int initialMarkerOffset, RangeMarker marker, List<Filter.Result> results) {
|
||||
myInitialMarkerOffset = initialMarkerOffset;
|
||||
myMarker = marker;
|
||||
myResults = results;
|
||||
}
|
||||
|
||||
void applyHighlights(EditorHyperlinkSupport hyperlinks) {
|
||||
if (myResults.isEmpty() || !myMarker.isValid()) return;
|
||||
|
||||
int delta = myMarker.getStartOffset() - myInitialMarkerOffset;
|
||||
for (Filter.Result result : myResults) {
|
||||
hyperlinks.highlightHyperlinks(result, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -67,10 +67,12 @@ public class EditorHyperlinkSupport {
|
||||
|
||||
private final Editor myEditor;
|
||||
@NotNull private final Project myProject;
|
||||
private final AsyncFilterRunner myFilterRunner;
|
||||
|
||||
public EditorHyperlinkSupport(@NotNull final Editor editor, @NotNull final Project project) {
|
||||
myEditor = editor;
|
||||
myProject = project;
|
||||
myFilterRunner = new AsyncFilterRunner(this, myEditor);
|
||||
|
||||
editor.addEditorMouseListener(new EditorMouseAdapter() {
|
||||
@Override
|
||||
@@ -262,30 +264,15 @@ public class EditorHyperlinkSupport {
|
||||
}
|
||||
|
||||
public void highlightHyperlinks(final Filter customFilter, final int line1, final int endLine) {
|
||||
final Document document = myEditor.getDocument();
|
||||
|
||||
final int startLine = Math.max(0, line1);
|
||||
|
||||
for (int line = startLine; line <= endLine; line++) {
|
||||
int endOffset = document.getLineEndOffset(line);
|
||||
if (endOffset < document.getTextLength()) {
|
||||
endOffset++; // add '\n'
|
||||
}
|
||||
final String text = getLineText(document, line, true);
|
||||
Filter.Result result = customFilter.applyFilter(text, endOffset);
|
||||
if (result != null) {
|
||||
highlightHyperlinks(result, customFilter);
|
||||
}
|
||||
}
|
||||
myFilterRunner.highlightHyperlinks(customFilter, Math.max(0, line1), endLine);
|
||||
}
|
||||
|
||||
void highlightHyperlinks(@NotNull Filter.Result result, @NotNull Filter filter) {
|
||||
void highlightHyperlinks(@NotNull Filter.Result result, int offsetDelta) {
|
||||
Document document = myEditor.getDocument();
|
||||
for (Filter.ResultItem resultItem : result.getResultItems()) {
|
||||
int start = resultItem.getHighlightStartOffset();
|
||||
int end = resultItem.getHighlightEndOffset();
|
||||
if (end < start || end > document.getTextLength()) {
|
||||
LOG.error("Filter returned wrong range: start=" + start + "; end=" + end + "; length=" + document.getTextLength() + "; filter=" + filter);
|
||||
int start = resultItem.getHighlightStartOffset() + offsetDelta;
|
||||
int end = resultItem.getHighlightEndOffset() + offsetDelta;
|
||||
if (start < 0 || end < start || end > document.getTextLength()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -382,7 +369,7 @@ public class EditorHyperlinkSupport {
|
||||
if (includeEol && endOffset < document.getTextLength()) {
|
||||
endOffset++;
|
||||
}
|
||||
return document.getCharsSequence().subSequence(document.getLineStartOffset(lineNumber), endOffset).toString();
|
||||
return document.getImmutableCharSequence().subSequence(document.getLineStartOffset(lineNumber), endOffset).toString();
|
||||
}
|
||||
|
||||
private static class HyperlinkInfoTextAttributes extends TextAttributes {
|
||||
|
||||
Reference in New Issue
Block a user