mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
optimization: use a primitive-aware map to avoid frequent TextRange allocations
GitOrigin-RevId: ac3378ca9d04b265b1a3ec4aa2fd710615e0219a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9b05a93fbc
commit
8d960a4c66
+32
-17
@@ -2,28 +2,36 @@
|
||||
package com.intellij.codeInsight.daemon.impl;
|
||||
|
||||
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
|
||||
import com.intellij.openapi.editor.impl.RangeMarkerImpl;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.ProperTextRange;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.TextRangeScalarUtil;
|
||||
import com.intellij.openapi.util.UserDataHolderEx;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
// cache for highlighters not needed anymore.
|
||||
// You call recycleHighlighter() to put unused highlighter into the cache
|
||||
// and then call pickupHighlighterFromGarbageBin() (if there is a sudden need for fresh highlighter with specified offsets) to remove it from the cache to re-initialize and use.
|
||||
/**
|
||||
* Cache for highlighters scheduled for removal.
|
||||
* You call {@link #recycleHighlighter} to put unused highlighter into the cache
|
||||
* and then call {@link #pickupHighlighterFromGarbageBin} (if there is a sudden need for fresh highlighter with specified offsets) to remove it from the cache to re-initialize and re-use.
|
||||
* In the end, call {@link UpdateHighlightersUtil#incinerateObsoleteHighlighters} to finally remove highlighters left in the cache that nobody picked up and reused.
|
||||
*/
|
||||
final class HighlightersRecycler {
|
||||
private final MultiMap<TextRange, RangeHighlighterEx> incinerator = new MultiMap<>();
|
||||
private final Long2ObjectMap<List<RangeHighlighterEx>> incinerator = new Long2ObjectOpenHashMap<>();
|
||||
private static final Key<Boolean> BEING_RECYCLED_KEY = Key.create("RECYCLED_KEY"); // set when the highlighter is just recycled, but not yet transferred to EDT to change its attributes. used to prevent double recycling the same RH
|
||||
|
||||
// return true if RH is successfully recycled, false if race condition intervened
|
||||
boolean recycleHighlighter(@NotNull RangeHighlighterEx highlighter) {
|
||||
if (highlighter.isValid() && ((UserDataHolderEx)highlighter).replace(BEING_RECYCLED_KEY, null, Boolean.TRUE)) {
|
||||
incinerator.putValue(ProperTextRange.create(highlighter), highlighter);
|
||||
long range = ((RangeMarkerImpl)highlighter).getScalarRange();
|
||||
incinerator.computeIfAbsent(range, __ -> new ArrayList<>()).add(highlighter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -31,13 +39,19 @@ final class HighlightersRecycler {
|
||||
|
||||
@Nullable // null means no highlighter found in the cache
|
||||
RangeHighlighterEx pickupHighlighterFromGarbageBin(int startOffset, int endOffset, int layer) {
|
||||
TextRange range = new TextRange(startOffset, endOffset);
|
||||
Collection<RangeHighlighterEx> collection = incinerator.get(range);
|
||||
for (RangeHighlighterEx highlighter : collection) {
|
||||
if (highlighter.isValid() && highlighter.getLayer() == layer) {
|
||||
incinerator.remove(range, highlighter);
|
||||
highlighter.putUserData(BEING_RECYCLED_KEY, null);
|
||||
return highlighter;
|
||||
long range = TextRangeScalarUtil.toScalarRange(startOffset, endOffset);
|
||||
List<RangeHighlighterEx> collection = incinerator.get(range);
|
||||
if (collection != null) {
|
||||
for (int i = 0; i < collection.size(); i++) {
|
||||
RangeHighlighterEx highlighter = collection.get(i);
|
||||
if (highlighter.isValid() && highlighter.getLayer() == layer) {
|
||||
collection.remove(i);
|
||||
if (collection.isEmpty()) {
|
||||
incinerator.remove(range);
|
||||
}
|
||||
highlighter.putUserData(BEING_RECYCLED_KEY, null);
|
||||
return highlighter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -45,7 +59,7 @@ final class HighlightersRecycler {
|
||||
|
||||
@NotNull
|
||||
Collection<? extends RangeHighlighter> forAllInGarbageBin() {
|
||||
return incinerator.values();
|
||||
return ContainerUtil.flatten(incinerator.values());
|
||||
}
|
||||
|
||||
// mark all remaining highlighters as not "recycled", to avoid double creation
|
||||
@@ -54,7 +68,8 @@ final class HighlightersRecycler {
|
||||
((UserDataHolderEx)highlighter).replace(BEING_RECYCLED_KEY, Boolean.TRUE, null);
|
||||
}
|
||||
}
|
||||
static boolean isBeingRecycled(RangeHighlighter highlighter) {
|
||||
|
||||
static boolean isBeingRecycled(@NotNull RangeHighlighter highlighter) {
|
||||
return highlighter.getUserData(BEING_RECYCLED_KEY) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.DocumentUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.diff.FilesTooBigForDiffException;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -420,6 +421,21 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
|
||||
return node.intervalEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this marker text range in the scalar form
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public long getScalarRange() {
|
||||
RangeMarkerTree.RMNode<?> node = myNode;
|
||||
if (node == null) {
|
||||
return -1;
|
||||
}
|
||||
long range = node.toScalarRange();
|
||||
int delta = node.computeDeltaUpToRoot();
|
||||
return TextRangeScalarUtil.shift(range, delta, delta);
|
||||
}
|
||||
|
||||
// return intrinsic range belonging to that node (without delta-up-to-the-root correction)
|
||||
long toScalarRange() {
|
||||
RangeMarkerTree.RMNode<?> node = myNode;
|
||||
if (node == null) {
|
||||
|
||||
Reference in New Issue
Block a user