char-slop/berry

multipurpose self-modifiable agentic harness

git clone https://git.t4t.associates/char-slop/berry

Charlotte Somdiff: preserve sparse edit boundaries in long files2396910

main
3.8 KiB108 linesraw
1import { assert, assertEquals } from "@std/assert";
2import { undo } from "@codemirror/commands";
3import { getChunks, getOriginalDoc } from "@codemirror/merge";
4import { withDom } from "@berry/util/dom.ts";
5import type { OpenFile } from "./edit-types.ts";
6import { diffEditor } from "./editor.ts";
7
8Deno.test("diff layout changes retain multiline edits, selection, and undo without serializing removed lines", () =>
9  withDom(() => {
10    const parent = document.createElement("div");
11    document.body.append(parent);
12    const opened: OpenFile = {
13      working: { machine: "local", root: "/repo", vcs: "jj", identity: "change" },
14      file: {
15        path: "a.txt",
16        from: null,
17        status: "modified",
18        binary: false,
19        modes: { old: "100644", new: "100644" },
20        hunks: [],
21      },
22      original: "first\r\nremoved\r\nend",
23      content: "first\r\nnew\r\nend",
24      version: "version",
25    };
26    let latest = opened.content;
27    const editor = diffEditor(parent, opened, "unified", {
28      changed: (content) => latest = content,
29      save() {},
30      escape() {},
31    });
32    try {
33      const start = editor.view.state.doc.line(2).from;
34      editor.view.dispatch({
35        changes: { from: start, to: start + 3, insert: "edited\nextra" },
36        selection: { anchor: start + 6 },
37      });
38      assertEquals(latest, "first\r\nedited\r\nextra\r\nend");
39      const selection = editor.view.state.selection.main;
40      editor.layout("split");
41      assertEquals(editor.content(), latest);
42      assertEquals(editor.view.state.selection.main, selection);
43      assertEquals(
44        parent.querySelector('[aria-label="Original a.txt"]')!.getAttribute("contenteditable"),
45        "false",
46      );
47      editor.layout("unified");
48      assertEquals(editor.view.state.selection.main, selection);
49      assertEquals(getOriginalDoc(editor.view.state).toString(), "first\nremoved\nend");
50      undo(editor.view);
51      assertEquals(editor.content(), opened.content);
52    } finally {
53      editor.destroy();
54    }
55  }));
56
57Deno.test("sparse edits in long files leave unchanged code out of the editor's diff", () =>
58  withDom(() => {
59    const parent = document.createElement("div");
60    document.body.append(parent);
61    const unchanged = Array.from(
62      { length: 500 },
63      (_, i) => `function kept${i}() { return value + ${i}; }\n`,
64    ).join("");
65    const opened: OpenFile = {
66      working: { machine: "local", root: "/repo", vcs: "jj", identity: "change" },
67      file: {
68        path: "long.ts",
69        from: null,
70        status: "modified",
71        binary: false,
72        modes: { old: "100644", new: "100644" },
73        hunks: [],
74      },
75      original: `function unused() { return 0; }\n\n${unchanged}export const label = "old";\n`,
76      content: `${unchanged}export const label = "new";\n`,
77      version: "version",
78    };
79    const editor = diffEditor(parent, opened, "unified", {
80      changed() {},
81      save() {},
82      escape() {},
83    });
84    const check = () => {
85      const oldStart = opened.original.indexOf(unchanged);
86      const newStart = editor.content().indexOf(unchanged);
87      assert(newStart >= 0);
88      const chunks = getChunks(editor.view.state)!.chunks;
89      assert(chunks.length > 0);
90      for (const chunk of chunks) {
91        assert(chunk.toA <= oldStart || chunk.fromA >= oldStart + unchanged.length);
92        assert(chunk.toB <= newStart || chunk.fromB >= newStart + unchanged.length);
93      }
94    };
95    try {
96      check();
97      editor.layout("split");
98      check();
99      const from = editor.view.state.doc.toString().lastIndexOf('"new"');
100      editor.view.dispatch({ changes: { from, to: from + 5, insert: '"edited"' } });
101      check();
102      assertEquals(editor.content(), `${unchanged}export const label = "edited";\n`);
103      editor.layout("unified");
104      check();
105    } finally {
106      editor.destroy();
107    }
108  }));