Skip to content

Instantly share code, notes, and snippets.

@g0xA52A2A
Last active August 21, 2022 17:58
Show Gist options
  • Save g0xA52A2A/4a03da0be21e4f39e72d66ad8340d131 to your computer and use it in GitHub Desktop.
Save g0xA52A2A/4a03da0be21e4f39e72d66ad8340d131 to your computer and use it in GitHub Desktop.

Vim will move the cursor to the beginning of an object after invoking operator upon it. From an interactive editing perspective this may be considered annoying however it is the consistent choice as operators can be destructive. As such restoring the cursor to its prior position after invoking an operator on an object may not make sense.

There are many ways possible to alter this behaviour to your preference with mappings and/or scripting. But with custom operator mappings this can be particularly ugly.

A common method is to drop a mark or save a view in the mapping. However the mapping must end with g@ so Vim will wait for the operator, meaning the mapping itself can't move the cursor movement back to its original location. As such the movement would have to be placed inside the function that operatorfunc has been set to. I consider this quite ugly as functions should not have to have such a priori knowledge about the mappings that are calling them. Note that dropping a mark or saving a view inside the operatorfunc function is not an option as the cursor has already been moved by the time this is invoked.

A neat solution to this is to save a view whenever operatorfunc is set with an autocmd. Then whenever the cursor is moved check if operatorfunc is set, if so restore the view, dispose of the temporary variable containing said view, and unset operatorfunc with autocmd's disabled as to avoid a loop.

To avoid having an autocmd always fire when the cursor is moved we can instantiate it only when operatorfunc is set. Now that this instantiation occurs whenever operatorfunc is set we can make two further refinements. Firstly we can stop checking if operatorfunc is set and secondly the autocmd that restores the view can be one shot.

function! OpfuncSteady() abort
  let w:opfuncview = winsaveview()
  autocmd OpfuncSteady CursorMoved,TextYankPost *
    \ call winrestview(w:opfuncview)
    \ | unlet w:opfuncview
    \ | noautocmd set operatorfunc=
    \ | autocmd! OpfuncSteady CursorMoved,TextYankPost *
endfunction

augroup OpfuncSteady
  autocmd!
  autocmd OptionSet operatorfunc call OpfuncSteady()
augroup END

One could even create re-implementations of Vim's built in operators to leverage this technique, though I'll leave that as an exercise for the reader.

@mroavi
Copy link

mroavi commented Aug 21, 2022

Ohh wow! I did not know that qw was something. It also works with the repeat operator. Thank you very much!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment