vimtricks.wiki Concise Vim tricks, one at a time.

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() and call 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 :keepjumps and :keepmarks for functions that should leave zero trace in the jump list or marks
  • The view dictionary can be inspected with :echo winsaveview() to understand what each key represents

Next

How do I right-justify a line of text to a specific width using a built-in Vim command?