mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
minor optimization: do not reallocate lists twice on each highlight visitor instantiation
GitOrigin-RevId: 4f6286ee96d8e2a8bbcb8f1731888088e44e64bd
This commit is contained in:
committed by
intellij-monorepo-bot
parent
715ddb123a
commit
164eedcb9d
@@ -797,6 +797,8 @@ com.intellij.codeInsight.daemon.impl.HighlightRangeExtension
|
||||
- a:isForceHighlightParents(com.intellij.psi.PsiFile):Z
|
||||
com.intellij.codeInsight.daemon.impl.HighlightVisitor
|
||||
- com.intellij.openapi.project.PossiblyDumbAware
|
||||
- sf:ARRAY_FACTORY:com.intellij.util.ArrayFactory
|
||||
- sf:EMPTY_ARRAY:com.intellij.codeInsight.daemon.impl.HighlightVisitor[]
|
||||
- sf:EP_HIGHLIGHT_VISITOR:com.intellij.openapi.extensions.ExtensionPointName
|
||||
- a:analyze(com.intellij.psi.PsiFile,Z,com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder,java.lang.Runnable):Z
|
||||
- a:clone():com.intellij.codeInsight.daemon.impl.HighlightVisitor
|
||||
|
||||
@@ -92,10 +92,10 @@ public /*sealed */class GeneralHighlightingPass extends ProgressableTextEditorHi
|
||||
// initial guess to show correct progress in the traffic light icon
|
||||
setProgressLimit(document.getTextLength()/2); // approx number of PSI elements = file length/2
|
||||
EditorColorsScheme globalScheme = editor != null ? editor.getColorsScheme() : EditorColorsManager.getInstance().getGlobalScheme();
|
||||
myHighlightVisitorRunner = new HighlightVisitorRunner(myProject, globalScheme);
|
||||
myHighlightVisitorRunner = new HighlightVisitorRunner(psiFile, globalScheme);
|
||||
if (!runVisitors) {
|
||||
// "do not run visitors" here means "reduce the set of visitors down to DefaultHighlightVisitor", because it reports error elements
|
||||
myHighlightVisitorRunner.setHighlightVisitorProducer(__ -> List.of(new DefaultHighlightVisitor(psiFile.getProject(), highlightErrorElements, false)));
|
||||
myHighlightVisitorRunner.setHighlightVisitorProducer(__ -> new HighlightVisitor[]{new DefaultHighlightVisitor(psiFile.getProject(), highlightErrorElements, false)});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@ import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.ArrayFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface HighlightVisitor extends PossiblyDumbAware {
|
||||
HighlightVisitor [] EMPTY_ARRAY = new HighlightVisitor[0];
|
||||
ArrayFactory<HighlightVisitor> ARRAY_FACTORY = count -> count == 0 ? EMPTY_ARRAY : new HighlightVisitor[count];
|
||||
ExtensionPointName<HighlightVisitor> EP_HIGHLIGHT_VISITOR = new ExtensionPointName<>("com.intellij.highlightVisitor");
|
||||
|
||||
boolean suitableForFile(@NotNull PsiFile file);
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.openapi.util.UserDataHolderEx;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.serviceContainer.AlreadyDisposedException;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ExceptionUtil;
|
||||
import com.intellij.util.TriConsumer;
|
||||
@@ -30,72 +31,72 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class HighlightVisitorRunner {
|
||||
private final Project myProject;
|
||||
private final PsiFile myPsiFile;
|
||||
private @Nullable final TextAttributesScheme myColorsScheme;
|
||||
|
||||
HighlightVisitorRunner(@NotNull Project project, @Nullable TextAttributesScheme scheme) {
|
||||
myProject = project;
|
||||
HighlightVisitorRunner(@NotNull PsiFile psiFile, @Nullable TextAttributesScheme scheme) {
|
||||
myPsiFile = psiFile;
|
||||
myColorsScheme = scheme;
|
||||
}
|
||||
|
||||
void setHighlightVisitorProducer(@NotNull IntFunction<? extends @NotNull List<HighlightVisitor>> highlightVisitorProducer) {
|
||||
void setHighlightVisitorProducer(@NotNull Function<? super Boolean, ? extends @NotNull HighlightVisitor @NotNull []> highlightVisitorProducer) {
|
||||
myHighlightVisitorProducer = highlightVisitorProducer;
|
||||
}
|
||||
|
||||
private static final PassRunningAssert HIGHLIGHTING_PERFORMANCE_ASSERT =
|
||||
new PassRunningAssert("the expensive method should not be called inside the highlighting pass");
|
||||
private volatile @NotNull IntFunction<? extends @NotNull List<HighlightVisitor>> myHighlightVisitorProducer = this::cloneHighlightVisitors;
|
||||
private volatile @NotNull Function<? super Boolean, ? extends @NotNull HighlightVisitor @NotNull []> myHighlightVisitorProducer = this::cloneAndFilterHighlightVisitors;
|
||||
static void assertHighlightingPassNotRunning() {
|
||||
HIGHLIGHTING_PERFORMANCE_ASSERT.assertPassNotRunning();
|
||||
}
|
||||
|
||||
private static final Key<AtomicInteger> HIGHLIGHT_VISITOR_INSTANCE_COUNT = new Key<>("HIGHLIGHT_VISITOR_INSTANCE_COUNT");
|
||||
|
||||
private @NotNull List<HighlightVisitor> cloneHighlightVisitors(int oldCount) {
|
||||
List<HighlightVisitor> highlightVisitors = HighlightVisitor.EP_HIGHLIGHT_VISITOR.getExtensionList(myProject);
|
||||
if (oldCount != 0) {
|
||||
HighlightVisitor[] clones = new HighlightVisitor[highlightVisitors.size()];
|
||||
for (int i = 0; i < highlightVisitors.size(); i++) {
|
||||
HighlightVisitor highlightVisitor = highlightVisitors.get(i);
|
||||
HighlightVisitor cloned = highlightVisitor.clone();
|
||||
assert cloned.getClass() == highlightVisitor.getClass() : highlightVisitor.getClass()+".clone() must return a copy of "+highlightVisitor.getClass()+"; but got: "+cloned+" of "+cloned.getClass();
|
||||
clones[i] = cloned;
|
||||
}
|
||||
// List.of will copy the array - do not use it
|
||||
return Arrays.asList(clones);
|
||||
}
|
||||
return highlightVisitors;
|
||||
}
|
||||
|
||||
private HighlightVisitor @NotNull [] filterVisitors(@NotNull List<? extends HighlightVisitor> highlightVisitors, @NotNull PsiFile psiFile) {
|
||||
List<HighlightVisitor> visitors = new ArrayList<>(highlightVisitors.size());
|
||||
for (HighlightVisitor visitor : DumbService.getInstance(myProject).filterByDumbAwareness(highlightVisitors)) {
|
||||
|
||||
if (visitor instanceof RainbowVisitor
|
||||
&& !RainbowHighlighter.isRainbowEnabledWithInheritance(myColorsScheme, psiFile.getLanguage())) {
|
||||
private @NotNull HighlightVisitor @NotNull [] cloneAndFilterHighlightVisitors(boolean mustClone) {
|
||||
Project project = myPsiFile.getProject();
|
||||
HighlightVisitor[] visitors = HighlightVisitor.EP_HIGHLIGHT_VISITOR.getExtensions(project);
|
||||
DumbService dumbService = DumbService.getInstance(project);
|
||||
int o = 0;
|
||||
HighlightVisitor[] clones = new HighlightVisitor[visitors.length];
|
||||
for (HighlightVisitor visitor : visitors) {
|
||||
if (!dumbService.isUsableInCurrentContext(visitor)) {
|
||||
continue;
|
||||
}
|
||||
if (visitor.suitableForFile(psiFile)) {
|
||||
visitors.add(visitor);
|
||||
if (visitor instanceof RainbowVisitor
|
||||
&& !RainbowHighlighter.isRainbowEnabledWithInheritance(myColorsScheme, myPsiFile.getLanguage())) {
|
||||
continue;
|
||||
}
|
||||
if (visitor.suitableForFile(myPsiFile)) {
|
||||
HighlightVisitor cloned;
|
||||
if (mustClone) {
|
||||
cloned = visitor.clone();
|
||||
assert cloned.getClass() == visitor.getClass() : visitor.getClass()+".clone() must return a copy of "+visitor.getClass()+"; but got: "+cloned+" of "+cloned.getClass();
|
||||
}
|
||||
else {
|
||||
cloned = visitor;
|
||||
}
|
||||
clones[o++] = cloned;
|
||||
}
|
||||
}
|
||||
if (visitors.isEmpty()) {
|
||||
GeneralHighlightingPass.LOG.error("No visitors registered. list=" + highlightVisitors + "; all visitors are:" + HighlightVisitor.EP_HIGHLIGHT_VISITOR.getExtensionList(myProject));
|
||||
if (o == 0) {
|
||||
GeneralHighlightingPass.LOG.error("No visitors registered. all visitors:" + Arrays.toString(visitors));
|
||||
}
|
||||
|
||||
return visitors.toArray(new HighlightVisitor[0]);
|
||||
return ArrayUtil.realloc(clones, o, HighlightVisitor.ARRAY_FACTORY);
|
||||
}
|
||||
|
||||
void createHighlightVisitorsFor(@NotNull PsiFile psiFile, @NotNull Consumer<? super HighlightVisitor[]> consumer) {
|
||||
AtomicInteger count = myProject.getUserData(HIGHLIGHT_VISITOR_INSTANCE_COUNT);
|
||||
Project project = myPsiFile.getProject();
|
||||
AtomicInteger count = project.getUserData(HIGHLIGHT_VISITOR_INSTANCE_COUNT);
|
||||
if (count == null) {
|
||||
count = ((UserDataHolderEx)myProject).putUserDataIfAbsent(HIGHLIGHT_VISITOR_INSTANCE_COUNT, new AtomicInteger(0));
|
||||
count = ((UserDataHolderEx)project).putUserDataIfAbsent(HIGHLIGHT_VISITOR_INSTANCE_COUNT, new AtomicInteger(0));
|
||||
}
|
||||
int oldCount = count.getAndIncrement();
|
||||
HighlightVisitor[] filtered = filterVisitors(myHighlightVisitorProducer.apply(oldCount), psiFile);
|
||||
// first ever queried HighlightVisitor can be used as is, but all further HighlightVisitors queried while the previous HighlightVisitor haven't finished, should be cloned to avoid reentrancy issues
|
||||
HighlightVisitor[] filtered = myHighlightVisitorProducer.apply(oldCount != 0);
|
||||
try {
|
||||
consumer.consume(filtered);
|
||||
}
|
||||
|
||||
@@ -2646,6 +2646,7 @@ a:com.intellij.openapi.project.DumbService
|
||||
- a:isDumb():Z
|
||||
- sf:isDumb(com.intellij.openapi.project.Project):Z
|
||||
- sf:isDumbAware(java.lang.Object):Z
|
||||
- f:isUsableInCurrentContext(java.lang.Object):Z
|
||||
- f:makeDumbAware(javax.swing.JComponent,com.intellij.openapi.Disposable):V
|
||||
- a:queueTask(com.intellij.openapi.project.DumbModeTask):V
|
||||
- f:repeatUntilPassesInSmartMode(java.lang.Runnable):V
|
||||
|
||||
@@ -416,6 +416,13 @@ abstract class DumbService {
|
||||
@ApiStatus.Internal
|
||||
abstract fun unsafeRunWhenSmart(runnable: Runnable)
|
||||
|
||||
/**
|
||||
* return true if [thing] can be used in current dumb context, i.e., either the [thing] is [isDumbAware] or the current context is smart; return false otherwise
|
||||
*/
|
||||
fun isUsableInCurrentContext(thing: Any) : Boolean {
|
||||
return !isDumb || isDumbAware(thing)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
@Topic.ProjectLevel
|
||||
|
||||
@@ -49,8 +49,9 @@ final class InjectedGeneralHighlightingPass extends ProgressableTextEditorHighli
|
||||
private final ProperTextRange myPriorityRange;
|
||||
private final @NotNull EditorColorsScheme myGlobalScheme;
|
||||
private final List<HighlightInfo> myHighlights = new ArrayList<>(); // guarded by myHighlights
|
||||
private final HighlightVisitorRunner myHighlightVisitorRunner;
|
||||
private final boolean myRunAnnotators;
|
||||
private final boolean myRunVisitors;
|
||||
private final boolean myHighlightErrorElements;
|
||||
private final HighlightInfoUpdater myHighlightInfoUpdater;
|
||||
|
||||
InjectedGeneralHighlightingPass(@NotNull PsiFile psiFile,
|
||||
@@ -67,13 +68,10 @@ final class InjectedGeneralHighlightingPass extends ProgressableTextEditorHighli
|
||||
myUpdateAll = updateAll;
|
||||
myPriorityRange = priorityRange;
|
||||
myGlobalScheme = editor != null ? editor.getColorsScheme() : EditorColorsManager.getInstance().getGlobalScheme();
|
||||
myHighlightVisitorRunner = new HighlightVisitorRunner(psiFile.getProject(), myGlobalScheme);
|
||||
myRunAnnotators = runAnnotators;
|
||||
myRunVisitors = runVisitors;
|
||||
myHighlightErrorElements = highlightErrorElements;
|
||||
myHighlightInfoUpdater = highlightInfoUpdater;
|
||||
if (!runVisitors) {
|
||||
// "do not run visitors" here means "reduce the set of visitors down to DefaultHighlightVisitor", because it reports error elements
|
||||
myHighlightVisitorRunner.setHighlightVisitorProducer(__ -> List.of(new DefaultHighlightVisitor(psiFile.getProject(), highlightErrorElements, false)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,9 +195,15 @@ final class InjectedGeneralHighlightingPass extends ProgressableTextEditorHighli
|
||||
List<? extends @NotNull PsiElement> inside = dividedElements.inside();
|
||||
LongList insideRanges = dividedElements.insideRanges();
|
||||
BooleanSupplier runnable = () -> {
|
||||
myHighlightVisitorRunner.createHighlightVisitorsFor(injectedPsi, visitors -> {
|
||||
HighlightVisitorRunner highlightVisitorRunner = new HighlightVisitorRunner(injectedPsi, myGlobalScheme);
|
||||
if (!myRunVisitors) {
|
||||
// "do not run visitors" here means "reduce the set of visitors down to DefaultHighlightVisitor", because it reports error elements
|
||||
highlightVisitorRunner.setHighlightVisitorProducer(__ -> new HighlightVisitor[]{ new DefaultHighlightVisitor(myProject, myHighlightErrorElements, false) });
|
||||
}
|
||||
|
||||
highlightVisitorRunner.createHighlightVisitorsFor(injectedPsi, visitors -> {
|
||||
int chunkSize = Math.max(1, inside.size() / 100); // one percent precision is enough
|
||||
myHighlightVisitorRunner.runVisitors(injectedPsi, injectedPsi.getTextRange(), inside,
|
||||
highlightVisitorRunner.runVisitors(injectedPsi, injectedPsi.getTextRange(), inside,
|
||||
insideRanges, List.of(), LongList.of(), visitors, false, chunkSize, true,
|
||||
() -> createInfoHolder(injectedPsi), (toolId, psiElement, infos) -> {
|
||||
// convert injected infos to host
|
||||
|
||||
Reference in New Issue
Block a user