How to write Vimscript functions that operate on a visually selected area
I was trying to write a Vim function that would act on a visual selection but found it surprisingly difficult.
As an example, suppose we want to write a function that converts a visual
selection to upper case and map it to the ,u
key binding1. A first attempt
might be:
function! MakeUpperCase() range
U " or `normal! U`
endfunction
" Define visual-mode mapping
xnoremap ,u call MakeUpperCase()<cr>
but hitting ,u
in visual mode errors:
E464: Ambiguous use of user-defined command U
This is because the function body is executed in normal mode (which can be
verified by checking the value of mode()
in the function body).
So we need to reselect the visual area first using the <
and >
marks that
are set when the function is called:
function! MakeUpperCase() range
normal! `<v`>U
endfunction
This works.
But suppose we want a function that upper cases the contents of the paragraph under the cursor. You might think this would work:
function! MakeCurrentParagraphUpperCase()
" Select current paragraph
normal! vip
call MakeUpperCase()
endfunction
" Define a normal-mode mapping
noremap ,p call MakeCurrentParagraphUpperCase()<cr>
But this fails as MakeUpperCase
is being called in visual mode.
So we extend MakeUpperCase
to check the value of mode()
to determine the
appropriate behaviour:
function! MakeUpperCase()
if mode() == "v"
normal! U
else
normal! `<v`>U
endif
endfunction
This works in both cases.
-
This is a contrived example as the
U
command command already does this. ↩︎