ADR 0098: Global marks reuse the jumplist's entry¶
Status¶
Accepted
Context¶
ADR 0097 added marks and stored them as a
buffer-local (line, column). It accepted A-Z but treated them like
a-z, and said what was missing: "a global mark has to name a file as
well as a position, which is the jumplist's problem too and should be
solved once for both."
This is that, and the prediction held — the thing a global mark needs to
store is exactly MainWindow::JumpEntry: a path, a buffer id for an
untitled buffer that has no path, a line and a column.
Decision¶
The window owns them, because only the window knows about files¶
m_globalMarks is a QHash<char, JumpEntry> on MainWindow. The
viewport cannot own them: the answer to `A may be a buffer that is
not the current one, or not open at all.
Setting and jumping go out as signals — globalMarkSetRequested,
globalMarkJumpRequested — the way fileOpenRequested and
jumpRecorded already do. The window fills in the file identity, since
that is the part it has.
Jumping reuses restoreJump() whole: it already switches to the buffer
if open, reopens it by path if it was closed, and positions the
cursor. It gained one parameter, exact, because ` wants the
column and ' wants the line's first non-blank. A global mark jump
records a jumplist entry first, so Ctrl+O comes back across the file
change.
An uppercase mark is stored locally as well¶
Not redundancy — it is what makes d'A work. vim's rule, confirmed by
running it:
| vim | ase | |
|---|---|---|
d'A, mark in this buffer |
deletes the range | same |
d'A, mark in another file |
E20: Mark not set, no change |
mark A not set, no change |
`A, mark in another file |
switches file, line 2 | same |
An operator needs offsets in the buffer it is operating on, and a position in another file has none. So the local copy answers operators, the global one answers jumps, and the failure case is an error rather than something surprising happening in the wrong file.
One letter, one place¶
The cost of that second copy is that it can go stale: set mA in one
buffer, set mA in another, and the first buffer would still answer
d'A from a copy of a mark that has moved.
So setting a global mark clears that letter from every other buffer's
local marks. Verified: after re-claiming A in the second buffer,
d'A in the first reports "mark A not set" and changes nothing.
Consequences¶
Global marks live as long as the window and no longer. vim persists them in a viminfo file across sessions; there is no such file here, and adding one is a larger decision about what state the editor keeps on disk than this ADR should make.
A mark in a buffer that was closed still works if the buffer had a path,
because restoreJump reopens it — the same behaviour Ctrl+O has. A
mark in a closed untitled buffer is gone, and says so.
This work turned up a bug it did not cause: l moves past the end of a
line and wraps onto the next one, where vim stops at the last character
(12l from column 9 of a 19-column line gives column 19 in vim, and
line 3 column 1 here). Found because a test set a mark with 12l and
the mark landed on the wrong line. Fixed separately — the point worth
recording is that it had been there since l was written and surfaced
only when something else depended on where the cursor actually was.