How do I run a command without moving the cursor or changing the scroll position?
Answer
:let view=winsaveview() | {cmd} | call winrestview(view)
Explanation
When writing Vimscript functions or mappings, commands like :substitute, gg, or :%normal will move the cursor and change the scroll position. The winsaveview() and winrestview() functions let you save and restore the exact window state — cursor line, column, scroll offset, and more — so the user sees no visible jump after your command runs.
How it works
winsaveview()— returns a dictionary containing the cursor position, topline (scroll position), and other window state{cmd}— any Ex command that would normally move the cursorwinrestview(view)— restores the saved window state exactly
The saved dictionary includes keys like lnum (cursor line), col (cursor column), topline (first visible line), and leftcol (horizontal scroll offset).
Example
Strip trailing whitespace from the entire file without moving the cursor:
:let view=winsaveview() | %s/\s\+$//e | call winrestview(view)
Wrap it in a function for reuse:
function! StripTrailingWhitespace()
let l:view = winsaveview()
%s/\s\+$//e
call winrestview(l:view)
endfunction
nnoremap <leader>w :call StripTrailingWhitespace()<CR>
Tips
- This pattern is essential for any mapping or autocommand that modifies the buffer globally
- The view dictionary can be inspected with
:echo winsaveview()to see all saved fields - Unlike using marks to save position,
winsaveview()also preserves the exact scroll offset