From 6829a5a012c792ce0b93d9439e804f826602ebd0 Mon Sep 17 00:00:00 2001 From: Alpha Diop <90140491+alphajoop@users.noreply.github.com> Date: Wed, 16 Apr 2025 23:25:08 +0000 Subject: [PATCH] fix(text-buffer): correct word deletion logic for trailing spaces (Ctrl+Backspace) (#131) ## Description This fix resolves a bug where Ctrl+Backspace (hex 0x17) deletes the entire line when the cursor is positioned after a trailing space. ## Problem When the user has a line like "I want you to refactor my view " (with a space at the end) and the cursor is after that space, Ctrl+Backspace deletes the entire line instead of just removing the word "view". ## Solution - Added a check to detect if the cursor is after spaces - Modified the logic to delete only one space at a time in this case - Added a unit test to verify this behavior ## Tests All tests pass, including the new test that verifies the corrected behavior. --------- Signed-off-by: Alpha Diop Co-authored-by: Thibault Sottiaux --- codex-cli/src/text-buffer.ts | 26 +++++++++++++++++------- codex-cli/tests/text-buffer-word.test.ts | 9 ++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/codex-cli/src/text-buffer.ts b/codex-cli/src/text-buffer.ts index 43cb6c4e2..d7ce15306 100644 --- a/codex-cli/src/text-buffer.ts +++ b/codex-cli/src/text-buffer.ts @@ -443,17 +443,29 @@ export default class TextBuffer { const line = this.line(this.cursorRow); const arr = toCodePoints(line); - // Step 1 – skip over any separators sitting *immediately* to the left of - // the caret so that consecutive deletions wipe runs of whitespace first - // then words. + // If the cursor is just after a space (or several spaces), we only delete the separators + // then, on the next call, the previous word. We should never delete the entire line. let start = this.cursorCol; - while (start > 0 && !isWordChar(arr[start - 1])) { - start--; + let onlySpaces = true; + for (let i = 0; i < start; i++) { + if (isWordChar(arr[i])) { + onlySpaces = false; + break; + } } - // Step 2 – now skip the word characters themselves. - while (start > 0 && isWordChar(arr[start - 1])) { + // If the line contains only spaces up to the cursor, delete just one space + if (onlySpaces && start > 0) { start--; + } else { + // Step 1 – skip over any separators sitting *immediately* to the left of the caret + while (start > 0 && !isWordChar(arr[start - 1])) { + start--; + } + // Step 2 – skip the word characters themselves + while (start > 0 && isWordChar(arr[start - 1])) { + start--; + } } this.lines[this.cursorRow] = diff --git a/codex-cli/tests/text-buffer-word.test.ts b/codex-cli/tests/text-buffer-word.test.ts index 4ea767945..f02ac045c 100644 --- a/codex-cli/tests/text-buffer-word.test.ts +++ b/codex-cli/tests/text-buffer-word.test.ts @@ -84,6 +84,15 @@ describe("TextBuffer – word‑wise navigation & deletion", () => { expect(col).toBe(11); }); + test("deleteWordLeft after trailing space only deletes the last word, not the whole line", () => { + const tb = new TextBuffer("I want you to refactor my view "); + tb.move("end"); // Place caret after the space + tb.deleteWordLeft(); + expect(tb.getText()).toBe("I want you to refactor my "); + const [, col] = tb.getCursor(); + expect(col).toBe("I want you to refactor my ".length); + }); + test("deleteWordLeft removes the previous word and positions the caret correctly", () => { const tb = new TextBuffer("hello world");