Skip to content

ADR 0081: cw changes to the end of the word

Status

Accepted

Context

cw was dw with a different operator: it changed through to the start of the next word, eating the space between them. Vim leaves that space — cw on ab cd gives Z cd, not Zcd. It is the most-used operator pair in the editor, so the difference showed up constantly.

Decision

The special case is vim's, and it is narrow

:help cw states it: when the cursor is in a word, cw changes only up to the end of that word. On whitespace it stays a plain w. Both halves were checked rather than assumed, by forcing the cursor to column 1 with 0 first — vim's -es mode otherwise starts at the first non-blank and hides the whitespace case:

buffer cw ce
ab cd Z cd Z cd
ab cd Zab cd Z cd

It is not ce

The obvious implementation — treat cw as ce — is wrong, and the matrix says where. e steps on to the next word when it is already at the end of one, so on .ab it reaches the end of ab; both this editor and vim agree that ce there changes the whole line. But vim's cw changes only the ..

So "the end of the word" means the end of the character-class run the cursor is sitting in, which is what vimWordRunEnd() now returns. Only the first step of a count is special — c2w on ab cd ef gives Z ef, so the second step behaves like e.

Nothing else moves

dw, d2w and yw are untouched: the special case is gated on the pending operator being c. ce is untouched, including on punctuation, where it and cw now correctly disagree.

Consequences

Sixteen cases were checked against vim 9.2 by running the same keys through vim -u NONE -N and diffing the buffer — word, punctuation, one and two leading spaces, mid-word, last word on a line, at the line end, and counts up to three — plus the dw/yw/ce cases that had to stay put. All match.

vimClassifyAt() indexes the cache without a bounds check, so the new caller guards the offset itself. Worth knowing before the next caller.