From 4b845d433f91efa468c1d49b42ccb9f9ad9c08e9 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Sat, 7 Apr 2018 23:27:25 +0300 Subject: [PATCH] lst: do not store RangeHighlighter in tracker Blocks * use single RangeHighlighter to render ranges in gutter --- .../com/intellij/diff/util/DiffDrawUtil.java | 4 + .../vcs/ex/LineStatusMarkerPopupRenderer.java | 2 +- .../vcs/ex/LineStatusMarkerRenderer.java | 168 ++++++++++++++---- .../openapi/vcs/ex/LineStatusTrackerBase.kt | 66 +------ .../editor/markup/ActiveGutterRenderer.java | 8 +- .../impl/EditorGutterComponentImpl.java | 2 +- .../openapi/vcs/actions/VcsPreviewPanel.java | 2 +- .../openapi/vcs/ex/LineStatusTracker.kt | 8 +- .../vcs/ex/PartialLocalLineStatusTracker.kt | 10 +- 9 files changed, 164 insertions(+), 106 deletions(-) diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffDrawUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffDrawUtil.java index 8920a3dd663c..09f12a347c43 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffDrawUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffDrawUtil.java @@ -186,6 +186,10 @@ public class DiffDrawUtil { return editor.logicalPositionToXY(editor.offsetToLogicalPosition(document.getLineStartOffset(line))).y; } + public static int yToLine(@NotNull Editor editor, int y) { + return editor.xyToLogicalPosition(new Point(0, y)).line; + } + @NotNull private static TextAttributes getTextAttributes(@NotNull final TextDiffType type, @Nullable final Editor editor, diff --git a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerPopupRenderer.java b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerPopupRenderer.java index e7684dd0932a..49f1e7435006 100644 --- a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerPopupRenderer.java +++ b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerPopupRenderer.java @@ -70,7 +70,7 @@ public abstract class LineStatusMarkerPopupRenderer extends LineStatusMarkerRend } @Override - protected boolean canDoAction(@NotNull Range range, MouseEvent e) { + protected boolean canDoAction(@NotNull Editor editor, @NotNull Range range, MouseEvent e) { return isInsideMarkerArea(e); } diff --git a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerRenderer.java b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerRenderer.java index 319531daf98e..4786cdaa6988 100644 --- a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerRenderer.java +++ b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerRenderer.java @@ -17,7 +17,10 @@ package com.intellij.openapi.vcs.ex; import com.intellij.diff.util.DiffDrawUtil; import com.intellij.diff.util.DiffUtil; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.DiffColors; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsManager; @@ -26,27 +29,139 @@ import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.EditorGutterComponentEx; import com.intellij.openapi.editor.impl.DocumentMarkupModel; import com.intellij.openapi.editor.markup.*; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.TextRange; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.update.MergingUpdateQueue; +import com.intellij.util.ui.update.Update; +import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.List; import static com.intellij.diff.util.DiffDrawUtil.lineToY; +import static com.intellij.diff.util.DiffDrawUtil.yToLine; +import static com.intellij.openapi.diagnostic.Logger.getInstance; +import static com.intellij.util.ui.update.MergingUpdateQueue.ANY_COMPONENT; public abstract class LineStatusMarkerRenderer { + private static final Logger LOG = getInstance(LineStatusMarkerRenderer.class); + @NotNull protected final LineStatusTrackerBase myTracker; + private final MarkupEditorFilter myEditorFilter; + + @NotNull private final MergingUpdateQueue myUpdateQueue; + private boolean myDisposed = false; + @NotNull private final RangeHighlighter myHighlighter; + @NotNull private final List myTooltipHighlighters = new ArrayList<>(); public LineStatusMarkerRenderer(@NotNull LineStatusTrackerBase tracker) { myTracker = tracker; + myEditorFilter = getEditorFilter(); + + Document document = myTracker.getDocument(); + MarkupModel markupModel = DocumentMarkupModel.forDocument(document, myTracker.getProject(), true); + myHighlighter = markupModel.addRangeHighlighter(0, document.getTextLength(), DiffDrawUtil.LST_LINE_MARKER_LAYER, null, + HighlighterTargetArea.LINES_IN_RANGE); + myHighlighter.setGreedyToLeft(true); + myHighlighter.setGreedyToRight(true); + + myHighlighter.setLineMarkerRenderer(new MyActiveGutterRenderer()); + + if (myEditorFilter != null) myHighlighter.setEditorFilter(myEditorFilter); + + myUpdateQueue = new MergingUpdateQueue("LineStatusMarkerRenderer", 100, true, ANY_COMPONENT, myTracker.getDisposable()); + + Disposer.register(myTracker.getDisposable(), new Disposable() { + @Override + public void dispose() { + myDisposed = true; + destroyHighlighters(); + } + }); + + scheduleUpdate(); } + public void scheduleUpdate() { + myUpdateQueue.queue(new Update("update") { + @Override + public void run() { + updateHighlighters(); + } + }); + } - protected boolean canDoAction(@NotNull Range range, MouseEvent e) { + @CalledInAwt + private void updateHighlighters() { + if (myDisposed) return; + + for (RangeHighlighter highlighter: myTooltipHighlighters) { + disposeHighlighter(highlighter); + } + myTooltipHighlighters.clear(); + + List ranges = myTracker.getRanges(); + if (ranges != null) { + MarkupModel markupModel = DocumentMarkupModel.forDocument(myTracker.getDocument(), myTracker.getProject(), true); + for (Range range: ranges) { + RangeHighlighter highlighter = createTooltipRangeHighlighter(range, markupModel); + if (myEditorFilter != null) highlighter.setEditorFilter(myEditorFilter); + myTooltipHighlighters.add(highlighter); + } + } + } + + private void destroyHighlighters() { + disposeHighlighter(myHighlighter); + + for (RangeHighlighter highlighter: myTooltipHighlighters) { + disposeHighlighter(highlighter); + } + myTooltipHighlighters.clear(); + } + + private static void disposeHighlighter(@NotNull RangeHighlighter highlighter) { + try { + highlighter.dispose(); + } + catch (Exception e) { + LOG.error(e); + } + } + + private boolean canDoAction(@NotNull Editor editor, MouseEvent e) { + Range range = getSelectedRange(editor, e); + return range != null && canDoAction(editor, range, e); + } + + private void doAction(@NotNull Editor editor, MouseEvent e) { + Range range = getSelectedRange(editor, e); + if (range != null) { + doAction(editor, range, e); + } + } + + @Nullable + private Range getSelectedRange(@NotNull Editor editor, MouseEvent e) { + int line = yToLine(editor, e.getY()); + Range range = myTracker.getRangeForLine(line); + if (range != null) return range; + + // special handling for deletion at the end of file + int lineCount = DiffUtil.getLineCount(editor.getDocument()); + if (line == lineCount - 1) { + return myTracker.getRangeForLine(lineCount); + } + return null; + } + + protected boolean canDoAction(@NotNull Editor editor, @NotNull Range range, MouseEvent e) { return false; } @@ -62,24 +177,9 @@ public abstract class LineStatusMarkerRenderer { return 0; } - @NotNull - RangeHighlighter createHighlighter(@NotNull Range range) { - MarkupModel markupModel = DocumentMarkupModel.forDocument(myTracker.getDocument(), myTracker.getProject(), true); - RangeHighlighter highlighter = createRangeHighlighter(range, markupModel); - - MarkupEditorFilter editorFilter = getEditorFilter(); - if (editorFilter != null) highlighter.setEditorFilter(editorFilter); - - highlighter.setLineMarkerRenderer(new MyActiveGutterRenderer(range)); - - return highlighter; - } - - - @NotNull - public static RangeHighlighter createRangeHighlighter(@NotNull Range range, - @NotNull MarkupModel markupModel) { + public static RangeHighlighter createTooltipRangeHighlighter(@NotNull Range range, + @NotNull MarkupModel markupModel) { TextRange textRange = DiffUtil.getLinesRange(markupModel.getDocument(), range.getLine1(), range.getLine2(), false); TextAttributes attributes = getTextAttributes(range); @@ -94,9 +194,8 @@ public abstract class LineStatusMarkerRenderer { return highlighter; } - @NotNull - private static TextAttributes getTextAttributes(@NotNull final Range range) { + private static TextAttributes getTextAttributes(@NotNull Range range) { return new TextAttributes() { @Override public Color getErrorStripeColor() { @@ -109,6 +208,21 @@ public abstract class LineStatusMarkerRenderer { // Gutter painting // + protected void paint(@NotNull Editor editor, @NotNull Graphics g) { + List ranges = myTracker.getRanges(); + if (ranges == null) return; + + Rectangle area = editor.getScrollingModel().getVisibleArea(); + int line1 = yToLine(editor, area.y); + int line2 = yToLine(editor, area.y + area.height); + + for (Range range : ranges) { + if (range.getLine2() < line1) continue; + if (range.getLine1() > line2) return; + paint(editor, range, g); + } + } + protected void paint(@NotNull Editor editor, @NotNull Range range, @NotNull Graphics g) { paintRange(g, editor, range, getFramingBorderSize()); } @@ -322,25 +436,19 @@ public abstract class LineStatusMarkerRenderer { private class MyActiveGutterRenderer implements ActiveGutterRenderer { - @NotNull private final Range myRange; - - public MyActiveGutterRenderer(@NotNull Range range) { - myRange = range; - } - @Override public void paint(Editor editor, Graphics g, Rectangle r) { - LineStatusMarkerRenderer.this.paint(editor, myRange, g); + LineStatusMarkerRenderer.this.paint(editor, g); } @Override - public boolean canDoAction(MouseEvent e) { - return LineStatusMarkerRenderer.this.canDoAction(myRange, e); + public boolean canDoAction(Editor editor, MouseEvent e) { + return LineStatusMarkerRenderer.this.canDoAction(editor, e); } @Override public void doAction(Editor editor, MouseEvent e) { - LineStatusMarkerRenderer.this.doAction(editor, myRange, e); + LineStatusMarkerRenderer.this.doAction(editor, e); } } } diff --git a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerBase.kt b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerBase.kt index 8bac924c5aec..3e5ad1411a08 100644 --- a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerBase.kt +++ b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerBase.kt @@ -26,9 +26,7 @@ import com.intellij.openapi.command.undo.UndoConstants import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.impl.DocumentImpl -import com.intellij.openapi.editor.markup.RangeHighlighter import com.intellij.openapi.localVcs.UpToDateLineNumberProvider.ABSENT_LINE_NUMBER -import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.vcs.VcsBundle @@ -166,7 +164,6 @@ abstract class LineStatusTrackerBase { if (isReleased) return@Runnable isReleased = true - updateHighlighters() Disposer.dispose(disposable) } @@ -180,10 +177,6 @@ abstract class LineStatusTrackerBase { protected open inner class MyDocumentTrackerHandler : DocumentTracker.Handler { - override fun onRangeRemoved(block: Block) { - destroyHighlighter(block) - } - override fun onRangeShifted(before: Block, after: Block) { after.ourData.innerRanges = before.ourData.innerRanges } @@ -191,22 +184,22 @@ abstract class LineStatusTrackerBase { override fun afterRefresh() { checkIfFileUnchanged() calcInnerRanges() - installMissingHighlighters() + updateHighlighters() } override fun afterRangeChange() { - installMissingHighlighters() + updateHighlighters() } override fun afterExplicitChange() { checkIfFileUnchanged() calcInnerRanges() - installMissingHighlighters() + updateHighlighters() } override fun onUnfreeze(side: Side) { calcInnerRanges() - installMissingHighlighters() + updateHighlighters() } private fun checkIfFileUnchanged() { @@ -221,17 +214,10 @@ abstract class LineStatusTrackerBase { for (block in blocks) { if (block.ourData.innerRanges == null) { block.ourData.innerRanges = calcInnerRanges(block) - destroyHighlighter(block) } } } } - - private fun installMissingHighlighters() { - for (block in blocks) { - installHighlighter(block) - } - } } private fun calcInnerRanges(block: Block): List { @@ -241,21 +227,8 @@ abstract class LineStatusTrackerBase { vcsDocument.lineOffsets, document.lineOffsets) } - @CalledInAwt protected fun updateHighlighters() { - LOCK.write { - for (block in blocks) { - updateHighlighter(block) - } - } - } - - @CalledInAwt - protected fun updateHighlighter(block: Block) { - LOCK.write { - destroyHighlighter(block) - installHighlighter(block) - } + renderer.scheduleUpdate() } @CalledInAwt @@ -276,32 +249,6 @@ abstract class LineStatusTrackerBase { } } - @CalledInAwt - private fun installHighlighter(block: Block) { - if (block.ourData.rangeHighlighter != null) return - if (!isValid() || block.range.isEmpty) return - try { - block.ourData.rangeHighlighter = renderer.createHighlighter(block.toRange()) - } - catch (ignore: ProcessCanceledException) { - } - catch (e: Exception) { - LOG.error(e) - } - } - - @CalledInAwt - private fun destroyHighlighter(block: Block) { - val highlighter = block.ourData.rangeHighlighter ?: return - try { - block.ourData.rangeHighlighter = null - highlighter.dispose() - } - catch (e: Exception) { - LOG.error(e) - } - } - fun isOperational(): Boolean = LOCK.read { return isInitialized && !isReleased } @@ -456,8 +403,7 @@ abstract class LineStatusTrackerBase { } - protected open class BlockData(internal var innerRanges: List? = null, - internal var rangeHighlighter: RangeHighlighter? = null) + protected open class BlockData(internal var innerRanges: List? = null) open protected fun createBlockData(): BlockData = BlockData() open protected val Block.ourData: BlockData get() = getBlockData(this) diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/markup/ActiveGutterRenderer.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/markup/ActiveGutterRenderer.java index 2f7bc7ffb45e..d49464218487 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/markup/ActiveGutterRenderer.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/markup/ActiveGutterRenderer.java @@ -45,5 +45,11 @@ public interface ActiveGutterRenderer extends LineMarkerRenderer { */ void doAction(Editor editor, MouseEvent e); - boolean canDoAction(final MouseEvent e); + default boolean canDoAction(Editor editor, MouseEvent e) { + return canDoAction(e); + } + + default boolean canDoAction(final MouseEvent e) { + return false; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index 928152fdb5cc..c8f21e5f391f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -1740,7 +1740,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse if (startY < e.getY() && e.getY() <= endY && renderer instanceof ActiveGutterRenderer && - ((ActiveGutterRenderer)renderer).canDoAction(e)) { + ((ActiveGutterRenderer)renderer).canDoAction(myEditor, e)) { gutterRenderer[0] = (ActiveGutterRenderer)renderer; layer[0] = highlighter.getLayer(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/VcsPreviewPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/VcsPreviewPanel.java index 2f1f6ce5db45..15c4bbcc1ae1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/VcsPreviewPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/VcsPreviewPanel.java @@ -123,7 +123,7 @@ class VcsPreviewPanel implements PreviewPanel { } private void addHighlighter(@NotNull Range range, @NotNull ColorKey colorKey) { - RangeHighlighter highlighter = LineStatusMarkerRenderer.createRangeHighlighter(range, myEditor.getMarkupModel()); + RangeHighlighter highlighter = LineStatusMarkerRenderer.createTooltipRangeHighlighter(range, myEditor.getMarkupModel()); highlighter.setLineMarkerRenderer(new ActiveGutterRenderer() { @Override public void paint(Editor editor, Graphics g, Rectangle r) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.kt index e47d6ebd8735..80308ecb5930 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.kt @@ -101,14 +101,14 @@ abstract class LineStatusTracker constructor(override val project: Pr : LineStatusMarkerPopupRenderer(tracker) { override fun getEditorFilter(): MarkupEditorFilter? = MarkupEditorFilterFactory.createIsNotDiffFilter() - override fun canDoAction(range: Range, e: MouseEvent?): Boolean { + override fun canDoAction(editor: Editor, range: Range, e: MouseEvent?): Boolean { if (tracker.mode == Mode.SILENT) return false - return super.canDoAction(range, e) + return super.canDoAction(editor, range, e) } - override fun paint(editor: Editor, range: Range, g: Graphics) { + override fun paint(editor: Editor, g: Graphics) { if (tracker.mode == Mode.SILENT) return - super.paint(editor, range, g) + super.paint(editor, g) } override fun createToolbarActions(editor: Editor, range: Range, mousePosition: Point?): List { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt index 9cd57c15be37..50044c566499 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt @@ -21,8 +21,6 @@ import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.actionSystem.Separator -import com.intellij.openapi.application.ModalityState -import com.intellij.openapi.application.runInEdt import com.intellij.openapi.application.runReadAction import com.intellij.openapi.command.CommandEvent import com.intellij.openapi.command.CommandListener @@ -265,11 +263,7 @@ class PartialLocalLineStatusTracker(project: Project, if (!affectedBlocks.isEmpty()) { dropExistingUndoActions() - runInEdt(ModalityState.any()) { - for (block in affectedBlocks) { - updateHighlighter(block) - } - } + updateHighlighters() } } @@ -762,10 +756,10 @@ class PartialLocalLineStatusTracker(project: Project, assert(blocks.size == states.size) blocks.forEachIndexed { i, block -> block.marker = idToMarker[states[i].changelistId] ?: defaultMarker - updateHighlighter(block) } updateAffectedChangeLists() + updateHighlighters() }