diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/Inlay.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/Inlay.java index 576e345aa039..619b006a0b0a 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/Inlay.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/Inlay.java @@ -32,7 +32,7 @@ public interface Inlay extends Disposable * Defines relative position of inlay element with respect to the containing text. */ @NotNull - Placement getPlacement(); + VerticalAlignment getVerticalAlignment(); /** * Tells whether this element is valid. Inlay becomes invalid on explicit disposal, @@ -103,7 +103,7 @@ public interface Inlay extends Disposable void repaint(); /** - * @see #getPlacement() + * @see #getVerticalAlignment() */ - enum Placement { INLINE, ABOVE_LINE, BELOW_LINE, AFTER_LINE_END } + enum VerticalAlignment { INLINE, ABOVE_LINE, BELOW_LINE } } diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/InlayModel.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/InlayModel.java index df549149217f..be257f79569c 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/InlayModel.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/InlayModel.java @@ -13,8 +13,7 @@ import java.util.List; /** * Provides an ability to introduce custom visual elements into editor's representation. - * Such elements are not reflected in document contents. Elements are 'anchored' to a certain document offset at creation, - * this offset behaves similar to a zero-range {@link RangeMarker} with respect to document changes. + * Such elements are not reflected in document contents. *

* WARNING! This is an experimental API, it can change at any time. * @see Editor#getInlayModel() @@ -30,24 +29,21 @@ public interface InlayModel { } /** - * Introduces an inline visual element at a given offset, its width and appearance is defined by the provided renderer. - * + * Introduces an inline visual element at a given offset, its width and appearance is defined by the provided renderer. With respect to + * document changes, created element behaves in a similar way to a zero-range {@link RangeMarker}. This method returns {@code null} + * if requested element cannot be created, e.g. if corresponding functionality is not supported by current editor instance. + * * @param relatesToPrecedingText whether element is associated with preceding or following text * (see {@link Inlay#isRelatedToPrecedingText()}) - * @return {@code null} if requested element cannot be created, e.g. if corresponding functionality - * is not supported by current editor instance. */ @Nullable Inlay addInlineElement(int offset, boolean relatesToPrecedingText, @NotNull T renderer); /** * Introduces a 'block' visual element at a given offset, its size and appearance is defined by the provided renderer. This element - * will be displayed between lines of text. - * - * @param relatesToPrecedingText whether element is associated with preceding or following text - * (see {@link Inlay#isRelatedToPrecedingText()}) - * @return {@code null} if requested element cannot be created, e.g. if corresponding functionality - * is not supported by current editor instance. + * will be displayed between lines of text. With respect to document changes, created element behaves in a similar way to a zero-range + * {@link RangeMarker}. This method returns {@code null} if requested element cannot be created, e.g. if corresponding functionality + * is not supported by current editor instance. */ @Nullable Inlay addBlockElement(int offset, @@ -56,18 +52,6 @@ public interface InlayModel { int priority, @NotNull T renderer); - - /** - * Introduces a visual element, which will be displayed after the end of corresponding logical line. - * - * @param relatesToPrecedingText whether element is associated with preceding or following text - * (see {@link Inlay#isRelatedToPrecedingText()}) - * @return {@code null} if requested element cannot be created, e.g. if corresponding functionality - * is not supported by current editor instance. - */ - @Nullable - Inlay addAfterLineEndElement(int offset, boolean relatesToPrecedingText, @NotNull T renderer); - /** * Returns a list of inline elements for a given offset range (both limits are inclusive). Returned list is sorted by offset. * Both visible and invisible (due to folding) elements are returned. @@ -152,34 +136,6 @@ public interface InlayModel { @Nullable Inlay getElementAt(@NotNull Point point); - /** - * Returns a list of after-line-end elements for a given offset range (both limits are inclusive). - * Returned list is sorted by offset. Both visible and invisible (due to folding) elements are returned. - * - * @see #addAfterLineEndElement(int, boolean, EditorCustomElementRenderer) - */ - @NotNull - List getAfterLineEndElementsInRange(int startOffset, int endOffset); - - /** - * Same as {@link #getAfterLineEndElementsInRange(int, int)}, but returned list contains only inlays with renderer of given type. - */ - @NotNull - default List> getAfterLineEndElementsInRange(int startOffset, int endOffset, Class type) { - //noinspection unchecked - return (List)ContainerUtil.filter(getAfterLineEndElementsInRange(startOffset, endOffset), - inlay -> type.isInstance(inlay.getRenderer())); - } - - /** - * Returns after-line-end elements for a given logical line, in creation order (this is the order they are displayed in). - * Elements are returned regardless of whether they are currently visible. - * - * @see #addAfterLineEndElement(int, boolean, EditorCustomElementRenderer) - */ - @NotNull - List getAfterLineEndElementsForLogicalLine(int logicalLine); - /** * When text is inserted at inline element's offset, resulting element's position is determined by its * {@link Inlay#isRelatedToPrecedingText()} property. But to enable natural editing experience around inline elements (so that typed text diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InlayModelWindow.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InlayModelWindow.java index ebb894121414..ab9d9a7fea4a 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InlayModelWindow.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InlayModelWindow.java @@ -37,15 +37,6 @@ class InlayModelWindow implements InlayModel { return null; } - @Nullable - @Override - public Inlay addAfterLineEndElement(int offset, - boolean relatesToPrecedingText, - @NotNull T renderer) { - logUnsupported(); - return null; - } - @NotNull @Override public List getInlineElementsInRange(int startOffset, int endOffset) { @@ -87,20 +78,6 @@ class InlayModelWindow implements InlayModel { return null; } - @NotNull - @Override - public List getAfterLineEndElementsInRange(int startOffset, int endOffset) { - logUnsupported(); - return Collections.emptyList(); - } - - @NotNull - @Override - public List getAfterLineEndElementsForLogicalLine(int logicalLine) { - logUnsupported(); - return Collections.emptyList(); - } - @Override public void setConsiderCaretPositionOnDocumentUpdates(boolean enabled) { logUnsupported(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/AfterLineEndInlayImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/AfterLineEndInlayImpl.java deleted file mode 100644 index 5f3733f72260..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/AfterLineEndInlayImpl.java +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.openapi.editor.impl; - -import com.intellij.openapi.editor.EditorCustomElementRenderer; -import com.intellij.openapi.editor.Inlay; -import com.intellij.openapi.editor.VisualPosition; -import com.intellij.util.DocumentUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.awt.*; -import java.util.List; - -class AfterLineEndInlayImpl extends InlayImpl { - private static int ourGlobalCounter = 0; - final int myOrder; - - AfterLineEndInlayImpl(@NotNull EditorImpl editor, int offset, boolean relatesToPrecedingText, @NotNull R renderer) { - super(editor, offset, relatesToPrecedingText, renderer); - //noinspection AssignmentToStaticFieldFromInstanceMethod - myOrder = ourGlobalCounter++; - } - - @Override - RangeMarkerTree getTree() { - return myEditor.getInlayModel().myAfterLineEndElementsTree; - } - - @Override - void doUpdateSize() { - myWidthInPixels = myRenderer.calcWidthInPixels(this); - if (myWidthInPixels <= 0) { - throw new IllegalArgumentException("Positive width should be defined for an after-line-end element"); - } - } - - @Override - Point getPosition() { - VisualPosition pos = getVisualPosition(); - return myEditor.visualPositionToXY(pos); - } - - @Nullable - @Override - public Rectangle getBounds() { - int targetOffset = DocumentUtil.getLineEndOffset(getOffset(), myEditor.getDocument()); - if (myEditor.getFoldingModel().isOffsetCollapsed(targetOffset)) return null; - Point pos = getPosition(); - return new Rectangle(pos.x, pos.y, getWidthInPixels(), getHeightInPixels()); - } - - @NotNull - @Override - public Placement getPlacement() { - return Placement.AFTER_LINE_END; - } - - @NotNull - @Override - public VisualPosition getVisualPosition() { - int offset = getOffset(); - int logicalLine = myEditor.getDocument().getLineNumber(offset); - int lineEndOffset = myEditor.getDocument().getLineEndOffset(logicalLine); - VisualPosition position = myEditor.offsetToVisualPosition(lineEndOffset, true, true); - if (myEditor.getFoldingModel().isOffsetCollapsed(lineEndOffset)) return position; - List inlays = myEditor.getInlayModel().getAfterLineEndElementsForLogicalLine(logicalLine); - int order = inlays.indexOf(this); - return new VisualPosition(position.line, position.column + 1 + order); - } - - @Override - public int getHeightInPixels() { - return myEditor.getLineHeight(); - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/BlockInlayImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/BlockInlayImpl.java index ccbb45cf38fb..feb3baec690c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/BlockInlayImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/BlockInlayImpl.java @@ -75,8 +75,8 @@ class BlockInlayImpl extends InlayImpl {}); // do caret merging if it's not scheduled for later - if (myVisualPositionUpdateScheduled) updateVisualPosition(); } } @@ -92,7 +90,6 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener, } } myIsInUpdate = true; - myVisualPositionUpdateScheduled = false; } @Override @@ -594,34 +591,28 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener, @Override public void onAdded(@NotNull Inlay inlay) { if (myEditor.getDocument().isInBulkUpdate()) return; - Inlay.Placement placement = inlay.getPlacement(); - if (placement == Inlay.Placement.INLINE) { + if (inlay.getVerticalAlignment() == Inlay.VerticalAlignment.INLINE) { int offset = inlay.getOffset(); for (CaretImpl caret : myCarets) { caret.onInlayAdded(offset); } } - else if (placement != Inlay.Placement.AFTER_LINE_END || hasCaretInVirtualSpace()) { + else { updateVisualPosition(); } } @Override public void onRemoved(@NotNull Inlay inlay) { - if (myEditor.getDocument().isInBulkUpdate()) return; - Inlay.Placement placement = inlay.getPlacement(); - if (myEditor.getDocument().isInEventsHandling()) { - if (placement == Inlay.Placement.AFTER_LINE_END) myVisualPositionUpdateScheduled = true; - return; - } - if (placement == Inlay.Placement.INLINE) { + if (myEditor.getDocument().isInEventsHandling() || myEditor.getDocument().isInBulkUpdate()) return; + if (inlay.getVerticalAlignment() == Inlay.VerticalAlignment.INLINE) { doWithCaretMerging(() -> { for (CaretImpl caret : myCarets) { - caret.onInlayRemoved(inlay.getOffset(), ((InlineInlayImpl)inlay).getOrder()); + caret.onInlayRemoved(inlay.getOffset(), ((InlayImpl)inlay).getOrder()); } }); } - else if (placement != Inlay.Placement.AFTER_LINE_END || hasCaretInVirtualSpace()) { + else { updateVisualPosition(); } } @@ -629,13 +620,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener, @Override public void onUpdated(@NotNull Inlay inlay) { if (myEditor.getDocument().isInBulkUpdate()) return; - if (inlay.getPlacement() != Inlay.Placement.AFTER_LINE_END || hasCaretInVirtualSpace()) { - updateVisualPosition(); - } - } - - private boolean hasCaretInVirtualSpace() { - return myEditor.getSettings().isVirtualSpace() && ContainerUtil.exists(myCarets, CaretImpl::isInVirtualSpace); + updateVisualPosition(); } @TestOnly diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index b7e5fb51eabf..c6371d451fdb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -8,6 +8,7 @@ import com.intellij.diagnostic.Dumpable; import com.intellij.ide.*; import com.intellij.ide.dnd.DnDManager; import com.intellij.ide.ui.UISettings; +import com.intellij.internal.performance.LatenciometerKt; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; @@ -627,14 +628,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (myDocument.isInEventsHandling() || myDocument.isInBulkUpdate()) return; validateSize(); int offset = inlay.getOffset(); - Inlay.Placement placement = inlay.getPlacement(); - if (placement == Inlay.Placement.INLINE) { + if (inlay.getVerticalAlignment() == Inlay.VerticalAlignment.INLINE) { repaint(offset, offset, false); } - else if (placement == Inlay.Placement.AFTER_LINE_END) { - int lineEndOffset = DocumentUtil.getLineEndOffset(offset, myDocument); - repaint(lineEndOffset, lineEndOffset, false); - } else { int visualLine = offsetToVisualLine(offset); int y = visualLineToY(visualLine) - EditorUtil.getTotalInlaysHeight(myInlayModel.getBlockElementsForVisualLine(visualLine, true)); @@ -2073,7 +2069,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi + ", caret model: " + getCaretModel().dumpState() + ", soft wraps data: " + getSoftWrapModel().dumpState() + "\n\nfolding data: " + getFoldingModel().dumpState() - + "\ninlay model: " + getInlayModel().dumpState() + (myDocument instanceof DocumentImpl ? "\n\ndocument info: " + ((DocumentImpl)myDocument).dumpState() : "") + "\nfont preferences: " + myScheme.getFontPreferences() + "\npure painting mode: " + myPurePaintingMode diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java index 5dc22888ccd2..4015aa44bb08 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java @@ -101,6 +101,24 @@ public class FoldingModelImpl extends InlayModel.SimpleAdapter return region instanceof FoldRegionImpl && ((FoldRegionImpl)region).hasDocumentRegionChanged(); } + @NotNull + FoldRegion getFirstRegion(@NotNull FoldingGroup group, @NotNull FoldRegion child) { + final List regions = getGroupedRegions(group); + if (regions.isEmpty()) { + final boolean inAll = Arrays.asList(getAllFoldRegions()).contains(child); + throw new AssertionError("Folding group without children; the known child is in all: " + inAll); + } + + FoldRegion main = regions.get(0); + for (int i = 1; i < regions.size(); i++) { + FoldRegion region = regions.get(i); + if (main.getStartOffset() > region.getStartOffset()) { + main = region; + } + } + return main; + } + public int getEndOffset(@NotNull FoldingGroup group) { final List regions = getGroupedRegions(group); int endOffset = 0; @@ -548,8 +566,7 @@ public class FoldingModelImpl extends InlayModel.SimpleAdapter @Override public void onUpdated(@NotNull Inlay inlay) { - Inlay.Placement placement = inlay.getPlacement(); - if (placement == Inlay.Placement.ABOVE_LINE || placement == Inlay.Placement.BELOW_LINE) myFoldTree.clearCachedInlayValues(); + if (inlay.getVerticalAlignment() != Inlay.VerticalAlignment.INLINE) myFoldTree.clearCachedInlayValues(); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/InlayImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/InlayImpl.java index c83cc0d4de24..31c1c8204db7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/InlayImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/InlayImpl.java @@ -10,9 +10,11 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.util.List; abstract class InlayImpl extends RangeMarkerWithGetterImpl implements Inlay { static final Key OFFSET_BEFORE_DISPOSAL = Key.create("inlay.offset.before.disposal"); + private static final Key ORDER_BEFORE_DISPOSAL = Key.create("inlay.order.before.disposal"); @NotNull final EditorImpl myEditor; @@ -76,10 +78,13 @@ abstract class InlayImpl inlays = inlayModel.getInlineElementsInRange(offset, offset); + putUserData(ORDER_BEFORE_DISPOSAL, inlays.indexOf(this)); putUserData(OFFSET_BEFORE_DISPOSAL, offset); //noinspection unchecked getTree().removeInterval((T)this); - myEditor.getInlayModel().notifyRemoved(this); + inlayModel.notifyRemoved(this); } } @@ -114,4 +119,9 @@ abstract class InlayImpl INLINE_ELEMENTS_COMPARATOR = Comparator.comparingInt((Inlay i) -> i.getOffset()) + private static final Comparator INLINE_ELEMENTS_COMPARATOR = Comparator.comparingInt((Inlay inlay) -> inlay.getOffset()) .thenComparing(i -> i.isRelatedToPrecedingText()); - private static final Comparator BLOCK_ELEMENTS_PRIORITY_COMPARATOR = Comparator.comparingInt(i -> -i.myPriority); - private static final Comparator BLOCK_ELEMENTS_COMPARATOR = Comparator.comparing((BlockInlayImpl i) -> i.getPlacement()) - .thenComparing(i -> i.getPlacement() == Inlay.Placement.ABOVE_LINE ? i.myPriority : -i.myPriority); - private static final Comparator AFTER_LINE_END_ELEMENTS_OFFSET_COMPARATOR = - Comparator.comparingInt((AfterLineEndInlayImpl i) -> i.getOffset()).thenComparingInt(i -> i.myOrder); - private static final Comparator AFTER_LINE_END_ELEMENTS_COMPARATOR = Comparator.comparingInt(i -> i.myOrder); + private static final Comparator BLOCK_ELEMENTS_PRIORITY_COMPARATOR = + Comparator.comparingInt((BlockInlayImpl i) -> -i.myPriority); + private static final Comparator BLOCK_ELEMENTS_COMPARATOR = + Comparator.comparing((BlockInlayImpl inlay) -> inlay.getVerticalAlignment()) + .thenComparing(i -> i.getVerticalAlignment() == Inlay.VerticalAlignment.ABOVE_LINE ? i.myPriority : -i.myPriority); private static final Processor UPDATE_SIZE_PROCESSOR = inlay -> { inlay.updateSize(); return true; @@ -45,7 +44,6 @@ public class InlayModelImpl implements InlayModel, Disposable, Dumpable { final List myInlaysInvalidatedOnMove = new ArrayList<>(); final RangeMarkerTree myInlineElementsTree; final MarkerTreeWithPartialSums myBlockElementsTree; - final RangeMarkerTree myAfterLineEndElementsTree; boolean myMoveInProgress; boolean myPutMergedIntervalsAtBeginning; @@ -56,7 +54,6 @@ public class InlayModelImpl implements InlayModel, Disposable, Dumpable { myEditor = editor; myInlineElementsTree = new InlineElementsTree(editor.getDocument()); myBlockElementsTree = new BlockElementsTree(editor.getDocument()); - myAfterLineEndElementsTree = new AfterLineEndElementTree(editor.getDocument()); myEditor.getDocument().addDocumentListener(new PrioritizedInternalDocumentListener() { @Override public int getPriority() { @@ -143,19 +140,6 @@ public class InlayModelImpl implements InlayModel, Disposable, Dumpable { return inlay; } - @Nullable - @Override - public Inlay addAfterLineEndElement(int offset, - boolean relatesToPrecedingText, - @NotNull T renderer) { - ApplicationManager.getApplication().assertIsDispatchThread(); - Document document = myEditor.getDocument(); - offset = Math.max(0, Math.min(document.getTextLength(), offset)); - AfterLineEndInlayImpl inlay = new AfterLineEndInlayImpl<>(myEditor, offset, relatesToPrecedingText, renderer); - notifyAdded(inlay); - return inlay; - } - @NotNull @Override public List getInlineElementsInRange(int startOffset, int endOffset) { @@ -298,10 +282,8 @@ public class InlayModelImpl implements InlayModel, Disposable, Dumpable { public Inlay getElementAt(@NotNull Point point) { boolean hasInlineElements = hasInlineElements(); boolean hasBlockElements = hasBlockElements(); - boolean hasAfterLineEndElements = hasAfterLineEndElements(); - if (!hasInlineElements && !hasBlockElements && !hasAfterLineEndElements) return null; + if (!hasInlineElements && !hasBlockElements) return null; - int offset = -1; VisualPosition visualPosition = myEditor.xyToVisualPosition(point); if (hasBlockElements) { int visualLine = visualPosition.line; @@ -332,84 +314,21 @@ public class InlayModelImpl implements InlayModel, Disposable, Dumpable { } } if (hasInlineElements) { - offset = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(visualPosition)); + int offset = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(visualPosition)); List inlays = getInlineElementsInRange(offset, offset); - if (!inlays.isEmpty()) { - VisualPosition startVisualPosition = myEditor.offsetToVisualPosition(offset); - int x = myEditor.visualPositionToXY(startVisualPosition).x; - Inlay inlay = findInlay(inlays, point, x); - if (inlay != null) return inlay; - } - } - if (hasAfterLineEndElements) { - if (offset < 0) offset = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(visualPosition)); - int logicalLine = myEditor.getDocument().getLineNumber(offset); - if (offset == myEditor.getDocument().getLineEndOffset(logicalLine) && !myEditor.getFoldingModel().isOffsetCollapsed(offset)) { - List inlays = myEditor.getInlayModel().getAfterLineEndElementsForLogicalLine(logicalLine); - if (!inlays.isEmpty()) { - Rectangle bounds = inlays.get(0).getBounds(); - assert bounds != null; - Inlay inlay = findInlay(inlays, point, bounds.x); - if (inlay != null) return inlay; - } + if (inlays.isEmpty()) return null; + + VisualPosition startVisualPosition = myEditor.offsetToVisualPosition(offset); + int x = myEditor.visualPositionToXY(startVisualPosition).x; + for (Inlay inlay : inlays) { + int endX = x + inlay.getWidthInPixels(); + if (point.x >= x && point.x < endX) return inlay; + x = endX; } } return null; } - private static Inlay findInlay(List inlays, @NotNull Point point, int startX) { - for (Inlay inlay : inlays) { - int endX = startX + inlay.getWidthInPixels(); - if (point.x >= startX && point.x < endX) return inlay; - startX = endX; - } - return null; - } - - @NotNull - @Override - public List getAfterLineEndElementsInRange(int startOffset, int endOffset) { - if (!hasAfterLineEndElements()) return Collections.emptyList(); - List range = - getElementsInRange(myAfterLineEndElementsTree, startOffset, endOffset, inlay -> true, AFTER_LINE_END_ELEMENTS_OFFSET_COMPARATOR); - //noinspection unchecked - return (List)range; - } - - @NotNull - @Override - public List> getAfterLineEndElementsInRange(int startOffset, int endOffset, Class type) { - if (!hasAfterLineEndElements()) return Collections.emptyList(); - List range = - getElementsInRange(myAfterLineEndElementsTree, startOffset, endOffset, inlay -> type.isInstance(inlay.myRenderer), - AFTER_LINE_END_ELEMENTS_OFFSET_COMPARATOR); - //noinspection unchecked - return (List)range; - } - - @NotNull - @Override - public List getAfterLineEndElementsForLogicalLine(int logicalLine) { - DocumentEx document = myEditor.getDocument(); - if (!hasAfterLineEndElements() || logicalLine < 0 || logicalLine > 0 && logicalLine >= document.getLineCount()) { - return Collections.emptyList(); - } - List result = new ArrayList<>(); - int startOffset = document.getLineStartOffset(logicalLine); - int endOffset = document.getLineEndOffset(logicalLine); - myAfterLineEndElementsTree.processOverlappingWith(startOffset, endOffset, inlay -> { - result.add(inlay); - return true; - }); - result.sort(AFTER_LINE_END_ELEMENTS_COMPARATOR); - //noinspection unchecked - return (List)result; - } - - public boolean hasAfterLineEndElements() { - return myAfterLineEndElementsTree.size() > 0; - } - @Override public void setConsiderCaretPositionOnDocumentUpdates(boolean enabled) { myConsiderCaretPositionOnDocumentUpdates = enabled; @@ -439,24 +358,6 @@ public class InlayModelImpl implements InlayModel, Disposable, Dumpable { } } - @NotNull - @Override - public String dumpState() { - return "Inline elements: " + dumpInlays(myInlineElementsTree) - + ", after-line-end elements: " + dumpInlays(myAfterLineEndElementsTree) - + ", block elements: " + dumpInlays(myBlockElementsTree); - } - - - private static String dumpInlays(RangeMarkerTree tree) { - StringJoiner joiner = new StringJoiner(",", "[", "]"); - tree.processAll(o -> { - joiner.add(Integer.toString(o.getOffset())); - return true; - }); - return joiner.toString(); - } - private class InlineElementsTree extends HardReferencingRangeMarkerTree { InlineElementsTree(@NotNull Document document) { super(document); @@ -507,17 +408,4 @@ public class InlayModelImpl implements InlayModel, Disposable, Dumpable { } } } - - private class AfterLineEndElementTree extends HardReferencingRangeMarkerTree { - AfterLineEndElementTree(@NotNull Document document) { - super(document); - } - - @Override - void fireBeforeRemoved(@NotNull AfterLineEndInlayImpl inlay, @NotNull @NonNls Object reason) { - if (inlay.getUserData(InlayImpl.OFFSET_BEFORE_DISPOSAL) == null) { - notifyRemoved(inlay); - } - } - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/InlineInlayImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/InlineInlayImpl.java index db74e5bde033..0f8c380e68d7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/InlineInlayImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/InlineInlayImpl.java @@ -5,7 +5,6 @@ import com.intellij.openapi.editor.EditorCustomElementRenderer; import com.intellij.openapi.editor.Inlay; import com.intellij.openapi.editor.VisualPosition; import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.util.Key; import com.intellij.util.DocumentUtil; import org.jetbrains.annotations.NotNull; @@ -13,8 +12,6 @@ import java.awt.*; import java.util.List; class InlineInlayImpl extends InlayImpl { - private static final Key ORDER_BEFORE_DISPOSAL = Key.create("inlay.order.before.disposal"); - InlineInlayImpl(@NotNull EditorImpl editor, int offset, boolean relatesToPrecedingText, @@ -51,16 +48,6 @@ class InlineInlayImpl extends InlayImpl inlays = myEditor.getInlayModel().getInlineElementsInRange(offset, offset); - putUserData(ORDER_BEFORE_DISPOSAL, inlays.indexOf(this)); - } - super.dispose(); - } - @Override void doUpdateSize() { myWidthInPixels = myRenderer.calcWidthInPixels(this); @@ -71,8 +58,8 @@ class InlineInlayImpl extends InlayImpl extends InlayImpl - * This constructor is assumed to be used before the document change. - * + * This constructor is assumed to be used before the document change, {@link #updateAfterDocumentChange(Document)} + * should be called 'after' document change to complete object creation. + * * @param event object that describes document change that caused cache update */ IncrementalCacheUpdateEvent(@NotNull DocumentEvent event, @NotNull EditorImpl editor) { @@ -43,12 +46,13 @@ public class IncrementalCacheUpdateEvent { */ IncrementalCacheUpdateEvent(int startOffset, int endOffset, @NotNull EditorImpl editor) { this(startOffset, endOffset, endOffset, editor); + myNewEndLogicalLine = myOldEndLogicalLine; } /** * Creates new {@code IncrementalCacheUpdateEvent} object that is configured to perform whole reparse of the given * document. - * + * * @param document target document to reparse */ IncrementalCacheUpdateEvent(@NotNull Document document) { @@ -56,8 +60,9 @@ public class IncrementalCacheUpdateEvent { myMandatoryEndOffset = document.getTextLength(); myLengthDiff = 0; myStartLogicalPosition = new LogicalPosition(0, 0); + myOldEndLogicalLine = myNewEndLogicalLine = Math.max(0, document.getLineCount() - 1); } - + private IncrementalCacheUpdateEvent(int startOffset, int oldEndOffset, int newEndOffset, @NotNull EditorImpl editor) { VisualLineInfo info = getVisualLineInfo(editor, startOffset, false); if (info.startsWithSoftWrap) { @@ -67,6 +72,7 @@ public class IncrementalCacheUpdateEvent { myStartLogicalPosition = editor.offsetToLogicalPosition(myStartOffset); myMandatoryEndOffset = newEndOffset; myLengthDiff = newEndOffset - oldEndOffset; + myOldEndLogicalLine = editor.getDocument().getLineNumber(oldEndOffset); } @@ -82,11 +88,11 @@ public class IncrementalCacheUpdateEvent { int wrapIndex = softWrapModel.getSoftWrapIndex(offset); int prevSoftWrapIndex = wrapIndex < 0 ? - wrapIndex - 2 : wrapIndex - (beforeSoftWrap ? 1 : 0); SoftWrap prevSoftWrap = prevSoftWrapIndex < 0 ? null : softWrapModel.getRegisteredSoftWraps().get(prevSoftWrapIndex); - + int visualLineStartOffset = prevSoftWrap == null ? startOffset : Math.max(startOffset, prevSoftWrap.getStart()); return new VisualLineInfo(visualLineStartOffset, prevSoftWrap != null && prevSoftWrap.getStart() == visualLineStartOffset); } - + private static class VisualLineInfo { private final int startOffset; private final boolean startsWithSoftWrap; @@ -97,6 +103,10 @@ public class IncrementalCacheUpdateEvent { } } + void updateAfterDocumentChange(@NotNull Document document) { + myNewEndLogicalLine = document.getLineNumber(myMandatoryEndOffset); + } + /** * Returns offset, from which soft wrap recalculation should start */ @@ -144,6 +154,8 @@ public class IncrementalCacheUpdateEvent { ", mandatoryEndOffset=" + myMandatoryEndOffset + ", actualEndOffset=" + myActualEndOffset + ", lengthDiff=" + myLengthDiff + - ", startLogicalPosition=" + myStartLogicalPosition; + ", startLogicalPosition=" + myStartLogicalPosition + + ", oldEndLogicalLine=" + myOldEndLogicalLine + + ", newEndLogicalLine=" + myNewEndLogicalLine; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java index cdc505f227bb..066415b3b9b1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java @@ -295,9 +295,7 @@ public class SoftWrapApplianceManager implements Dumpable { myContext.softWrapStartOffset++; } - myContext.inlineInlays = myEditor.getInlayModel().getInlineElementsInRange(start, endOffsetUpperEstimate); - myContext.afterLineEndInlays = myEditor.getInlayModel().getAfterLineEndElementsInRange(DocumentUtil.getLineStartOffset(start, document), - endOffsetUpperEstimate); + myContext.inlays = myEditor.getInlayModel().getInlineElementsInRange(start, endOffsetUpperEstimate); // Perform soft wraps calculation. while (!iterationState.atEnd()) { @@ -949,15 +947,10 @@ public class SoftWrapApplianceManager implements Dumpable { myDocumentChangedEvent = new IncrementalCacheUpdateEvent(event, myEditor); } - public void documentChanged(DocumentEvent event, boolean processAlsoLineEnd) { + public void documentChanged(DocumentEvent event) { LOG.assertTrue(myDocumentChangedEvent != null); + myDocumentChangedEvent.updateAfterDocumentChange(event.getDocument()); recalculate(myDocumentChangedEvent); - if (processAlsoLineEnd) { - int lineEndOffset = DocumentUtil.getLineEndOffset(myDocumentChangedEvent.getMandatoryEndOffset(), event.getDocument()); - if (lineEndOffset > myDocumentChangedEvent.getActualEndOffset()) { - recalculate(new IncrementalCacheUpdateEvent(lineEndOffset, lineEndOffset, myEditor)); - } - } myDocumentChangedEvent = null; } @@ -1212,10 +1205,8 @@ public class SoftWrapApplianceManager implements Dumpable { int fontType; boolean skipToLineEnd; - List inlineInlays; - int inlineInlayIndex; - List afterLineEndInlays; - int afterLineEndInlayIndex; + List inlays; + int inlayIndex; @Override public String toString() { @@ -1241,10 +1232,8 @@ public class SoftWrapApplianceManager implements Dumpable { skipToLineEnd = false; fontType2spaceWidth.reset(); logicalLineData.reset(); - inlineInlays = null; - inlineInlayIndex = 0; - afterLineEndInlays = null; - afterLineEndInlayIndex = 0; + inlays = null; + inlayIndex = 0; } int getSpaceWidth() { @@ -1329,11 +1318,11 @@ public class SoftWrapApplianceManager implements Dumpable { } private int getInlaysWidthForOffset(int offset) { - while (inlineInlayIndex < inlineInlays.size() && inlineInlays.get(inlineInlayIndex).getOffset() < offset) inlineInlayIndex++; - while (inlineInlayIndex > 0 && inlineInlays.get(inlineInlayIndex - 1).getOffset() >= offset) inlineInlayIndex--; + while (inlayIndex < inlays.size() && inlays.get(inlayIndex).getOffset() < offset) inlayIndex++; + while (inlayIndex > 0 && inlays.get(inlayIndex - 1).getOffset() >= offset) inlayIndex--; int width = 0; - while (inlineInlayIndex < inlineInlays.size() && inlineInlays.get(inlineInlayIndex).getOffset() == offset) { - width += inlineInlays.get(inlineInlayIndex++).getWidthInPixels(); + while (inlayIndex < inlays.size() && inlays.get(inlayIndex).getOffset() == offset) { + width += inlays.get(inlayIndex++).getWidthInPixels(); } return width; } @@ -1343,28 +1332,7 @@ public class SoftWrapApplianceManager implements Dumpable { return nextOffset < text.length() && text.charAt(nextOffset) != '\n' || nextOffset > tokenEndOffset || nextOffset == tokenEndOffset && nextIsFoldRegion - ? 0 : getInlaysWidthForOffset(nextOffset) + getAfterLineEndInlaysWidth(currentPosition.logicalLine); - } - - private int getAfterLineEndInlaysWidth(int logicalLine) { - int startOffset = myEditor.getDocument().getLineStartOffset(logicalLine); - int endOffset = myEditor.getDocument().getLineEndOffset(logicalLine); - while (afterLineEndInlayIndex < afterLineEndInlays.size() - && afterLineEndInlays.get(afterLineEndInlayIndex).getOffset() < startOffset) { - afterLineEndInlayIndex++; - } - while (afterLineEndInlayIndex > 0 && afterLineEndInlays.get(afterLineEndInlayIndex - 1).getOffset() >= startOffset) { - afterLineEndInlayIndex--; - } - int width = 0; - while (afterLineEndInlayIndex < afterLineEndInlays.size()) { - Inlay inlay = afterLineEndInlays.get(afterLineEndInlayIndex); - int offset = inlay.getOffset(); - if (offset < startOffset || offset > endOffset) break; - width += inlay.getWidthInPixels(); - afterLineEndInlayIndex++; - } - return width; + ? 0 : getInlaysWidthForOffset(nextOffset); } /** diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java index 2e403b15304a..f4f3e6da963c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java @@ -3,6 +3,8 @@ */ package com.intellij.openapi.editor.impl.view; +import com.intellij.openapi.diagnostic.Attachment; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.util.EditorUtil; @@ -26,6 +28,8 @@ import java.util.List; * @see VisualPosition */ class EditorCoordinateMapper { + private static final Logger LOG = Logger.getInstance(EditorCoordinateMapper.class); + private final EditorView myView; private final Document myDocument; private final FoldingModelImpl myFoldingModel; @@ -93,28 +97,19 @@ class EditorCoordinateMapper { int column = pos.column; int logicalLineCount = myDocument.getLineCount(); if (line >= logicalLineCount) { - if (line == 0) { - int resultColumn = logToVisWithInlays(0, column, pos.leansForward); - if (resultColumn < 0) resultColumn = Integer.MAX_VALUE; // guarding against overflow - return new VisualPosition(0, resultColumn, pos.leansForward); - } - else { - return new VisualPosition(line - logicalLineCount + myView.getEditor().getVisibleLineCount(), column, pos.leansForward); - } + return new VisualPosition(line - logicalLineCount + myView.getEditor().getVisibleLineCount(), column, pos.leansForward); } int offset = logicalPositionToOffset(pos); int visualLine = offsetToVisualLine(offset, beforeSoftWrap); int maxVisualColumn = 0; int maxLogicalColumn = 0; - int endLogicalLine = line; for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap)) { if (!pos.leansForward && offset == fragment.getVisualLineStartOffset()) { return new VisualPosition(visualLine, fragment.getStartVisualColumn()); } - endLogicalLine = fragment.getEndLogicalLine(); - maxVisualColumn = fragment.getEndVisualColumn(); if (fragment.isCollapsedFoldRegion()) { int startLogicalLine = fragment.getStartLogicalLine(); + int endLogicalLine = fragment.getEndLogicalLine(); int startLogicalColumn = fragment.getStartLogicalColumn(); int endLogicalColumn = fragment.getEndLogicalColumn(); if ((line > startLogicalLine || line == startLogicalLine && (column > startLogicalColumn || @@ -123,7 +118,7 @@ class EditorCoordinateMapper { return new VisualPosition(visualLine, fragment.getStartVisualColumn(), true); } if (line == endLogicalLine && column == endLogicalColumn && !pos.leansForward) { - return new VisualPosition(visualLine, maxVisualColumn); + return new VisualPosition(visualLine, fragment.getEndVisualColumn()); } maxLogicalColumn = startLogicalLine == endLogicalLine ? Math.max(maxLogicalColumn, endLogicalColumn) : endLogicalColumn; } @@ -138,47 +133,31 @@ class EditorCoordinateMapper { } maxLogicalColumn = Math.max(maxLogicalColumn, maxColumn); } + maxVisualColumn = fragment.getEndVisualColumn(); + } + int resultColumn = column - maxLogicalColumn + maxVisualColumn; + if (resultColumn < 0) { + if (maxVisualColumn > maxLogicalColumn) { + resultColumn = Integer.MAX_VALUE; // guarding against overflow + } + else { + LOG.error("Error converting " + pos + " to visual position", + new Attachment("details.txt", String.format("offset: %d, visual line: %d, max logical column: %d, max visual column: %d", + offset, visualLine, maxLogicalColumn, maxVisualColumn)), + new Attachment("dump.txt", myView.getEditor().dumpState())); + resultColumn = 0; + } } - int resultColumn = maxVisualColumn + logToVisWithInlays(endLogicalLine, column - maxLogicalColumn, pos.leansForward); - if (resultColumn < 0) resultColumn = Integer.MAX_VALUE; // guarding against overflow return new VisualPosition(visualLine, resultColumn, pos.leansForward); } - private int logToVisWithInlays(int logLine, int remainingLogColumn, boolean leansForward) { - if (remainingLogColumn > 1 || remainingLogColumn == 1 && leansForward) { - remainingLogColumn += myView.getEditor().getInlayModel().getAfterLineEndElementsForLogicalLine(logLine).size(); - } - return remainingLogColumn; - } - - private int visToLogWithInlays(int logLine, int remainingVisColumns, boolean[] leansForward) { - if (remainingVisColumns == 0) return 0; - int inlayCount = myView.getEditor().getInlayModel().getAfterLineEndElementsForLogicalLine(logLine).size(); - if (inlayCount == 0) return remainingVisColumns; - if (remainingVisColumns < inlayCount + 1) { - leansForward[0] = false; - return 1; - } - if (remainingVisColumns == inlayCount + 1) { - leansForward[0] = true; - } - return remainingVisColumns - inlayCount; - } - @NotNull LogicalPosition visualToLogicalPosition(@NotNull VisualPosition pos) { int line = pos.line; int column = pos.column; int visualLineCount = myView.getEditor().getVisibleLineCount(); if (line >= visualLineCount) { - if (line == 0) { - boolean[] leansForward = new boolean[] {pos.leansRight}; - int resultColumn = visToLogWithInlays(0, column, leansForward); - return new LogicalPosition(0, resultColumn, leansForward[0]); - } - else { - return new LogicalPosition(line - visualLineCount + myDocument.getLineCount(), column, pos.leansRight); - } + return new LogicalPosition(line - visualLineCount + myDocument.getLineCount(), column, pos.leansRight); } int offset = visualLineToOffset(line); int logicalLine = myDocument.getLineNumber(offset); @@ -214,10 +193,11 @@ class EditorCoordinateMapper { maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); } if (myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) == null) { - boolean[] leansForward = new boolean[] {pos.leansRight}; - int resultColumn = maxLogicalColumn + visToLogWithInlays(logicalLine, column - maxVisualColumn, leansForward); - if (resultColumn < 0) resultColumn = Integer.MAX_VALUE; // guarding against overflow - return new LogicalPosition(logicalLine, resultColumn, leansForward[0]); + int resultColumn = column - maxVisualColumn + maxLogicalColumn; + if (resultColumn < 0 && maxLogicalColumn > maxVisualColumn) { + resultColumn = Integer.MAX_VALUE; // guarding against overflow + } + return new LogicalPosition(logicalLine, resultColumn, true); } else { return offsetToLogicalPosition(maxOffset).leanForward(true); @@ -321,7 +301,6 @@ class EditorCoordinateMapper { int lastColumn = 0; float x = getStartX(visualLine); float px = (float)p.getX(); - int logicalLine = -1; if (visualLine < myView.getEditor().getVisibleLineCount()) { int visualLineStartOffset = visualLineToOffset(visualLine); int maxOffset = 0; @@ -346,7 +325,6 @@ class EditorCoordinateMapper { x = nextX; lastColumn = fragment.getEndVisualColumn(); maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); - logicalLine = fragment.getEndLogicalLine(); } if (myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) != null) { int markerWidth = myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); @@ -356,34 +334,10 @@ class EditorCoordinateMapper { } px -= markerWidth; lastColumn++; - logicalLine = -1; } - else if (logicalLine == -1) { - logicalLine = myDocument.getLineNumber(visualLineStartOffset); - } - } - else if (visualLine == 0) { - logicalLine = 0; } float plainSpaceWidth = myView.getPlainSpaceWidth(); float remainingShift = px - x; - if (remainingShift > plainSpaceWidth && logicalLine >= 0) { - List inlays = myView.getEditor().getInlayModel().getAfterLineEndElementsForLogicalLine(logicalLine); - int inlaysWidth = 0; - int inlayCount = 0; - for (Inlay inlay : inlays) { - int width = inlay.getWidthInPixels(); - int newWidth = inlaysWidth + width; - if (remainingShift <= plainSpaceWidth + newWidth) { - boolean leftPart = remainingShift <= plainSpaceWidth + (inlaysWidth + newWidth) / 2; - return new VisualPosition(visualLine, lastColumn + 1 + inlayCount + (leftPart ? 0 : 1), leftPart); - } - inlaysWidth = newWidth; - inlayCount++; - } - remainingShift -= inlaysWidth; - lastColumn += inlayCount; - } int additionalColumns = remainingShift <= 0 ? 0 : Math.round(remainingShift / plainSpaceWidth); return new VisualPosition(visualLine, lastColumn + additionalColumns, remainingShift > additionalColumns * plainSpaceWidth); } @@ -395,7 +349,6 @@ class EditorCoordinateMapper { int y = visualLineToY(visualLine); float x = getStartX(visualLine); int lastColumn = 0; - int logicalLine = -1; if (visualLine < myView.getEditor().getVisibleLineCount()) { int visualLineStartOffset = visualLineToOffset(visualLine); int maxOffset = 0; @@ -411,32 +364,11 @@ class EditorCoordinateMapper { x = fragment.getEndX(); lastColumn = endColumn; maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); - logicalLine = fragment.getEndLogicalLine(); } if (column > lastColumn && myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) != null) { column--; x += myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); } - else if (logicalLine == -1) { - logicalLine = myDocument.getLineNumber(visualLineStartOffset); - } - } - else if (visualLine == 0) { - logicalLine = 0; - } - if (column > lastColumn + 1 && logicalLine >= 0) { - List inlays = myView.getEditor().getInlayModel().getAfterLineEndElementsForLogicalLine(logicalLine); - int inlaysWidth = 0; - int inlayCount = 0; - for (Inlay inlay : inlays) { - inlayCount++; - inlaysWidth += inlay.getWidthInPixels(); - if (column == lastColumn + 1 + inlayCount) { - break; - } - } - x += inlaysWidth; - column -= inlayCount; } float additionalShift = column <= lastColumn ? 0 : (column - lastColumn) * myView.getPlainSpaceWidth(); return new Point2D.Double(x + additionalShift, y); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java index 573f718245d8..ae6632c1b4c8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java @@ -501,20 +501,7 @@ public class EditorPainter implements TextDrawingCallback { int offset = iterationState.getEndOffset(); SoftWrapModelImpl softWrapModel = myEditor.getSoftWrapModel(); if (softWrapModel.getSoftWrap(offset) == null) { - int logicalLine = myDocument.getLineNumber(offset); - List inlays = myEditor.getInlayModel().getAfterLineEndElementsForLogicalLine(logicalLine); - if (!inlays.isEmpty()) { - x += myView.getPlainSpaceWidth(); - int lineHeight = myView.getLineHeight(); - TextAttributes backgroundAttributes = iterationState.getPastLineEndBackgroundAttributes(); - for (Inlay inlay : inlays) { - int width = inlay.getWidthInPixels(); - inlay.getRenderer().paint(inlay, g, new Rectangle((int) x, y - myView.getAscent(), width, lineHeight), - backgroundAttributes); - x += width; - } - } - paintLineExtensions(g, visualLine, logicalLine, x, y, extensionData); + paintLineExtensions(g, visualLine, offset, x, y, extensionData); } else if (paintSoftWraps) { softWrapModel.doPaint(g, SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED, @@ -684,7 +671,7 @@ public class EditorPainter implements TextDrawingCallback { } } - private void paintLineExtensions(Graphics2D g, int visualLine, int logicalLine, float x, int y, + private void paintLineExtensions(Graphics2D g, int visualLine, int offset, float x, int y, TIntObjectHashMap> extensionData) { List data = extensionData.get(visualLine); if (data == null) return; @@ -695,7 +682,7 @@ public class EditorPainter implements TextDrawingCallback { int currentLineWidth = myCorrector.lineWidth(visualLine, x); EditorSizeManager sizeManager = myView.getSizeManager(); if (currentLineWidth > sizeManager.getMaxLineWithExtensionWidth()) { - sizeManager.setMaxLineWithExtensionWidth(logicalLine, currentLineWidth); + sizeManager.setMaxLineWithExtensionWidth(myDocument.getLineNumber(offset), currentLineWidth); myEditor.getContentComponent().revalidate(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java index b983798b6737..1a177e008e2f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java @@ -17,7 +17,6 @@ import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapAwareDocumentPa import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; -import com.intellij.util.DocumentUtil; import gnu.trove.TIntArrayList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -64,7 +63,6 @@ class EditorSizeManager extends InlayModel.SimpleAdapter implements PrioritizedD private boolean myDirty; // true if we cannot calculate preferred size now because soft wrap model was invalidated after editor // became hidden. myLineWidths contents is irrelevant in such a state. Previously calculated preferred size // is kept until soft wraps will be recalculated and size calculations will become possible - private boolean myAfterLineEndInlayUpdated; private final List myDeferredRanges = new ArrayList<>(); @@ -97,7 +95,6 @@ class EditorSizeManager extends InlayModel.SimpleAdapter implements PrioritizedD @Override public void beforeDocumentChange(@NotNull DocumentEvent event) { - myAfterLineEndInlayUpdated = false; myDuringDocumentUpdate = true; if (myDocument.isInBulkUpdate()) return; myDocumentChangeStartOffset = event.getOffset(); @@ -109,10 +106,6 @@ class EditorSizeManager extends InlayModel.SimpleAdapter implements PrioritizedD myDuringDocumentUpdate = false; if (myDocument.isInBulkUpdate()) return; doInvalidateRange(myDocumentChangeStartOffset, myDocumentChangeEndOffset); - if (myAfterLineEndInlayUpdated) { - int lineEndOffset = DocumentUtil.getLineEndOffset(myDocumentChangeEndOffset, myDocument); - doInvalidateRange(lineEndOffset, lineEndOffset); - } assertValidState(); } @@ -143,19 +136,8 @@ class EditorSizeManager extends InlayModel.SimpleAdapter implements PrioritizedD @Override public void onUpdated(@NotNull Inlay inlay) { - if (myDocument.isInBulkUpdate() - || inlay.getPlacement() != Inlay.Placement.INLINE && inlay.getPlacement() != Inlay.Placement.AFTER_LINE_END) return; - if (myDuringDocumentUpdate) { - if (inlay.getPlacement() == Inlay.Placement.AFTER_LINE_END) { - myAfterLineEndInlayUpdated = true; - } - return; - } - int offset = inlay.getOffset(); - if (inlay.getPlacement() == Inlay.Placement.AFTER_LINE_END) { - offset = DocumentUtil.getLineEndOffset(offset, myDocument); - } - doInvalidateRange(offset, offset); + if (myDuringDocumentUpdate || myDocument.isInBulkUpdate() || inlay.getVerticalAlignment() != Inlay.VerticalAlignment.INLINE) return; + doInvalidateRange(inlay.getOffset(), inlay.getOffset()); } private void onSoftWrapRecalculationEnd(IncrementalCacheUpdateEvent event) { @@ -342,8 +324,7 @@ class EditorSizeManager extends InlayModel.SimpleAdapter implements PrioritizedD FoldRegion[] topLevelRegions = myEditor.getFoldingModel().fetchTopLevel(); if (quickEvaluationListener != null && (topLevelRegions == null || topLevelRegions.length == 0) && myEditor.getSoftWrapModel().getRegisteredSoftWraps().isEmpty() && - !myView.getTextLayoutCache().hasCachedLayoutFor(visualLine) - && !myEditor.getInlayModel().hasInlineElements() && !myEditor.getInlayModel().hasAfterLineEndElements()) { + !myView.getTextLayoutCache().hasCachedLayoutFor(visualLine) && !myEditor.getInlayModel().hasInlineElements()) { // fast path - speeds up editor opening quickEvaluationListener.run(); return (int)(myView.getLogicalPositionCache().offsetToLogicalColumn(visualLine, @@ -362,15 +343,6 @@ class EditorSizeManager extends InlayModel.SimpleAdapter implements PrioritizedD if (myEditor.getSoftWrapModel().getSoftWrap(maxOffset) != null) { x += myEditor.getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); } - else { - List inlays = myEditor.getInlayModel().getAfterLineEndElementsForLogicalLine(iterator.getEndLogicalLine()); - if (!inlays.isEmpty()) { - x += myView.getPlainSpaceWidth(); - for (Inlay inlay : inlays) { - x += inlay.getWidthInPixels(); - } - } - } return (int)x; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLinesIterator.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLinesIterator.java index 229e41804876..9db69c10d4ac 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLinesIterator.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLinesIterator.java @@ -164,7 +164,7 @@ public class VisualLinesIterator { int foldIndex = myLocation.foldRegion; while (foldIndex < myFoldRegions.length && myFoldRegions[foldIndex].getEndOffset() <= inlayOffset) foldIndex++; if (foldIndex < myFoldRegions.length && myFoldRegions[foldIndex].getStartOffset() <= inlayOffset) continue; - (inlay.getPlacement() == Inlay.Placement.ABOVE_LINE ? myInlaysAbove : myInlaysBelow).add(inlay); + (inlay.getVerticalAlignment() == Inlay.VerticalAlignment.ABOVE_LINE ? myInlaysAbove : myInlaysBelow).add(inlay); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentInlayModel.java b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentInlayModel.java index 4177538b6050..0c134f798806 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentInlayModel.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentInlayModel.java @@ -32,14 +32,6 @@ public class TextComponentInlayModel implements InlayModel { return null; } - @Nullable - @Override - public Inlay addAfterLineEndElement(int offset, - boolean relatesToPrecedingText, - @NotNull T renderer) { - return null; - } - @NotNull @Override public List getInlineElementsInRange(int startOffset, int endOffset) { @@ -75,18 +67,6 @@ public class TextComponentInlayModel implements InlayModel { return null; } - @NotNull - @Override - public List getAfterLineEndElementsInRange(int startOffset, int endOffset) { - return Collections.emptyList(); - } - - @NotNull - @Override - public List getAfterLineEndElementsForLogicalLine(int logicalLine) { - return Collections.emptyList(); - } - @Override public void setConsiderCaretPositionOnDocumentUpdates(boolean enabled) {}