0

I'm trying to create a vimscript function that does a substitution on a visual block and then highlights the visual block again with gv. For instance:

function! MySubstitution()
   s/foo/bar/
   normal gv
endfunction

However, when I do :call MySubstitution() on a visual block, what ends up happening is the substitution happens, and then my visual block reappears with one line fewer highlighted.

Why isn't doing normal gv as suggested in this answer working like a normal gv?

The same peculiar issue happens whether I do :normal gv in the command bar or if I try to remap a key to gv:

vnoremap <C-L> :normal gv<CR>

If I repeat any of these normal gv attempts, my visual block will get smaller and smaller, one line at a time per attempt.

2 Answers 2

1

:normal gv in visual mode becomes :'<,'>normal gv which runs it for every line in selection.

Now why it ends up with a selection 1 line less, I am not sure about, but I think it is related to gv and v_gv same command in normal and visual modes. gv re-selects previous visual selection and same gv in visual mode exchanges current and previous selections.

Combined with ranged normal that moves cursor to the first col of each line in selection you can get this behavior. Selection changes per each normal command step and then exchange of current and previous selection happens. But again, am not 100% sure.

Usually one needs to erase range in mappings as in:

xnoremap <C-L> :<C-u>normal! gv<CR>

And your function would need to handle range explicitly:

function! MySubstitution() range
   execute a:firstline .. ',' .. a:lastline .. 's/foo/bar/'
   normal! gv
endfunction
3
  • Interestingly, changing my function to your range handling code worked. However, changing my remapping to xnoremap did nothing (as in, doesn't seem to have any perceivable behavior). Any idea why?
    – wxz
    Apr 2 at 16:19
  • Actually the remapping command worked when changing xnoremap to nnoremap. If you know why will you update your answer to include why the latter worked and not the former and then I'll accept your anwer? Thank you!
    – wxz
    Apr 2 at 16:54
  • xnoremap for visual mode only -- you have to be in visual mode to "make it work". nnoremap for normal mode only. If you are talking about <C-L> mapping -- it works as in, 1. remove selection (<C-u>). 2. re-select what you have just removed.
    – Maxim Kim
    Apr 2 at 22:50
1

The call

:'<,'>call F()

(which you get from visual mode) runs F on each line unless F has range. So you’re invoking normal gv on each line. I can’t explain the result you got, but that seems like a likely culprit of issues.

1
  • Also consider if you really need gv at the end of a mapping: could you make it . repeatable instead and let the user (yourself?) gv if they want?
    – D. Ben Knoble
    Apr 2 at 21:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.