fix(tui): handle multi-line text in insertTextAtCursor()

Add insertTextAtCursorInternal() to properly handle newlines by splitting
lines at the cursor position.

- Normalize line endings (CRLF/CR to LF)
- Handle single-line and multi-line insertion
- Position cursor at end of inserted text
- Call onChange only once at the end

Refactor handlePaste() to use insertTextAtCursorInternal() for
multi-line pastes, while retaining character-by-character insertion for
single-line pastes to preserve autocomplete triggering.
This commit is contained in:
Sviatoslav Abakumov 2026-01-25 16:28:06 +04:00
parent 3635e45ffd
commit 7d2d170c1b
No known key found for this signature in database
2 changed files with 101 additions and 50 deletions

View file

@ -1466,6 +1466,44 @@ describe("Editor component", () => {
assert.strictEqual(editor.getText(), "hello| world");
});
it("insertTextAtCursor handles multiline text", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);
editor.setText("hello world");
editor.handleInput("\x01"); // Ctrl+A - go to start
for (let i = 0; i < 5; i++) editor.handleInput("\x1b[C"); // Move right 5 (after "hello", before space)
// Insert multiline text
editor.insertTextAtCursor("line1\nline2\nline3");
assert.strictEqual(editor.getText(), "helloline1\nline2\nline3 world");
// Cursor should be at end of inserted text (after "line3", before " world")
const cursor = editor.getCursor();
assert.strictEqual(cursor.line, 2);
assert.strictEqual(cursor.col, 5); // "line3".length
// Single undo should restore entire pre-insert state
editor.handleInput("\x1b[45;5u"); // Ctrl+- (undo)
assert.strictEqual(editor.getText(), "hello world");
});
it("insertTextAtCursor normalizes CRLF and CR line endings", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);
editor.setText("");
// Insert text with CRLF
editor.insertTextAtCursor("a\r\nb\r\nc");
assert.strictEqual(editor.getText(), "a\nb\nc");
editor.handleInput("\x1b[45;5u"); // Undo
assert.strictEqual(editor.getText(), "");
// Insert text with CR only
editor.insertTextAtCursor("x\ry\rz");
assert.strictEqual(editor.getText(), "x\ny\nz");
});
it("undoes setText to empty string", () => {
const editor = new Editor(createTestTUI(), defaultEditorTheme);