22

Since I frequently don't use the excellent motions and text objects that vim provides, (and since "Holding down 'j' is a vim anti-pattern,") I'd like vim to assist me in training to use these instead of using hjkl more than a few times in a row.

When I started using vim, I was annoyed that I didn't use hjkl to move but would instead use the arrow keys. As a reminder not to do this, I remapped the arrow keys to keep myself from using them - I knew using the home row to navigate would be a better long term plan, so I cut out the positive reinforcement I would get by having working arrow keys.

map <left> <nop>   
map <right> <nop>  
# I quickly removed nop's for up and down because using 
#  the mouse wheel to scroll is sometimes useful

I no longer need these mappings in my .vimrc because this worked very well, and I quickly switched, without having to make a conscious effort to do so. In a similar fashion, I'd now like to cut off my repeated use of basic movement keys hjkl as well. I was envisioning something like this:

let g:last_mov_key = 'none'
let g:times_mov_key_repeated = 0
function! MovementKey(key)
    if (g:last_mov_key == a:key)
        let g:times_mov_key_repeated = g:times_mov_key_repeated + 1
    else
        let g:last_mov_key = a:key
        let g:times_mov_key_repeated = 0
    endif
    if g:times_mov_key_repeated > 3
        echo "Negative Reinforcement!"                             
    endif
endfunction

noremap j :call MovementKey('j')<CR>gj
noremap k :call MovementKey('k')<CR>gk
noremap h :call MovementKey('h')<CR>h
noremap l :call MovementKey('l')<CR>l

But this breaks in visual mode, and I imagine in tons of other cases where using the vim command line in the middle of something changes the state when it shouldn't. How can I constrain myself to have to use more complicated motions?

Edit: Question edited after first two answers for clarity, paragraphs reordered. I want some assistance from vim moving beyond romainl's "level 0". So far answers advise me not to waste my time, but what if we assume that I'm a fan of the learning technique where I change my habits by altering my environment to change incentives? In this model I want to accomplish a task, say, scrolling down a page, and I will more or less randomly attempt key combinations until I achieve that task. Dopamine signalling etc. in my brain will reinforce the action which eventually achieves this result. I could focus on remembering not to use hjkl, or I could focus on the task I was originally trying to do anyway, edit text, and without really thinking about it find myself using more efficient editing techniques.

