How do I save and restore the cursor position and scroll state in a Vimscript function?
Answer
winsaveview() and winrestview()
Explanation
When writing Vimscript functions or complex mappings that move the cursor, it is essential to restore the original view afterward so the user does not notice any jarring repositioning. winsaveview() captures a snapshot of the window state — cursor line and column, top line, horizontal scroll position — and winrestview() restores it exactly.
How it works
winsaveview()returns a dictionary with keys:lnum(cursor line),col(cursor column),coladd,curswant,topline,topfill,leftcol,skipcol. This covers both the cursor and the viewport.winrestview(dict)takes the saved dictionary and restores all of those values atomically.
This is more complete than saving getpos('.') because it also captures the scroll position (which line is at the top of the window).
Example
A function that strips trailing whitespace from the whole file without moving the cursor:
function! StripTrailingWhitespace()
let l:view = winsaveview()
:%s/\s\+$//e
call winrestview(l:view)
endfunction
nnoremap <leader>W :call StripTrailingWhitespace()<CR>
Without winsaveview(), the substitution would jump the cursor to the last match and potentially change the scroll position.
Tips
- Use
let l:saved = winsaveview()andcall winrestview(l:saved)as a standard wrapper around any function that uses global motions or searches - Unlike
getpos('.'),winsaveview()also saves the horizontal scroll (leftcol) — useful in buffers with long lines - Pair with
:keepjumpsand:keepmarksfor functions that should leave zero trace in the jump list or marks - The
viewdictionary can be inspected with:echo winsaveview()to understand what each key represents