Search code examples
vim

Custom function trough a range with input


I am doing a mapping to a function that takes a symbol as input() and would "wrap" all the words of selected lines with such symbol:

"Quoter/Wrapper
function Quoter(symbol) 
    exe a:firstline . ',' . a:lastline . 's/\S\+/' . a:symbol . '&' . a:symbol . '/g'
endfunction

map <Leader>QQ :call <line1>,<line2>Quoter(input('Symbol(Quoter)'))<CR>

Which seems to work just fine except it's asking for the input multiple times(seems like it's for the number of lines selected), how can I get to it ask just once?


Solution

  • If you scroll down a bit from :help :function, you end up on :help :func-range, which explains how functions handle range. Paraphrasing…

    • Without the range argument, the function is called for every line in the given range:

      function! Foo()
          echo a:firstline .. ':' .. a:lastline
      endfunction
      :4,8call Foo()
      4:8
      4:8
      4:8
      4:8
      4:8
      
    • With range, the function is called only once and expected to handle the range itself:

      function! Foo() range
          echo a:firstline .. ':' .. a:lastline
      endfunction
      :4,8call Foo()
      4:8
      

    Note that the way you call the function is incorrect, too:

    map <key> :call <line1>,<line2>Foo()<CR>
    
    • The range comes before :call, not before the function name:

      map <key> :<line1>,<line2>call Foo()<CR>
      
    • The appropriate range is automatically added by Vim when you execute an Ex command from visual mode, so there is no need for a range:

      map <key> :call Foo()<CR>
      
    • Since your mapping is meant to be a visual mode mapping, you should use the appropriate :map command:

      xmap <key> :call Foo()<CR>
      
    • Since you don't use another mapping in your mapping, explicitly making it non-recursive would be a good idea:

      xnoremap <key> :call Foo()<CR>
      
    • If you only use a recent Vim, why not use :help <Cmd>?

      xnoremap <key> <Cmd>call Foo()<CR>