Preventing repeated use of hjkl movement keys in vim
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.
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.
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.
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 ( :hh
, :hj
, :hk
, :hl
) 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.
下一篇: 防止在vim中重复使用hjkl移动键