3

I have this function which displays line numbers. I want it to be called in different ways:

  • from a command, specifying the lines as arguments
  • with a visual selection, printing the first and last line numbers of the selection
  • with a motion, printing the current line and the line at the end of the motion

So far I have the first two ones:

command! -nargs=* PL :call PrintLines('command',<f-args>)
vnoremap <silent> P :<C-U>call PrintLines('visual')<CR>

function! PrintLines(type,...)

  " Get the lines to fold around
  if a:type == 'command'
    let l:line_before = a:000[0]
    let l:line_after = a:000[1]
  elseif a:type == 'visual'
    let l:line_before = line("'<")
    let l:line_after = line("'>")
  endif

  echo l:line_before
  echo l:line_after

endfunction

But I can't figure out the way to use this with a motion. Ideally it would be possible to do something like:

 nnoremap P{motion} :<C-U>call('normal')<CR>

And use it like this:

 Pib

1 Answer 1

6

You'll nee to use the g@ feature (see :h g@) This operator allow to specify a motion after a function call and position the marks '[ and '] at the beginning and the end of the text selected by the motion.

When calling a function this way the mode of selection (line-wise, character-wise, block-wise) is passed as the first argument of the function.

With this method you can use line("'[") and line("']") to get the number of the lines. Your function should look like this:

function! PrintLines(type,...)

    " Get the lines to fold around
    if a:type == 'command'
        let l:line_before  = a:000[0]
        let l:line_after   = a:000[1]
    elseif a:type == 'visual'
        let l:line_before  = line("'<")
        let l:line_after   = line("'>")
    elseif a:type =~ 'line\|char\|block'
        " Test the different mode that g@ can pass as argument
        " and get the corresponding lines
        let l:line_before  = line("'[")
        let l:line_after   = line("']")
    endif

    echomsg l:line_before.' '.l:line_after

endfunction

Now about the mapping it should look like this (largely inspired from the example of the doc):

nnoremap <silent> P :set opfunc=PrintLines<CR>g@

You can then use your mapping P and Vim will wait for a motion input (like } or 5j) to call the function correctly.

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.