Thomas
  • 6,515
  • 1
  • 31
  • 47
  • 1
    For the record setting `mouse=a` lets you map Up/Down to and still have a working scrollwheel (also enables mouse selection if that's a thing you need). – Holloway Oct 06 '15 at 10:49

5 Answers5

11

You listen to other people waaay too much. Just use for movement whatever keys you like best, and leave other keys alone. Remapping hjkl keys is somewhat troublesome, and best not done, because they're hardcoded in vim due to historical reasons.

Rook
  • 60,248
  • 49
  • 165
  • 242
  • I came to the conclusion myself, and provided a link in case someone was interested in why I might feel like this. I frequently find myself holding down the j key while waiting to be at a different part of a document due to some deep subconscious instinct, and I'd like to squash this habit out. But it's helpful to hear that remapping hjkl keys is particularly difficult, I may just have to learn the hard way, practice and bothering to think about it. – Thomas Feb 02 '12 at 02:58
  • @Thomas - Well, remap some modifier key with hjkl keys, to take you down an entire screen, if you don't feel like waiting. Or something similar. Vim really has plenty of options for moving. – Rook Feb 02 '12 at 03:04
  • My (admittedly petty) issue is that because I could just keep holding j and wait, I won't be sufficiently motivated to try other things. I think I might just disable hjkl for a few (unproductive) days. – Thomas Feb 02 '12 at 03:25
  • @Thomas - Don't take this the wrong way, but you're wasting(!) too much time on the wrong things. There is nothing wrong with either hjkl nor with using arrow keys, nor with (some other variant which suits you best). ... You won't get anything from all this, unless your work consists of just moving up and down the file all day. – Rook Feb 02 '12 at 03:28
10

One thing I've found that works is :noremap jj <nop>. Vim will delay the j motion because it thinks you might be extending it, which slows you down enough to try something else. You also can't hold the j key down anymore because it just triggers the nop instead. You can still use the button in emergencies, but it's frustrating enough to make you avoid it in the day-to-day editing.

Hovercouch
  • 1,962
  • 12
  • 12
  • As a fulltime vim user, I think this is the best and by far the simplest solution to OP's desire to avoid holding down movement keys – Jon Church Jun 03 '18 at 01:48
  • Trying out this method and I think it works pretty well, only part bugging me is that the delay is also applied when moving a number of lines down, like `48j` which I find to be an ok usage of `hjkl` but I guess there's no way to have separate behaviors for those? – patrick.elmquist Oct 11 '21 at 09:41
6

EDIT

After reading your edit and your comment here is the radical solution I propose: remap hjkl to do super annoying things:

nnoremap h $
nnoremap l 0
nnoremap j gg
nnoremap k G
" and so on for other modes

hjkl are mapped to the extreme opposite of their original behaviour, essentially making them completely unusable. You should do the same for their synonyms (:h h, :h j, :h k, :h l) too for completeness but there are cases when character-by-character/line-by-line movement is useful. In such cases you will be glad to have + or - or <Space>.

I don't really like that kind of pavlovian method, though. I find it too brutal and the risk that you actually get used to these weird mappings seems quite high.

END EDIT

Short version:

Neither the arrow keys nor hjkl are worth using so your remappings are ultimately useless. After a while you will get used to Vim's motions and text-objects. Use what feels more natural to you in the meantime. Don't rush it. It will come. Naturally.

Long version:

Are you a touch typist? Or do you plan to learn touch typing?

I'm not a touch typist and I don't plan to ever become one: I use hjkl only to actually input hjkl in INSERT mode.

When I need to move 1 or 2 lines above or below without a particular "target" I use the arrow keys, when I need to move a couple letters to the left or to the right I use the arrow keys. There is no shame in doing that.

There is a problem, however, when you type jjjjjjjjjj or hit the down arrow key 10 times to move down 10 lines: jjjjjjjjjjj is obviously just as bad as

This stupid mantra of "don't use the arrow keys" is just the tree that hides the forest: it's repeated so often that it makes you focus on useless patterns/antipatterns instead of actually learning more powerful ways.

I don't want to paraphrase the beautiful Your problem with Vim is that you don't grok vi. but you could see it as "levels of enlightenment":

  • Level 0 would be

    jjjjjjjjjjj or then all the necessary horizontal movements to reach your target.

    Reading the line above, isn't it rather obvious that the hjkl Vs debate is absolutely stupid?

    Whether you use one method or the other you end up mashing your keyboard moving vertically AND horizontally to reach the value you want to edit. What a waste. And what a waste of time and energy to force yourself to use one method over another since both are equally bad.

  • Level 1 would be

    10j22l to go down 10 lines and reach your target 22 characters to the right. While it's a lot less typing, you now have to count lines and characters which is not particularly better.

  • Level 2 would be

    10jwww to go down 10 lines and reach your target 3 words to the right or 10jf# to go down 10 lines and jump to the first letter of your target (an hex color value).

    Hmm, that's a lot better.

  • Level 3 would be

    /#<CR> to jump directly to your target (an hex color value, as before). If it's above your current position you would do ?#<CR>.

If you are a touch typist, or are training to become one, the debate is settled: hjkl (or jkl; as some like to remap them) are quick and natural and you don't need arrow keys at all and if you are not, the benefits of using hjkl over are at best minimal.

Focus on eEbBwWfFtT/?} and Co. instead. One bit at a time. Don't "force" yourself.

Community
  • 1
  • 1
romainl
  • 186,200
  • 21
  • 280
  • 313
  • My aim with this question is to move beyond level 0 - I absolutely agree that hjkl over the arrow keys isn't really the point - I mention turning off arrow keys as a common example of a reminder not to use a technique, which I used years ago when I started using vim. I'd now like a similar reminder not to press j repeatedly, but unfortunately it appears more difficult to rebind hjkl than the arrow keys. – Thomas Feb 02 '12 at 14:07
  • OK, I like your description of your process. Our brains don't seem to work the same way ;). Anyway, I've edited my answer with a proposal. By the way, I didn't want "level 0" to sound pejorative, I hope you didn't take it wrong. – romainl Feb 02 '12 at 15:41
  • Nice, thanks. I had thought that I would need to use hjkl some still, but if I skip straight to searching to navigate I suppose I won't, at least for the brief training period. Don't worry, I didn't take the "level 0" thing negatively - the "levels of enlightenment" are very helpful. – Thomas Feb 02 '12 at 16:49
  • I'm going with this solution for now - but I'm going to make then nops instead of binding them to confusing things - that seems too harsh. – Thomas Feb 06 '12 at 17:46
