Repository navigation
Selection not rendered on RTL line when it ends at two empty end-of-line decoration spans #341563
Description
Does this issue occur when all extensions are disabled?: No. It needs an extension that adds end-of-line decorations (here GitLens current-line blame), but the bug is in core rendering: RangeUtil.readHorizontalRanges returns null for a DOM shape that the public decorations API produces.
- VS Code Version: 1.141.1
- OS Version: Windows 11 Pro 10.0.26200
Steps to Reproduce:
- Install GitLens (current-line blame is on by default).
- Open a file in a git repo containing a line that has RTL text, e.g.
Text: =ThisItem.'שם פרטי'. - Place the cursor on that line so the blame annotation appears at its end.
- Select from the middle of the line to its end (e.g. Shift+End), or make a multi-line selection that spans the line.
Expected: the selection background is rendered on that line.
Actual: no selection background on that line. Lines without RTL text are fine.
Cause
The blame decoration renders as two empty spans at the end of the line, one from beforeContentClassName and one from afterContentClassName:
<span class="mtk13" style="unicode-bidi:isolate">=ThisItem.'שם פרטי'</span>
<span class="ced-1-TextEditorDecorationType1-...-3"></span>
<span class="ced-1-TextEditorDecorationType1-...-4"></span> <!-- ::after has the blame text -->Because the line contains RTL text, ViewLine skips its LTR fast paths and calls RangeUtil.readHorizontalRanges. The end-of-line column maps to the second empty span, offset 0 (the first isPseudoAfter() part). Then:
- rangeUtil.ts#L126-L131 moves the end to the previous span with offset
MAX_SAFE_SMALL_INTEGER, but that span is the other empty one. - The empty-span fallback at rangeUtil.ts#L142 only runs when
endOffset === 0, so it is skipped. endElementisnull, so the function returnsnull, and no visible range is produced for the line.
With a single trailing empty span this works, because the step back lands on a span with text.
Possible fix
I tested this fix locally. With it, the selection renders correctly; without it, it doesn't. Unit tests with two trailing empty spans fail before the change and pass after it. I'm happy to open a PR if that's useful.
Diff
+ private static _findPreviousTextNode(domNode: HTMLElement, childIndex: number): ChildNode | null {
+ for (let i = childIndex - 1; i >= 0; i--) {
+ const textNode = domNode.children[i].firstChild;
+ if (textNode) {
+ return textNode;
+ }
+ }
+ return null;
+ }
...
if (!startElement || !endElement) {
- // When having an empty <span> (without any text content), try to move to the previous <span>
- if (!startElement && startOffset === 0 && startChildIndex > 0) {
- startElement = domNode.children[startChildIndex - 1].firstChild;
+ // When having empty <span>s (without any text content), try to move to the end of the closest previous <span> with text.
+ // The end of an empty <span> is the same as its beginning, so this also applies when the end was moved above to an empty <span>.
+ if (!startElement && startOffset === 0) {
+ startElement = this._findPreviousTextNode(domNode, startChildIndex);
startOffset = Constants.MAX_SAFE_SMALL_INTEGER;
}
- if (!endElement && endOffset === 0 && endChildIndex > 0) {
- endElement = domNode.children[endChildIndex - 1].firstChild;
+ if (!endElement && (endOffset === 0 || endOffset === Constants.MAX_SAFE_SMALL_INTEGER)) {
+ endElement = this._findPreviousTextNode(domNode, endChildIndex);
endOffset = Constants.MAX_SAFE_SMALL_INTEGER;
}
}
Activity
vs-code-engineering commentedon Oct 14, 2026
Thanks for creating this issue! It looks like you may be using an old version of VS Code; the latest stable release is 1.141.2. Please try upgrading to the latest version and checking whether this issue remains.
Happy Coding!
Fix selection not rendered when line ends with multiple empty spans