Imagine you have a command that uses Z to save and restore some selections. What if you want everything to restore exactly how it looked before, including the window scroll position?
Turns out, we can easily write commands to save the current window “scroll position”, and then restore it whenever we want. We can use these commands when we call z and Z to get accurate “restore everything, even visually” behavior.
It turns out, if you have the line number of the line you want to be at the top of the screen, this is doable. The top line is like an “anchor”.
The %val{window_range} expansion can help us figure out what line number is currently at the top of screen. It always gives four space-separated integers, and the FIRST one is the line at the top of the screen (0-index). For example, if line 48 is at the top of screen, the value can expand like 47 0 46 0.
So the idea is to save that line number to an option, scroll around our window, and then run a command to “restore” from that option. Almost like Z and z, but for scroll instead of selections.
First we need to declare the option as an int-list, because that’s the format of the window range exapansion. Then we can just set this option to the value of the window range whenever we want to “save”
declare-option -hidden int-list hop_top
define-command save-scroll %{
set window hop_top %val{window_range}
}
It’s important to NOT quote the window range expansion, because we want the “Y coordinate” to be a separate element in the int-list. Specifically, it’s the FIRST element. By not quoting, Kakoune well perform word splitting.
Then, we can define a helper for restoring. The idea is for this command to take the stored window_range as parameters. If we don’t quote and allow for word splitting, then we can easily isolate the line number we want without needing to shell out.
define-command -hidden -params 1.. restore-scroll-helper %{
eval -save-regs ^ %{
set local scrolloff 0,0
exec "Z%arg{1}gvtvjz"
}
}
So if we call this command like restore-scroll-helper %opt{hop_top}, it will do word splitting and the first parameter %arg{1} is the line number we should restore to the top of the screen.
To actually restore the line, it’s simple. We save the current selection with Z, then then we use g to hop cursor to the target line number, for example 24g. Then vt scrolls the top of the screen to the current line. Finally, we use vj to scroll down one line (because the number in window_range is off by 1 due to 0-index), and then restore the selection with z.
We also temporarily override scrolloff to be 0,0. In case any scrolloff is set, that would mess with us because then vt won’t set that line number to exactly the top of the window. So we can override the option to avoid this mess.
Then we can define another command that actually calls our helper:
define-command restore-scroll %{
restore-scroll-helper %opt{hop_top}
}
and perhaps create some mappings:
map global user z :save-scroll<ret>
map global user Z :restore-scroll<ret>