From be404e0d4f91548af6ed600b2fd2027ad8a041c7 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Fri, 31 Jul 2026 01:47:40 -0300 Subject: focus history is a stack of locations, and the Jumplist is that same stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One container, not two: focus_hist (a stack of pane ids rebuilt every sync) is now jumps[] + a current pointer, and the +Jumps buffer is a RENDERING of that array — nothing copies it, nothing shadows it. prevFocus, Toggleterm, Look's directory order, Back/Forward, Last and Jumplist all walk the one list. A pane id is reused, so a location that only remembered an id would retarget after a respawn: panes now carry a monotonic serial and an entry whose slot holds a different serial is dead. trackJump compacts those out and fixes the pointer in the same pass. The push rule lives in ONE place and says: a location is worth remembering when you cannot see it any more — a different pane, or more than a bodyful of rows away in the same one. So hjkl never grows the list and 100G, a search hit and a goto-definition do. Ctrl-o/Ctrl-i walk it, SPC j j toggles the last two, SPC j l lists them. Ctrl-i IS Tab on a legacy host, where the binding simply never fires and Tab-executes is untouched; kitty reports them apart. SPC j o/i work anywhere. Two goldens moved, both the SPC ? Help listing gaining four rows. --- src/builtins.zig | 66 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 9 deletions(-) (limited to 'src/builtins.zig') diff --git a/src/builtins.zig b/src/builtins.zig index cf144d33..7e20a95b 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -351,13 +351,12 @@ pub const Right = struct { } }; -/// The file<->terminal hop. "Latest" is already recorded: focus_hist is the -/// MRU sync() rebuilds every update (most recent last), the same list a -/// closing pane hands focus back through — so this walks it instead of keeping -/// a second one. Which side is which: only a shell is a terminal; a file, an -/// image and an output buffer (+Search/+Help) are all DOCS you read, so -/// isTerminal is the whole test. Landing pushes this pane to the top of that -/// same history, which is why the hop back is the same key. +/// The file<->terminal hop. "Latest" is already recorded: the jump stack runs +/// oldest-first and a closing pane hands focus back through the same list — so +/// this walks it instead of keeping a second one. Which side is which: only a +/// shell is a terminal; a file, an image and an output buffer (+Search/+Help) +/// are all DOCS you read, so isTerminal is the whole test. Landing pushes this +/// pane onto that same history, which is why the hop back is the same key. pub const Toggleterm = struct { pub fn run(c: Ctx) void { const want_term = !c.pane.isTerminal(); @@ -369,10 +368,10 @@ pub const Toggleterm = struct { if (k != c.id and op.isTerminal() == want_term) t = k; } // ...but the most recently focused one wins - var i = c.p.nfocus; + var i = c.p.njumps; while (i > 0) { i -= 1; - const hid = c.p.focus_hist[i]; + const hid = c.p.jumps[i].pane; const hp = c.p.panes[hid] orelse continue; if (hid != c.id and hp.isTerminal() == want_term) { t = hid; @@ -386,6 +385,55 @@ pub const Toggleterm = struct { } }; +// ---- the jump group ---- + +// Where focus HAS BEEN, as three verbs and a list over the one stack pardes +// keeps (Pardes.jumps — see trackJump for what gets onto it). Builtins rather +// than bare key handlers for the same reason the four directions are: one +// implementation, reachable by chord, by `SPC j ...`, and by executing the +// word wherever it is written. + +/// Ctrl-o: one step back into the history. +pub const Back = struct { + pub fn run(c: Ctx) void { + c.p.jumpBy(-1); + } +}; + +/// Ctrl-i: one step forward again, up to wherever Back started. +pub const Forward = struct { + pub fn run(c: Ctx) void { + c.p.jumpBy(1); + } +}; + +/// vim's Ctrl-^: the pane you were in before this one, whichever it was — the +/// hop you press twice a minute and never want to count steps for. +/// +/// It does NOT move the stack cursor: it goes somewhere, so trackJump records +/// it like any other move, and that is exactly what makes it an involution — +/// after the hop, the pane you came from is the newest OTHER pane, so pressing +/// it again comes straight back. Back/Forward walk history; this one makes it. +pub const Last = struct { + pub fn run(c: Ctx) void { + var i = c.p.njumps; + while (i > 0) { + i -= 1; + const j = c.p.jumps[i]; + if (j.pane != c.id) return c.p.focusPaneLine(j.pane, j.line, j.col); + } + } +}; + +/// The same stack, as text you can read and click. Not a copy of it and not a +/// second list kept in step — the buffer is RENDERED from the stack when you +/// ask, the way +Search is rendered from a walk. +pub const Jumplist = struct { + pub fn run(c: Ctx) void { + output_pane.openJumps(c.p, c.id); + } +}; + // ---- the language group ---- // Reached as `SPC l ` — see leader_path for why the prefix -- cgit v1.3