lst: do not store RangeHighlighter in tracker Blocks

* use single RangeHighlighter to render ranges in gutter
This commit is contained in:
Aleksey Pivovarov
2018-07-04 16:18:35 +03:00
parent 01ece18e55
commit 4b845d433f
9 changed files with 164 additions and 106 deletions
@@ -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,
@@ -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);
}
@@ -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<RangeHighlighter> 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<? extends Range> 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<? extends Range> 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);
}
}
}
@@ -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<R : Range> {
if (isReleased) return@Runnable
isReleased = true
updateHighlighters()
Disposer.dispose(disposable)
}
@@ -180,10 +177,6 @@ abstract class LineStatusTrackerBase<R : Range> {
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<R : Range> {
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<R : Range> {
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<Range.InnerRange> {
@@ -241,21 +227,8 @@ abstract class LineStatusTrackerBase<R : Range> {
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<R : Range> {
}
}
@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<R : Range> {
}
protected open class BlockData(internal var innerRanges: List<Range.InnerRange>? = null,
internal var rangeHighlighter: RangeHighlighter? = null)
protected open class BlockData(internal var innerRanges: List<Range.InnerRange>? = null)
open protected fun createBlockData(): BlockData = BlockData()
open protected val Block.ourData: BlockData get() = getBlockData(this)
@@ -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;
}
}
@@ -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();
}
@@ -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) {
@@ -101,14 +101,14 @@ abstract class LineStatusTracker<R : Range> 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<AnAction> {
@@ -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()
}