5

I'm pretty new to vimscript, and I wrote this little function to map H so that the cursor will go to the first non-whitespace character of the line, or to the beginning of the line if the cursor is already at the first non-whitespace character. I just want to a) share this for anyone who might find this useful, and b) ask if there's a more efficient way to do this. I feel like there should be some more in-built way to do this.

function! HFunc()
    let colnum1 = col(".")
    execute "normal! ^"
    let colnum2 = col(".")
    if(colnum1 == colnum2)
        execute "normal! ^0"
    endif
endfunction

nnoremap <silent>H :call HFunc()<cr>
  • 2
    The H part of your question is completely irrelevant and H is a pretty useful command so it's not really a good idea to advocate remapping it to something as futile. I suggest replacing everywhere with "some shortcut". – romainl Apr 27 '16 at 8:37
  • @romainl Good point. Changed the question title. – Ben Lindsay Apr 27 '16 at 12:39
5

It can definitely be simplified with:

nnoremap <silent><expr> H indent('.')+1 == col('.') ? '0' : '^'

<expr> causes the map execute the expression on the {rhs}.

indent('.') is the number of whitespace characters at the beginning of the line. The character right after that would be the beginning of the line ^.

To do the opposite:

nnoremap <silent><expr> L col('.') == match(getline('.'), '\S\zs\s*$') ? '$' : 'g_'

I'm not sure if there's a better way, but match(getline('.'), '\S\zs\s*$') finds the position of the last non-whitespace character.

g_ is a motion to go to the last non-whitespace character.

As for your script, you didn't really need to use the execute statements since you weren't including variables. They could've just been normal! ^ and normal! 0. Also, normal! ^0 is actually two motions: Move to the beginning of the line, then to the first column of the line. The ^ was redundant.

In any case, good job taking a stab at vim script. It's not an easy language to learn 🙂

  • 3
    I would use the expression col('.') > indent('.') ? '^' : '0'. This simplifies things a little, and it will now go to the beginning also when the column is between the beginning of the line and the first non-whitespace character. – Karl Yngve Lervåg Apr 27 '16 at 6:32
  • Thanks for the tips! That one-liner definitely looks nicer. Quick followup: would there be a way to do a similar thing with L for a "strong" right motion for the unfortunate cases where there is trailing whitespace? Is there something similar to `indent('.')+1' that could get the column number of the last non-whitespace character? – Ben Lindsay Apr 27 '16 at 12:32
  • @KarlYngveLervåg That's perfect, thanks for that. I like that functionality better than what I originally had. – Ben Lindsay Apr 27 '16 at 12:32
  • @KarlYngveLervåg I think it should be col('.') > indent('.')+1 ? '^' : '0' though, right? – Ben Lindsay Apr 27 '16 at 12:43
  • 2
    @TommyA The > does make a difference compared to == in the case where the cursor is between column 0 and the first whitespace character. It makes the cursor go left instead of right in that case. Thanks for adding the L mapping! I added that to my .vimrc too. I wish I could upvote you again. – Ben Lindsay Apr 27 '16 at 13:55
2

Here's another solution:

noremap <silent> H :call  search('\(^\s\+\)\@<=\S\\|^', 'b', line('.'))<CR>

It's a bit convoluted but it works! :D

search() will position your cursor at a given pattern. I've given it TWO patterns separated by \| which means it will jump to the first one it finds. \(^\s\+\)\@<=\S is a pattern that looks for the first non-whitespace character. (The look behind is necessary, because otherwise we would go to the first of the line.) ^ Is a pattern looking for the beginning of the line (obviously). The 'b' is a flag telling search() to look backwards.

The one downside to this is that if you press H While on the first character of the line, it will continue searching backwards. I tried to think of how to stop this, but nothing came to mind immediately.

Edit: Fixed the multi-line problem by adding line('.') to the search command. (Duh!)

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.