fix(tui): keep paste registry in sync when deleting paste markers

Undo snapshots now restore paste content and counters alongside editor text. Paste marker renumbering shifts registry entries in ascending ID order before rewriting markers, preventing literal or incorrect paste content on submit.\n\nCloses #6844
This commit is contained in:
Mario Zechner
2026-07-20 14:03:15 +02:00
parent bb437b097b
commit 3595e080cb
3 changed files with 104 additions and 12 deletions
+75
View File
@@ -3553,6 +3553,11 @@ describe("Editor component", () => {
return editor.getText();
}
/** Helper: 12-line paste content with a distinguishing tag */
function bigPaste(tag: string): string {
return Array.from({ length: 12 }, (_, i) => `${tag}${i}`).join("\n");
}
it("creates a paste marker for large pastes", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);
const text = pasteWithMarker(editor);
@@ -3690,6 +3695,76 @@ describe("Editor component", () => {
assert.strictEqual(editor.getText(), textBefore);
});
it("undo after paste marker deletion restores the paste registry", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);
let submitted = "";
editor.onSubmit = (t) => {
submitted = t;
};
const paste = bigPaste("alpha");
editor.handleInput(`\x1b[200~${paste}\x1b[201~`);
editor.handleInput("\x7f"); // delete the marker
editor.handleInput("\x1b[45;5u"); // undo: restores marker text and registry
editor.handleInput("\r");
assert.strictEqual(submitted, paste);
});
it("undo after deleting the first of two paste markers restores both registry entries", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);
let submitted = "";
editor.onSubmit = (t) => {
submitted = t;
};
const pasteA = bigPaste("alpha");
const pasteB = bigPaste("beta");
editor.handleInput(`\x1b[200~${pasteA}\x1b[201~`); // #1 = A
editor.handleInput(`\x1b[200~${pasteB}\x1b[201~`); // #2 = B, cursor at end
editor.handleInput("\x01"); // Ctrl+A
editor.handleInput("\x1b[C"); // right over marker #1
editor.handleInput("\x7f"); // delete marker #1, renumbers #2 -> #1
editor.handleInput("\x1b[45;5u"); // undo
editor.handleInput("\r");
assert.strictEqual(submitted, pasteA + pasteB);
});
it("renumbers the paste registry in ascending id order when markers are out of order in text", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);
let submitted = "";
editor.onSubmit = (t) => {
submitted = t;
};
const pasteA = bigPaste("alpha");
const pasteB = bigPaste("beta");
const pasteC = bigPaste("gamma");
editor.handleInput(`\x1b[200~${pasteA}\x1b[201~`); // #1 = A
editor.handleInput("\x01"); // Ctrl+A
editor.handleInput(`\x1b[200~${pasteB}\x1b[201~`); // #2 = B, text: [#2][#1]
editor.handleInput("\x01"); // Ctrl+A
editor.handleInput(`\x1b[200~${pasteC}\x1b[201~`); // #3 = C, text: [#3][#2][#1]
editor.handleInput("\x05"); // Ctrl+E
editor.handleInput("\x7f"); // delete marker #1, renumber #3 -> #2 and #2 -> #1
editor.handleInput("\r");
assert.strictEqual(submitted, pasteC + pasteB);
});
it("undo after setText restores paste markers and registry", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);
let submitted = "";
editor.onSubmit = (t) => {
submitted = t;
};
const paste = bigPaste("alpha");
editor.handleInput(`\x1b[200~${paste}\x1b[201~`);
editor.setText("replacement");
editor.handleInput("\x1b[45;5u"); // undo
editor.handleInput("\r");
assert.strictEqual(submitted, paste);
});
it("handles multiple paste markers in same line", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);
pasteWithMarker(editor);