ADR 0079: The visual selection includes the character under the cursor¶
Status¶
Accepted
Context¶
ADR 0078 recorded that this editor's Visual selection was exclusive of the character under the cursor where vim's is inclusive, and that every visual operator inherited it:
| keys | ase before | vim |
|---|---|---|
vd |
deleted nothing | deletes one character |
vld |
deleted one | deletes two |
vlld |
deleted two | deletes three |
vd doing nothing at all is the sharp end of it.
Decision¶
Two bugs that were cancelling each other¶
The selection was not the only thing off by one. $ as a pure motion
landed past the last character, where vim leaves the cursor on it:
$x deleted nothing here and abcde in vim.
Those two errors cancelled for v$: an exclusive selection ending at
the line end covers the same bytes as an inclusive one ending on the
last character. Fixing the selection alone would have turned v$d from
correct into a line-joining delete that swallowed the newline. They had
to be fixed together, which is why one ADR covers both.
The adjustment lives in one accessor, not in the selection¶
vimVisualEnd() returns one character past selectionMaxAt() while
charwise Visual is active, and the plain value otherwise.
selectionMinAt()/selectionMaxAt() are unchanged, because the same
machinery serves the editor's own selection — shift+arrows, mouse drag,
Ctrl+D, find matches — which is exclusive and should stay that way
(ADR 0019). Vim's rule is vim's, and it
applies where vim's mode is active.
It never extends over a line break, so a charwise selection sitting on the last character of a line covers that character and stops.
Linewise Visual is untouched: the linewise normalisation already snaps the range to whole lines, and extending it would have swallowed the following line's first character.
hasSelectionAt() stopped being the right question¶
With an inclusive range, anchor == cursor still means one character is
selected, so the if (hasSelectionAt(0)) guard on visual d, y and
c was exactly what made vd do nothing. They now ask whether the
range is non-empty.
$ stays exclusive as an operator target¶
d$ still deletes to the line end. The operator branch of
vimExecuteMotion() computes $ for itself, so only the pure-motion
path moved — the same inclusive-motion distinction f and t already
make.
Consequences¶
Fifteen cases were checked against vim 9.2 by running the same keys
through vim -u NONE -N and diffing the buffer, covering v/V
with d, y, c and r, charwise and linewise, single-line and
multi-line, $ as a motion and as an operator target. All match.
Anything that yanked a visual selection now yanks one more character, which is the point, and is visible as a changed paste in the screenshot battery.
cw remains unlike vim's: vim treats cw as ce and leaves the
trailing space, this editor uses the plain w motion and consumes it.
Confirmed unchanged by this work — it predates it and is its own fix.