3

I would like to propose a solution that is more in line with the OP's way of thinking.

I too decided that blocking certain motions if not preceded by a count was a good idea, as discussed here. I too tried a simply direct remapping, but this did not work in many contexts, as described by the OP.

But I did finally come up with an approach that worked using key maps that returned an expression:

function! DisableIfNonCounted(move) range
    if v:count
        return a:move
    else
        " You can make this do something annoying like:
        " echoerr "Count required!"
        " sleep 2
        return ""
    endif
endfunction

function! SetDisablingOfBasicMotionsIfNonCounted(on)
    let keys_to_disable = get(g:, "keys_to_disable_if_not_preceded_by_count", ["j", "k", "l", "h", "gj", "gk"])
    if a:on
        for key in keys_to_disable
            execute "noremap <expr> <silent> " . key . " DisableIfNonCounted('" . key . "')"
        endfor
        let g:keys_to_disable_if_not_preceded_by_count = keys_to_disable
        let g:is_non_counted_basic_motions_disabled = 1
    else
        for key in keys_to_disable
            try
                execute "unmap " . key
            catch /E31:/
            endtry
        endfor
        let g:is_non_counted_basic_motions_disabled = 0
    endif
endfunction

function! ToggleDisablingOfBasicMotionsIfNonCounted()
    let is_disabled = get(g:, "is_non_counted_basic_motions_disabled", 0)
    if is_disabled
        call SetDisablingOfBasicMotionsIfNonCounted(0)
    else
        call SetDisablingOfBasicMotionsIfNonCounted(1)
    endif
endfunction

command! ToggleDisablingOfNonCountedBasicMotions :call ToggleDisablingOfBasicMotionsIfNonCounted()
command! DisableNonCountedBasicMotions :call SetDisablingOfBasicMotionsIfNonCounted(1)
command! EnableNonCountedBasicMotions :call SetDisablingOfBasicMotionsIfNonCounted(0)

DisableNonCountedBasicMotions

Note that the code is included here for convenience, but I would check with the gist as well to see if there have been updates/fixes.

You can relax the constraint for horizontal motions, or add/remove other motions by setting the list of the keys/commands that are effected, g:keys_to_disable_if_not_preceded_by_count, in your ~/.vimrc. For example, the following only enforces count-prefixed motions for "j" and "k":

let g:keys_to_disable_if_not_preceded_by_count = ["j", "k"]

Also, as noted in the gist, you might find it really useful to add something like this in your ~/.vimrc as well as the code above:

set number
if has('autocmd')
augroup vimrc_linenumbering
    autocmd!
    autocmd WinLeave *
                \ if &number |
                \   set norelativenumber |
                \ endif
    autocmd BufWinEnter *
                \ if &number |
                \   set relativenumber |
                \ endif
    autocmd VimEnter *
                \ if &number |
                \   set relativenumber |
                \ endif
augroup END

Or install a plugin line vim-numbers.

Even though this question has (long) been answered to the OP's satisfaction with an alternate approach, I am adding my solution here in case anyone is searching for something different.

Jeet
  • 38,594
  • 7
  • 49
  • 56
  • This is terrific, I just added it! It'd be great if you'd include the code here, but I'm content with the gist. Thanks! – Thomas Mar 03 '15 at 18:03
  • 1
    I added the code, but kept the link to the gist as I will be updating the gist with enhancements/fixes. – Jeet Mar 03 '15 at 18:10
0

A very platform-specific solution I just found, Mac only: using something vim-like (I've only gotten this to work with PyCharm in vim mode), turn on MacOS key holding*, which makes holding down e bring up èéêëēėę to choose from. This makes holding down keys never work! This is really annoying unless you are trying to learn to stop holding down keys!

If I could figure out how to make this work in my terminal emulator (iTerm, Terminal.app, etc) this could probably be made to work in normal vim?

*This turns this feature on in case it's disabled for you: defaults write -g ApplePressAndHoldEnabled -bool true

Thomas
  • 6,515
  • 1
  • 31
  • 47