6

I'm trying to create a function mapping that takes a count, in this case the number of lines to yank to the system clipboard.

func! Clipboard()
  echo 'Clipboard function called'
  if v:count is 0
    normal "+yy
  else
    exec 'normal ' . v:count . '"+yy'
  endif
endfunc
nmap YY :call Clipboard()<CR>

Problem is it's being called multiple times when a count is provided and even then the clipboard ends up with only one entry. Pretty bizarre.

e.g. with this text

first line
second line
third line
fourth line
fifth line
sixth line
seventh line
eighth line

I place the cursor at 'first line' and hit '4YY' and the clipboard ends up with these lines:

fourth line
fifth line
sixth line
seventh line

How's this happening?
The messages printed are:

4 lines yanked
4 lines yanked
4 lines yanked
4 lines yanked

If the function is really being called 4 times like the message says, why do I have only one entry in the clipboard?

5
  • :h func-range, or use :<C-u>call Clipboard()<CR> in your map.
    – VanLaser
    Commented Aug 16, 2015 at 19:14
  • My gvim can't find help for func-range. Works perfectly though. But how? Commented Aug 16, 2015 at 19:27
  • Using range, the function handles the range itself (a:firstline, a:lastline), so it's called only once. See here. The other approach (using <C-u>) works too - you clear the range prior to calling your function, so it's called only once, but you still have access to v:count (also v:count1) - so it's probably easier to use in your case.
    – VanLaser
    Commented Aug 16, 2015 at 19:48
  • BTW your exercise could be resumed to: noremap YY :y<cr>, with visual mode support.
    – VanLaser
    Commented Aug 16, 2015 at 19:55
  • 1
    Thanks a lot mate. Not going to pretend I understand all that, maybe someday. What's important is that it works for now Commented Aug 16, 2015 at 20:40

2 Answers 2

2

(just to move the relevant comments in an answer)

Use range, so that the function handles the range itself (a:firstline, a:lastline), so it's called only once. See here (:h func-range).

Another approach: use :<C-u>call Clipboard()<CR> in your map. You clear the range prior to calling your function, so it's called only once, but you still have access to v:count (also v:count1) - so it's probably easier to use in your case.

4

When using counts and calling functions a range will be inserted automatically by Vim. You can see this by doing simply doing 3:. A common solution is to use <c-u> to remove the range. See :h c_CTRL-U for more help.

nmap YY :<c-u>call Clipboard()<CR>

However there are a bunch of issues worth going over:

  • You should generally avoid recursive mappings. Use noremap variants. e.g. nnoremap <key> lhs
  • General rule of thumb: unless you are mapping to a <Plug> mapping use noremap.
  • Y is a Vim command and often remapped to y$. Your key choice will cause a delay if you want to use Y.
  • Use v:count1 instead of v:count to simplify your code. See :h v:count1.
  • Use full names. e.g. function instead of func. This will help aid with readability.
  • Probably don't want this function in the global scope. Use s: and <SID>. See :h internal-variables and :h <SID>
  • This only method assumes that you only want to deal with yanking lines. Probably best to include a visual mode method as well.
  • Should we be able to d to clipboard?
  • Possibly check out 'clipboard'. See :h 'clipboard'.
  • Possibly make it easy to do "+ instead of change a bunch of mappings. e.g. nnoremap "" "+
2
  • lol at that rate we'll end up with a plugin. I would love visual mode functionality, how do I modify it? Commented Aug 17, 2015 at 18:55
  • Visual mode mapping: xnoremap <key> "+y. You can experiment with using "+y and "+Y to meet your needs. Commented Aug 17, 2015 at 18:59

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.