import { assert, assertEquals } from "@std/assert"; import { undo } from "@codemirror/commands"; import { getChunks, getOriginalDoc } from "@codemirror/merge"; import { withDom } from "@berry/util/dom.ts"; import type { OpenFile } from "./edit-types.ts"; import { diffEditor } from "./editor.ts"; Deno.test("diff layout changes retain multiline edits, selection, and undo without serializing removed lines", () => withDom(() => { const parent = document.createElement("div"); document.body.append(parent); const opened: OpenFile = { working: { machine: "local", root: "/repo", vcs: "jj", identity: "change" }, file: { path: "a.txt", from: null, status: "modified", binary: false, modes: { old: "100644", new: "100644" }, hunks: [], }, original: "first\r\nremoved\r\nend", content: "first\r\nnew\r\nend", version: "version", }; let latest = opened.content; const editor = diffEditor(parent, opened, "unified", { changed: (content) => latest = content, save() {}, escape() {}, }); try { const start = editor.view.state.doc.line(2).from; editor.view.dispatch({ changes: { from: start, to: start + 3, insert: "edited\nextra" }, selection: { anchor: start + 6 }, }); assertEquals(latest, "first\r\nedited\r\nextra\r\nend"); const selection = editor.view.state.selection.main; editor.layout("split"); assertEquals(editor.content(), latest); assertEquals(editor.view.state.selection.main, selection); assertEquals( parent.querySelector('[aria-label="Original a.txt"]')!.getAttribute("contenteditable"), "false", ); editor.layout("unified"); assertEquals(editor.view.state.selection.main, selection); assertEquals(getOriginalDoc(editor.view.state).toString(), "first\nremoved\nend"); undo(editor.view); assertEquals(editor.content(), opened.content); } finally { editor.destroy(); } })); Deno.test("sparse edits in long files leave unchanged code out of the editor's diff", () => withDom(() => { const parent = document.createElement("div"); document.body.append(parent); const unchanged = Array.from( { length: 500 }, (_, i) => `function kept${i}() { return value + ${i}; }\n`, ).join(""); const opened: OpenFile = { working: { machine: "local", root: "/repo", vcs: "jj", identity: "change" }, file: { path: "long.ts", from: null, status: "modified", binary: false, modes: { old: "100644", new: "100644" }, hunks: [], }, original: `function unused() { return 0; }\n\n${unchanged}export const label = "old";\n`, content: `${unchanged}export const label = "new";\n`, version: "version", }; const editor = diffEditor(parent, opened, "unified", { changed() {}, save() {}, escape() {}, }); const check = () => { const oldStart = opened.original.indexOf(unchanged); const newStart = editor.content().indexOf(unchanged); assert(newStart >= 0); const chunks = getChunks(editor.view.state)!.chunks; assert(chunks.length > 0); for (const chunk of chunks) { assert(chunk.toA <= oldStart || chunk.fromA >= oldStart + unchanged.length); assert(chunk.toB <= newStart || chunk.fromB >= newStart + unchanged.length); } }; try { check(); editor.layout("split"); check(); const from = editor.view.state.doc.toString().lastIndexOf('"new"'); editor.view.dispatch({ changes: { from, to: from + 5, insert: '"edited"' } }); check(); assertEquals(editor.content(), `${unchanged}export const label = "edited";\n`); editor.layout("unified"); check(); } finally { editor.destroy(); } }));