ADR 0076: Replacing a character (r)¶
Status¶
Accepted
Context¶
r{char} was the last common single-key change missing from Normal
mode. It is also the cheapest one to add correctly, because the
machinery it needs already exists: f/F/t/T already wait for a
target character (ADR 0069),
and . already repeats a change (ADR 0075).
Decision¶
Verified against vim rather than from memory¶
vim is installed on the development machine, so every rule below was
checked by running the same keys through vim -u NONE -N and diffing
the resulting buffer, rather than recalled:
| keys | buffer | result |
|---|---|---|
rX |
abcdef |
Xbcdef |
3rX |
abcdef |
XXXdef, cursor on the last replaced character |
9rX |
abcdef |
unchanged |
4l2rX |
abcdef |
abcdXX |
4l3rX |
abcdef |
unchanged |
3r<CR> |
abcdef |
one empty line, then def |
rX |
empty line | unchanged |
All eight cases, including a . repeat, now match vim byte for byte.
A count that does not fit does nothing¶
3rX with two characters left on the line replaces nothing at all — not
the two that are there. That is vim's rule and it is the right one: a
partial replace is a silent wrong answer, and a no-op is at least
visible as nothing happening.
r writes no register¶
It is not a delete. Clobbering the unnamed register with one character
would make a following p paste that character instead of whatever was
last yanked, which is not what anyone means by r.
Enter is detected by key code, not by text¶
r<CR> replaces the count characters with a single line break. The
newline case cannot be recognised from event->text(): Return arrives
as "\r" under X11 but as U+0000 under the offscreen platform plugin,
which produced three NUL bytes in the buffer the first time this was
tried. The key code is the same everywhere, so that is what it branches
on. Any other non-printable target cancels, as it does in vim.
This is worth remembering beyond r — any future key that needs to tell
Return, Tab or Escape apart from typed text has the same problem.
Consequences¶
r is repeatable for free: vimMarkChange() at the mutation is all it
needed, exactly as ADR 0075
predicted. rZ then . on the next line works without a line of code
specific to it.
R (replace mode, overtyping until Escape) is not implemented. It is a
mode rather than a single change, so it needs its own insert-session
handling rather than another call to this.
Visual-mode r, which replaces every character in the selection, is
also not implemented — the same gap Visual-mode operators have in
ADR 0075.
r followed by a key that carries no text — an arrow, say — leaves the
buffer untouched and does not eat the key after it, which is what vim
does. Verified both ways: r<Right>X and r<Right>j each leave the
buffer exactly as vim leaves it. The mechanism by which the pending
state is dropped on that path was not traced, so treat it as observed
behaviour rather than as a guarantee the code states anywhere.