Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/diffs/src/editor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3214,7 +3214,11 @@ export class Editor<LAnnotation> implements DiffsEditor<LAnnotation> {
tokenizer.stopBackgroundTokenize();

const t = performance.now();
const dirtyLines = tokenizer.tokenize(change, renderRange, !this.#isDiff);
const dirtyLines = tokenizer.tokenize(
change,
renderRange,
!this.#isDiff && fileInstance.getEditorViewport !== undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don’t use the viewport hook as a row-realignment signal

This predicate treats any non-diff component that defines getEditorViewport as a host that will realign shifted rows, but that hook is also valid on a plain File just to bound editor focus/scrolling (see packages/diffs/test/editorPublicApi.test.ts:125-130). In that setup, inserting a newline after cached grammar states takes the shifted-state fast path again, so tokenize() can settle after only the split lines while File.applyDocumentChange() does not rerender/realign the existing DOM rows; the following row can still be left missing or stale, which is the same failure this patch is trying to avoid. Use a capability tied to virtualization/row realignment rather than the viewport callback.

Useful? React with 👍 / 👎.

);
const t2 = performance.now();

if (dirtyLines.size > 0) {
Expand Down
43 changes: 43 additions & 0 deletions packages/diffs/test/editorApplyEdits.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ async function waitForEditableContent(
}

interface EditorTestWindow extends Window {
InputEvent: {
new (type: string, eventInitDict?: InputEventInit): InputEvent;
};
KeyboardEvent: {
new (type: string, eventInitDict?: KeyboardEventInit): KeyboardEvent;
};
Expand Down Expand Up @@ -1209,6 +1212,46 @@ describe('Editor move line commands', () => {
});

describe('Editor editing commands', () => {
test('keeps following rows rendered after Enter without a virtualizer', async () => {
const { cleanup, content, editor, window } = await createEditorFixture(
'alpha\nbravo\ncharlie'
);

try {
// Seed cached grammar states, as they are after initial tokenization, so
// Enter can stop tokenizing when the unchanged syntax state reconverges.
editor.applyEdits([
{
range: {
start: { line: 0, character: 0 },
end: { line: 0, character: 5 },
},
newText: 'ALPHA',
},
]);
editor.setState({ selections: [caret(1, 3)] });
content.dispatchEvent(
new window.InputEvent('beforeinput', {
bubbles: true,
cancelable: true,
composed: true,
inputType: 'insertParagraph',
})
);

expect(editor.getText()).toBe('ALPHA\nbra\nvo\ncharlie');
expect(
Array.from(
content.querySelectorAll<HTMLElement>('[data-line]'),
(row) => row.textContent
)
).toEqual(['ALPHA', 'bra', 'vo', 'charlie']);
expect(editor.getState().selections).toEqual([caret(2, 0)]);
} finally {
cleanup();
}
});

test('deletes to the end of the line with macOS control+k', async () => {
const { cleanup, content, editor, window } =
await createEditorFixture('hello world\nnext');
Expand Down