[BUG] M-d / Alt+D (delete-word-forward) consumes trailing whitespace, diverging from expected behavior for readline / MacOS / Emacs
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
In the chat input, M-d or M-DEL (or any terminal binding that sends ESC d, e.g. iTerm2's default cmd+D mapping) deletes the next word PLUS the whitespace following it.
What Should Happen?
GNU readline and Emacs kill-word both stop at the end of the word and leave trailing whitespace intact. The current Claude Code behavior matches vi's dw, not the readline/Emacs convention that the rest of the input bindings follow.
Verified against:
- GNU readline (bash prompt): preserves the trailing space.
- Emacs kill-word (M-d): preserves the trailing space.
- zsh ZLE kill-word: preserves the trailing space.
Documentation indicates use of GNU-readline semantics (see shortcut table at https://code.claude.com/docs/en/interactive-mode). In addition, issues such as #49317 (closed) apply fixes to conform to GNU readline standards.
(Current behavior resembles vi dw - suggesting that someone could conceivably prefer the current behavior.)
Error Messages/Logs
n/a
Steps to Reproduce
- In the Claude Code prompt, type:
fooBar baz - Move cursor to the
'B'in fooBar - Press M-d (or cmd+D in iTerm2 with the default "Send Escape Sequence: d" mapping).
Expected: foo baz — single space preserved between foo and baz.
Actual: foobaz — no space.
Claude Model
None
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.140
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
Root cause
Claude Code's input layer uses Node.js readline. The Node implementation of readlof _deleteWordRight in lib/internal/readline/interface.js uses this regex:
const match = trailing.match(/^(?:\s+|\W+|\w+)\s*/);
The trailing \s* greedily consumes whitespace after the killed token. Confirmed by extracting strings from the bundled CC binary (2.1.140):
[kDeleteWordRight]() {
if (this.cursor < this.line.length) {
this[kBeforeEdit](this.line, this.cursor
var trailing = StringPrototypeSlice.call(this.line, this.cursor),
match = RegExpPrototypeExec.call(/^(;
this.line = StringPrototypeSlice.call(this.line, 0, this.cursor) +
StringPrototypeSlice.call(tr
this[kRefreshLine]();
}
}
This appears to be a long-standing Node divergence from GNU readline — unlikely to be fixed upstream because it would break
compatibility for everyone relying on the cu to live in Claude Code.
Proposed fix
Override _deleteWordRight (or the equivalent with the trailing \s* removed:
const match = trailing.match(/^(?:\s+|\W+|\w
Optional enhancement: expose this as a confit.wordDeleteStyle: "emacs" | "vi", default"emacs") so vi-style users can opt back in. Could also be exposed as a bindable chat:deleteWordForward action in keybindings.json, matching the pattern requeeToLineStart.
Impact
Hits any user re-editing text mid-sentence in a Claude chat input.
Related
- #27561 — Modern text input (broader rework
- #51694 — Request to expose chat:deleteToLineStart as a bindable action; same shape of fix
- #49317 — Prior precedent of fixing readlin bindings
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