vim mode: support for c* (change) operations
Open 💬 3 comments Opened Jul 13, 2026 by NikoPit
What variant of Codex are you using?
CLI
What feature would you like to see?
Support for c* (e.g: c$ ce cw) operations in codex cli vim mode.
Additional information
_No response_
3 Comments
Did some digging into why
c+ motion does nothing whileciw/cawwork fine.In
codex-rs/tui/src/bottom_pane/textarea.rs, the change operator is already wired as an operator-pending state (VimPending::Operator(VimOperator::Change)), and text objects work becausehandle_vim_text_objectroutes throughapply_vim_operator_to_range, which kills the range and enters insert mode.Motions, however, are explicitly excluded for
Change, in two places:So
c$/ce/cwresolve a motion, hit the guard, and are consumed as no-ops. There's a test locking this in (vim_text_object_cancellation_and_unsupported_change_motions_do_not_edit), so it looks like a deliberate punt rather than an oversight.Outline of a fix:
op != VimOperator::Changeguard and haveapply_vim_operatordelegate toapply_vim_operator_to_range(this also dedupes the two match blocks; change = kill range + enter insert).c$,c0,ce,cb,ch,cl. For example,ceinheritsde's range viarange_for_motion(VimMotion::WordEnd), which already matches vim's inclusive word-end semantics.cwneeds vim's special case: on a non-blank character it behaves likece, meaning it must not eat the whitespace after the word, and on the last character of a word it changes just that character (unlikece, which would extend to the next word's end). The existingsmall_word_range_at_cursor()(used byciw) already computes the right word piece;cursor..piece.endcovers the cursor-inside-a-word case, with a fallback to the plainWordForwardrange when the cursor sits on whitespace.cc,cj,ck) needs different semantics (kill the lines, leave one empty line, enter insert), andccwould need achange_lineentry inVimOperatorKeymapfollowing thedd/yypattern (config field + defaults + keymap-setup action +just write-config-schema). That could be a follow-up to keep the first change small.The test above would flip to asserting the new behavior, plus new cases for
cwmid-word, on the last char of a word, and on whitespace.Happy to put together a PR along these lines if the team wants to invite one.
I implemented the analysis above as a focused, one-commit reference patch, including the
cwspecial case, linewise change, and counts:Behavior covered:
cwuses Vim’s change-to-word-end semantics instead of consuming the following whitespace.ccchanges the current line while leaving an editable empty line.ch,cl,cj,ck,cw,cb,ce,c0, andc$.ciwandcawbehavior remains intact.2cw,2dd,2cc,d2w,2d3w,2ciw,d2aw, and2w2x.change_lineis exposed through the configurable Vim operator keymap and the generated config schema.I intentionally split this away from the startup-mode and undo/redo work so it is independently reviewable. The patch passed all 255
codex-configtests and all 3,524codex-tuitests, plus scoped lint and formatting.Since unsolicited PRs are closed without review, please treat this as a tested reference implementation. Would a maintainer like to pick it up internally or explicitly invite the focused PR?
@fcoury-oai — since you handled the earlier Vim composer work (#18595, #24380, #24382), I’d appreciate your take on this implementation. I split the work into three independently tested, one-commit branches because external PRs are invitation-only. If any align with the intended direction, I’m happy to open whichever PR(s) you explicitly invite.