2

How do you create a binding that behaves like yy but doesn't yank the leading white spaces?

nnoremap <leader>f ^v$y

Yanks one line and the ^J. Which is what I want but how do I add a [count] to do mulitple lines?

  • 1
    Maybe instead of yanking think about adjusting the indention level while putting/pasting. ]p & [p puts/pastes with the current indention level. See :h ]p – Peter Rincker Jul 29 at 18:10
1

Your mapping is tricky because you don't want to yank the leading whitespaces. You cannot simply supply a count to your map as you would do for an operator. You need to define a new command with the -count attribute, or a new function and bind it using v:count.

I came up with this :

command! -count=1 YY let @"=join(map(getline(line('.'), line('.')+<count>-1), 'substitute(v:val, "^\\s*", "", "")'),"\<nl>")."\<nl>"
nnoremap <silent> <leader>f :<c-u>execute v:count1 . 'YY'<CR>

which get the list of the current line and the <count>-1 ones below (default is 1); removes their leading whitespaces; and join them in a single string with <NL> (^J) between and after; finally setting the result to the unnamed register "

For a more "intuitive" solution without using Vimscript functions, you could do the following. It is more incremental, like if you were in normal mode.

function! YY(count)
    let l:s=@s                  " save register 's'
    let @s=''                   " empty register 's'
    let l:curpos = getcurpos()  " save cursor position
    for l:i in range(a:count)   " do your mapping but *append* to register 's'
        normal! ^v$"Syj
    endfor                      " and move down one line. repeat
    call setpos('.', l:curpos)  " restore cursor position
    let @"=@s                   " set unnamed register to register 's'
    let @s=l:s                  " restore register 's'
endfunction
nnoremap <silent> <leader>f :<c-u>call YY(v:count1)<CR>

Maybe someone has a better solution, this is all I got...

Usefull helps.

  • :h command-count and more generally :h user-commands
  • :h v:count and h v:count1
  • :h function-list
1

You can use the v:count variable to access the count passed to a mapping.

If you don't pass any count, the v:count variable will evaluate to zero. If you prefer something that evaluates to 1 instead, you can use v:count1 which does just that.

For your particular use case, yanking without leading whitespace, my suggestion is to just yank the contents normally, then process the yank contents using substitute() to strip leading whitespace.

When writing a mapping that does yanks, puts or deletes, you might also want to consider v:register, in case the user specifies a non-default register when calling your mapping. You need to evaluate it early on in the mapping, since it may be overwritten by other commands you run, so best to pass it as an argument. You can then use setreg() to set the register's contents.

Assuming you want to save the yank on the specified register only and not overwrite the default register (this is not how the yy command works, but is a useful behavior), then this should work:

function! YankTrimLeadingSpace(register)
  let saved_unnamed_register = @@
  execute 'normal!' v:count1.'yy'
  let @@ = substitute(@@, '\v(^|\n)\zs\s+', '', 'g')
  if a:register != '"'
    call setreg(a:register, @@)
    let @@ = saved_unnamed_register
  endif
endfunction

nnoremap <silent> <leader>f :<c-u>call YankTrimLeadingSpace(v:register)<cr>

It also works with uppercase register specifications, which will append to the existing registers. Works with and without a count.

  • 1
    This! Integrates better than my answer. And to make it work with ranges also, we could (1) make the function handle it by appending range; (2) replace the execute 'normal!'... line with execute a:firstline.','.a:lastline.'yank' and (3) remove <c-u> to the mapping. We can thus create an xnoremap mapping just like the normal one. And the normal [count]<leader>f still works as expected. – perelo Jul 29 at 9:12
  • 1
    question though : does \n in the regex is 'portable' ? Wouldn't it be safer to use \<nl> ? – perelo Jul 29 at 9:14
  • @perelo "is \n in the regex portable?" Yes. See :help /\n which will show you it matches a newline. I also tested it with ff=dos and ff=mac. Internally, Vim keeps a consistent representation, just converting to the final platform at the last layer. – filbranden Jul 29 at 14:21
  • @perelo Yes some handling of Visual mode would be nice. Another way to handle it would be to pass visualmode() as an extra argument to the function and then yank inside it, that would allow handling char-wise or block-wise visual as well. (Similar to this?) But the normal version of this one is very line oriented, so ranges are probably a great translation of it? I'll update the answer with your suggestion. – filbranden Jul 29 at 14:27
  • Though... Handling both range and non-range is quite difficult actually... I can see why separate n + v maps are typically used. Not to mention that usually normal maps with an operator are the norm, since they're most flexible. – filbranden Jul 29 at 15:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.