diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextLayoutCache.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextLayoutCache.java index ad81fc34f76c..fe366b98af99 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextLayoutCache.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextLayoutCache.java @@ -9,14 +9,15 @@ import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; import com.intellij.openapi.editor.impl.EditorDocumentPriorities; import com.intellij.openapi.util.Disposer; -import com.intellij.util.containers.hash.LinkedHashMap; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import org.jetbrains.annotations.NotNull; import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.*; /** * Editor text layout storage. Layout is stored on a per-logical-line basis, @@ -37,19 +38,7 @@ final class TextLayoutCache implements PrioritizedDocumentListener, Disposable { private int myDocumentChangeOldEndLine; @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - private Map myLaidOutChunks = - // using our own LinkedHashMap implementation to avoid IDEA-205735 - new LinkedHashMap<>(MAX_CHUNKS_IN_ACTIVE_EDITOR, true) { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - if (size() > getChunkCacheSizeLimit()) { - if (LOG.isDebugEnabled()) LOG.debug("Clearing chunk for " + myView.getEditor().getVirtualFile()); - eldest.getKey().clearCache(); - return true; - } - return false; - } - }; + private final ObjectLinkedOpenHashSet laidOutChunks = new ObjectLinkedOpenHashSet<>(MAX_CHUNKS_IN_ACTIVE_EDITOR); TextLayoutCache(EditorView view) { myView = view; @@ -91,7 +80,7 @@ final class TextLayoutCache implements PrioritizedDocumentListener, Disposable { @Override public void dispose() { myLines = null; - myLaidOutChunks = null; + laidOutChunks.clear(); } private int getAdjustedLineNumber(int offset) { @@ -130,7 +119,8 @@ final class TextLayoutCache implements PrioritizedDocumentListener, Disposable { } if (oldEndLine < newEndLine) { myLines.addAll(oldEndLine + 1, Collections.nCopies(newEndLine - oldEndLine, null)); - } else if (oldEndLine > newEndLine) { + } + else if (oldEndLine > newEndLine) { List layouts = myLines.subList(newEndLine + 1, oldEndLine + 1); for (LineLayout layout : layouts) { if (layout != null) { @@ -162,28 +152,33 @@ final class TextLayoutCache implements PrioritizedDocumentListener, Disposable { return myView.getEditor().getContentComponent().isShowing() ? MAX_CHUNKS_IN_ACTIVE_EDITOR : MAX_CHUNKS_IN_INACTIVE_EDITOR; } - void onChunkAccess(LineLayout.Chunk chunk) { - myLaidOutChunks.put(chunk, this); // value doesn't matter, null is not supported by our map implementation + void onChunkAccess(@NotNull LineLayout.Chunk chunk) { + if (laidOutChunks.addAndMoveToFirst(chunk) && laidOutChunks.size() > getChunkCacheSizeLimit()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Clearing chunk for " + myView.getEditor().getVirtualFile()); + } + laidOutChunks.removeLast().clearCache(); + } } private void removeChunksFromCache(LineLayout layout) { - layout.getChunksInLogicalOrder().forEach(myLaidOutChunks::remove); + layout.getChunksInLogicalOrder().forEach(laidOutChunks::remove); } private void trimChunkCache() { int limit = getChunkCacheSizeLimit(); - if (myLaidOutChunks.size() > limit) { - Iterator it = myLaidOutChunks.keySet().iterator(); - while (myLaidOutChunks.size() > limit) { - LineLayout.Chunk chunk = it.next(); - if (LOG.isDebugEnabled()) LOG.debug("Clearing chunk for " + myView.getEditor().getVirtualFile()); - chunk.clearCache(); - it.remove(); + while (laidOutChunks.size() > limit) { + LineLayout.Chunk chunk = laidOutChunks.removeLast(); + if (LOG.isDebugEnabled()) { + LOG.debug("Clearing chunk for " + myView.getEditor().getVirtualFile()); } + chunk.clearCache(); } } private void checkDisposed() { - if (myLines == null) myView.getEditor().throwDisposalError("Editor is already disposed"); + if (myLines == null) { + myView.getEditor().throwDisposalError("Editor is already disposed"); + } } }