From 744725eade311d7a6fb501357c6cfc4e641af88b Mon Sep 17 00:00:00 2001 From: Stanislav Alekseev Date: Tue, 3 Mar 2026 19:14:47 +0200 Subject: [PATCH] [editor] IJPL-237520 paint selection correctly when soft wrap symbols are present (cherry picked from commit 9736ba82194fc3b14a37c00402028dcc9c8096ea) IJ-CR-194975 GitOrigin-RevId: 8bdc1980e0da580fa81d7a5a05ba8917a93f6001 --- .../editor/impl/view/EditorPainter.java | 20 ++++++++++--------- .../editor/impl/view/IterationState.java | 7 +++---- .../editor/impl/view/LineFragmentPainter.java | 1 + .../editor/impl/view/SelectionLinePainter.kt | 12 ----------- 4 files changed, 15 insertions(+), 25 deletions(-) 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 fa0e2aeb027a..90b614480fff 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 @@ -523,7 +523,7 @@ public final class EditorPainter implements TextDrawingCallback { if (visualLine == 0) xEnd -= myView.getPrefixTextWidthInPixels(); paintBackground(attributes, startX, y, xEnd); if (shouldUseNewSelection() - && it.isInSelection() + && it.isInSelection(true) && myEditor.isRightAligned()) { mySelectionLinePainter.paintSelection(new Rectangle2D.Float( xEnd - selectionExtensionWidth, y, @@ -542,7 +542,7 @@ public final class EditorPainter implements TextDrawingCallback { } @Override - public void paint(VisualLineFragmentsIterator.Fragment fragment, int start, int end, + public void paint(IterationState it, VisualLineFragmentsIterator.Fragment fragment, int start, int end, TextAttributes attributes, float xStart, float xEnd, int y) { if (dryRun) return; FoldRegion foldRegion = fragment.getCurrentFoldRegion(); @@ -551,6 +551,11 @@ public final class EditorPainter implements TextDrawingCallback { if (foldRegionInnerAttributes == null || !paintFoldingBackground(foldRegionInnerAttributes, xStart, y, xEnd - xStart, foldRegion)) { paintBackground(attributes, xStart, y, xEnd - xStart); + if (it != null && it.isInSelection(false) && shouldUseNewSelection()) { + mySelectionLinePainter.paintSelection(new Rectangle2D.Float( + xStart, y, xEnd - xStart, myLineHeight + )); + } } Inlay inlay = fragment.getCurrentInlay(); if (inlay != null) { @@ -625,7 +630,7 @@ public final class EditorPainter implements TextDrawingCallback { return; } paintBackground(backgroundAttributes.getBackgroundColor(), x, y, endX - x, myLineHeight); - if (it.isInSelection() && shouldUseNewSelection() && !myEditor.isRightAligned()) { + if (it.hasPastLineEndExtension() && shouldUseNewSelection() && !myEditor.isRightAligned()) { mySelectionLinePainter.paintSelection( new Rectangle2D.Float(x, y, selectionExtensionWidth, myLineHeight) ); @@ -789,9 +794,6 @@ public final class EditorPainter implements TextDrawingCallback { if (attributes == null) return; paintBackground(attributes.getBackgroundColor(), x, y, width, height); - if (shouldUseNewSelection() && mySelectionLinePainter.isLineInSelection(x, y, width)) { - mySelectionLinePainter.paintSelection(new Rectangle2D.Float(x, y, width, height)); - } } private void paintBackground(Color color, float x, int y, float width) { @@ -1720,7 +1722,7 @@ public final class EditorPainter implements TextDrawingCallback { TextAttributes attributes = it.getStartOffset() == start ? it.getBreakAttributes() : it.getMergedAttributes(); float xNew = fragment.getEndX(); if (xNew >= myClip.getMinX()) { - painter.paint(fragment, 0, 0, attributes, x, xNew, y); + painter.paint(it, fragment, 0, 0, attributes, x, xNew, y); } x = xNew; } @@ -1734,7 +1736,7 @@ public final class EditorPainter implements TextDrawingCallback { int curEnd = fragment.isRtl() ? Math.max(it.getEndOffset(), end) : Math.min(it.getEndOffset(), end); float xNew = fragment.offsetToX(x, start, curEnd); if (xNew >= myClip.getMinX()) { - painter.paint(fragment, + painter.paint(it, fragment, fragment.isRtl() ? fragmentStartOffset - start : start - fragmentStartOffset, fragment.isRtl() ? fragmentStartOffset - curEnd : curEnd - fragmentStartOffset, attributes, x, xNew, y); @@ -1755,7 +1757,7 @@ public final class EditorPainter implements TextDrawingCallback { else { float xNew = fragment.getEndX(); if (xNew >= myClip.getMinX()) { - painter.paint(fragment, 0, fragment.getVisualLength(), getFoldRegionAttributes(foldRegion), x, xNew, y); + painter.paint(it, fragment, 0, fragment.getVisualLength(), getFoldRegionAttributes(foldRegion), x, xNew, y); } x = xNew; prevEndOffset = -1; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java index 3794c6bf3dad..821f04b6bcf7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java @@ -296,8 +296,8 @@ public final class IterationState { } @ApiStatus.Internal - public boolean isInSelection() { - return myIsInSelection; + public boolean hasPastLineEndExtension() { + return isInSelection(hasSoftWrap()); } @ApiStatus.Internal @@ -405,7 +405,7 @@ public final class IterationState { ); } - private boolean isInSelection(boolean atBreak) { + boolean isInSelection(boolean atBreak) { return myCurrentSelectionIndex < myCaretData.selectionsSize() && (myReverseIteration ? lessThan(myStartOffset, myCaretData.selectionEnd(myCurrentSelectionIndex, true), !atBreak) : lessThan(myCaretData.selectionStart(myCurrentSelectionIndex, false), myStartOffset, !atBreak)); @@ -527,7 +527,6 @@ public final class IterationState { private void setAttributes(TextAttributes attributes, boolean atBreak, boolean beforeBreak) { boolean isInSelection = isInSelection(atBreak); - myIsInSelection = isInSelection; boolean isInCaretRow = isInCaretRow( !myReverseIteration && (!atBreak || !beforeBreak), myReverseIteration || (atBreak && beforeBreak) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragmentPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragmentPainter.java index 8e700ec97137..4c5dadd60dd3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragmentPainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragmentPainter.java @@ -16,6 +16,7 @@ interface LineFragmentPainter { ); void paint( + IterationState iterationState, VisualLineFragmentsIterator.Fragment fragment, int start, int end, diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SelectionLinePainter.kt b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SelectionLinePainter.kt index dc55bd3c678d..9b60392294e7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SelectionLinePainter.kt +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SelectionLinePainter.kt @@ -211,10 +211,6 @@ internal class SelectionLinePainter( return editor.yToVisualLine(y - yShift) } - private fun visualLineToY(visualLine: Int): Int { - return editor.visualLineToY(visualLine) + yShift - } - private fun customFoldRegionsFor(visualLine: Int): List { return customFoldRegions.filter { cfr -> val startLine = editor.offsetToVisualLine(cfr.startOffset) @@ -360,14 +356,6 @@ internal class SelectionLinePainter( return customFoldRegionsFor(visualLine).isNotEmpty() || caretSelectionsForLine(visualLine).hasSelectionEnd(false, block.bottomRight.x) } - fun isLineInSelection(x: Float, y: Int, width: Float): Boolean { - val line = yToVisualLine(y) - if (y != visualLineToY(line)) return false - - val selection = caretSelectionsForLine(line).selectionContaining(x.toDouble()) ?: return false - return selection.contains((x + width).toDouble()) - } - private fun paintRoundedBlock(block: SelectionRectangle, cornerTypes: Array) { val (topLeftType, topRightType, bottomRightType, bottomLeftType) = cornerTypes