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
  • 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, 2016 at 8:37
  • @romainl Good point. Changed the question title. Apr 27, 2016 at 12:39

2 Answers 2

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 🙂

8
  • 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. Apr 27, 2016 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? Apr 27, 2016 at 12:32
  • @KarlYngveLervåg That's perfect, thanks for that. I like that functionality better than what I originally had. Apr 27, 2016 at 12:32
  • @KarlYngveLervåg I think it should be col('.') > indent('.')+1 ? '^' : '0' though, right? Apr 27, 2016 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. Apr 27, 2016 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 and acknowledge that you have read and understand our privacy policy and code of conduct.

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