From da90af78a1d0706f17391027953ae10c36863ebe Mon Sep 17 00:00:00 2001 From: Je Xia Date: Mon, 20 Jul 2026 23:43:18 +0800 Subject: [PATCH] [diffs/edit] Clamp selection rendering to render window --- packages/diffs/src/editor/editor.ts | 9 ++++- .../diffs/test/editorVirtualizedEdit.test.ts | 38 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/diffs/src/editor/editor.ts b/packages/diffs/src/editor/editor.ts index 20e3cbd1c..034eaa8df 100644 --- a/packages/diffs/src/editor/editor.ts +++ b/packages/diffs/src/editor/editor.ts @@ -3737,7 +3737,14 @@ export class Editor implements DiffsEditor { } const { start, end } = range; - for (let line = start.line; line <= end.line; line++) { + let firstLine = start.line; + let endLine = end.line + 1; + if (this.#renderRange !== undefined) { + const { startingLine, totalLines } = this.#renderRange; + firstLine = Math.max(firstLine, startingLine); + endLine = Math.min(endLine, startingLine + totalLines); + } + for (let line = firstLine; line < endLine; line++) { if (!this.#isLineVisible(line)) { continue; } diff --git a/packages/diffs/test/editorVirtualizedEdit.test.ts b/packages/diffs/test/editorVirtualizedEdit.test.ts index 532ad57e5..56fa8361f 100644 --- a/packages/diffs/test/editorVirtualizedEdit.test.ts +++ b/packages/diffs/test/editorVirtualizedEdit.test.ts @@ -1,8 +1,9 @@ -import { afterAll, describe, expect, test } from 'bun:test'; +import { afterAll, describe, expect, spyOn, test } from 'bun:test'; import { File } from '../src/components/File'; import { DEFAULT_THEMES } from '../src/constants'; import { Editor } from '../src/editor/editor'; +import { DirectionForward } from '../src/editor/selection'; import { disposeHighlighter } from '../src/highlighter/shared_highlighter'; import type { FileContents, RenderRange } from '../src/types'; import { installDom, wait } from './domHarness'; @@ -304,3 +305,38 @@ describe('Editor edits at the bottom of a virtualized window', () => { } }); }); + +describe('Editor selections in a virtualized window', () => { + test('limits a document-spanning selection to the rendered lines', async () => { + const range = makeRange(4900, 7); + const { cleanup, content, editor, fileContainer } = + await createWindowedEditor(10_000, range); + editor.setOptions({ roundedSelection: false }); + + const querySelector = spyOn(content, 'querySelector'); + try { + editor.setState({ + selections: [ + { + start: { line: 0, character: 0 }, + end: { line: 9999, character: 10 }, + direction: DirectionForward, + }, + ], + view: { scrollLeft: 0, scrollTop: 0 }, + }); + + expect( + querySelector.mock.calls + .map(([selector]) => /^\[data-line="(\d+)"\]/.exec(selector)?.[1]) + .filter((line) => line !== undefined) + ).toEqual(['10000']); + expect( + fileContainer.shadowRoot?.querySelectorAll('[data-selection-range]') + ).toHaveLength(range.totalLines); + } finally { + querySelector.mockRestore(); + cleanup(); + } + }); +});