ADR 0070: The jumplist¶
Status¶
Accepted
Context¶
Go-to-definition (ADR 0067) made the gap impossible to ignore: you can
follow a name into a file you did not choose, and the only way back is
the buffer bar. ADR 0068 cleared Ctrl+O and Ctrl+I for this.
Decision¶
The shape is the undo stack's¶
A list plus an index into it. Recording a jump truncates everything
after the index and appends — exactly the rule a new edit follows
against the redo stack. m_jumpIndex == m_jumps.size() means "at the
present", with nothing to go forward to.
It lives in MainWindow, not the viewport: jumps cross buffers, and the
buffer list is the window's. The same split that already routes search
hits and cross-file definitions through the window.
What counts as a jump¶
The one rule worth stating plainly: a jump is a movement you could not
have made with h j k l. Record too much and "back" takes you one
line up; record too little and the key does nothing when you need it.
Recorded: gg, G, :N, {, }, go-to-definition, following a
search hit, and opening a file (from the browser or Ctrl+P — the most
common way to end up somewhere you want back from).
Not recorded: h j k l w b e f t, scrolling, Ctrl+D/Ctrl+U.
Three subtleties the sites have to get right:
gg/Gwith an operator pending is a range, not a move —dGdeletes to the end of the file; it does not take you there. Those record nothing.- Search records once, when the bar opens, not on each incremental keystroke. Otherwise typing a six-character query buries the position you actually wanted under six entries.
- Go-to-definition records when the answer arrives, not when the request is sent, so a lookup that finds nothing leaves no phantom entry.
Going back records the present¶
The first Ctrl+O from the present pushes where you are before moving,
or forward has nowhere to return to and back becomes a one-way door.
Vim does the same. Subsequent presses don't re-push, because the index
is no longer at the end.
Consecutive entries on the same line of the same buffer are dropped:
without that, gd twice on one symbol records two identical positions
and the first Ctrl+O appears to do nothing.
Entries outlive their buffers¶
An entry carries both a path and the viewport's address. The address finds a buffer that is still open; the path reopens one that was closed, which is what vim does and what makes the history trustworthy over a long session.
An untitled buffer has no path, so a closed one cannot come back. That entry is dropped when it is reached, with a message, rather than left as a step that silently does nothing every time you pass it.
Two bindings again¶
Ctrl+O/Ctrl+I are vim's. Alt+Left/Alt+Right are what every
browser and IDE uses. The same "two audiences" split as gd/F12, and
two lines of code.
They are window shortcuts rather than viewport keys, like Ctrl+Tab and
Ctrl+W — the list spans buffers, so it belongs to the object that owns
them.
Consequences¶
Verified live:
Gto line 888,Ctrl+Oback to line 1,Ctrl+Iforward to 888.Ctrl+Popensmotion.h;Ctrl+Oreturns tomain.cppat line 888, the exact position, not the top of the file.- Pressing
Ctrl+Opast the beginning saysno earlier position;Ctrl+Ipast the end saysno later position— the message line (ADR 0062) again turning a dead key into an answer. - Closing
motion.hand pressingCtrl+Ireopens it and lands on the remembered line.
ctest 9/9, clean build, zero warnings.
Deliberately not recorded, and arguable: switching buffers by hand
(Ctrl+Tab, clicking a tab). Vim counts a buffer switch as a jump. Here
the tab strip is always visible and switching back is one click, so the
history stays about where you were reading rather than every tab you
have touched. Easy to change if it feels wrong in use.
Also not done: '' (back to the last position in this file), marks, and
showing the list — :jumps in vim. The list exists and nothing displays
it, which is fine until someone wants to know why back went where it
did.