I've written some customizations in my .vimrc that I would like to further improve and refine.

In particular, for some key combinations, I'm doing text substitutions, like this:

vmap <C-S-w> :s/\%V.*\%V./Foo & Bar<Enter>:noh<Enter>

It replaces whatever has been marked with "Foo Bar". This works great.

However, I would like to extend this for the macro to be able to distinguish if the selection was made in regular visual mode ("v") or in visual line mode ("V").

Let's say on visual mode I want the above substitution but on visual line mode I want "Fooline & Barline".

I believe the answer lies within a vim function, but I cannot figure out how to approach this.

How to determine the current selection state and how to execute a substitution from a function?

1 Answer 1


You can use the mode() function.

It returns: v in visual mode and V in visual line mode.

More information with:

:help mode()
mode([expr])    Return a string that indicates the current mode.
        If [expr] is supplied and it evaluates to a non-zero Number or
        a non-empty String (|non-zero-arg|), then the full mode is
        returned, otherwise only the first letter is returned.
        Also see |state()|.

           n        Normal
           v        Visual by character
           vs       Visual by character using |v_CTRL-O| in Select mode
           V        Visual by line
           Vs       Visual by line using |v_CTRL-O| in Select mode
           CTRL-V   Visual blockwise
           CTRL-Vs  Visual blockwise using |v_CTRL-O| in Select mode

For you case it would mean:

vmap <expr> <C-S-w> mode(1) ==# 'v' ? (':s/\%V.*\%V./Foo & Bar<Enter>:noh<Enter>') : (':s/\%V\_.*\%V./Fooline & Barline<Enter>:noh<Enter>')

Or alternatively using a function:

function! ReplaceSelection()
  if mode(1) ==# 'v'
    return ':s/\%V.*\%V./Foo & Bar' .. "\<Enter>:noh\<Enter>"
    return ':s/\%V\_.*\%V./Fooline & Barline' .. "\<Enter>:noh\<Enter>"

vmap <expr> <C-S-w> ReplaceSelection()
  • 1
    Thank you so much for your help! This feels really close but unfortunately, it doesn't work (yet). I tried fixing it... initially I got "trailing data" and suspect that the "?" of the ternary operator was missing. If I however go with vmap <expr> <C-S-w> (mode(1) ==# 'v') ? (":s/\%V.*\%V./Foo & Bar<Enter>:noh<Enter>") : (":s/\%V_.*\%V./Fooline & Barline<Enter>:noh<Enter>") then I get E486: Pattern not found for the %V... part :-( Sep 12, 2022 at 9:34
  • You are right I forgot the ?. I'll try to reproduce your problem. Sep 12, 2022 at 10:26
  • I also forgot to escape the \ into \\ because of the double quotes ". It should work fine now. Sep 12, 2022 at 11:47
  • 1
    @VivianDeSmedt No need to escape the backslashes: just use single quotes instead. The strings are used as the rhs of the mapping, so the <Enter> special characters will be treated how you want them to even without using double quotes. Also, I'd be tempted to separate your expression out into a function, rather than trying to cram it into a one-liner. Might not matter as much in your .vimrc, but it'd be a lot reader to read on this site!
    – Rich
    Sep 12, 2022 at 13:09
  • @Rich, thanks I have tried both and it works but it seems that for the function version I have to "translate" the <Enter>. Sep 12, 2022 at 14:44

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.