ADR 0105: Named registers, and a test that was lying¶
Status¶
Accepted
Context¶
There was one register. " was not handled at all, which made it worse
than missing: typing "ayy left " doing nothing, then a entered
Insert mode and yy was typed into the file. A command that does not
exist yet should do nothing, not edit the buffer.
before: foo.bar baz
"ayyjP: fyyjPoo.bar baz
That is the only defect found in this work that loses the user's text.
Decision¶
"x names a register for the next yank, delete or paste¶
Registers a–z, with an uppercase name appending to the same slot.
Verified against vim, same file and keys:
| keys | |
|---|---|
"ayy … "ap |
yank to a, paste from a |
"add … "ap |
delete to a |
"ayy … "Ayy … "ap |
uppercase appends |
"ayy … p |
a named yank also fills the unnamed register |
"ayw … "aP |
charwise, pasted before |
"ayy … "byy … "ap"bp |
two registers side by side |
The name has to outlive the pending-state reset¶
vimApplyPendingOperatorCharwise and its linewise twin read the operator
and then call resetVimPendingState() before doing the work, so a
register named on the pending state would be gone by the time the yank
ran. They park it in m_vimRegisterInUse first; everything else still
has m_vimPendingRegister live when it needs it. Either way
vimTakeRegister() consumes it exactly once, so a name never leaks into
the following command.
" is recognised above the guard in vimApplyNormalKey that abandons a
pending operator — the fourth feature to need that, after marks, d'a
and text objects.
The suite found a bug in the suite¶
"aVy then "ap pasted in our editor and did nothing in vim, and the
editor was right: vim consumes the register prefix on the next
command, even a mode change, so "aV wastes it and y goes to the
unnamed register. Ours agreed.
The failure was that registers are process-wide statics and the test runs
every case in one process, so an earlier case's "ayy was still sitting
in a. vim starts each case in a fresh process. VimRegister::clearAll()
now runs per case.
That mattered more than the case that exposed it: any register- dependent expectation could have passed or failed for the wrong reason. A suite that shares state between cases is not measuring what it says.
Consequences¶
120 cases now, 33 ms.
Macros and registers remain separate stores. In vim they are the same
thing — qa records into register a, and "ap pastes the recording as
text. Here macros hold RecordedKey structs (key code, modifiers, text)
because ADR 0097 needed Escape and
Backspace, which carry no text and would be lost by a text register.
Unifying them means encoding keys as text the way vim does, and that is
its own change.
Numbered registers ("0–"9), the small-delete register "-, the
black-hole "_ and the clipboard registers "+/"* are not
implemented. "+ is the notable absence: yank still mirrors to the
system clipboard unconditionally, which covers the common case by
accident rather than by asking.