Vim: Making Those Arrow Keys Work for You (Or Why the Anti-Arrow-Key Propoganda is Wrong)

The Great Controversy

A standard dictum amongst experienced Vim users is not to use the arrow keys to move around your document. This dictum is often repeated again and again, in tones that range from the taken-for-granted to hysterical-zeal. The most common reason given for this is that using the arrow keys takes your hands away from the home row of your keyboard, and thus is wasteful both in terms of time and energy, whereas the standard Vim movement keys —| h, j, k, and l —| keep your hands on the home row, and therefore is far more efficient. Here is what I have discovered: this anti-arrow key argument is wrong!

The Great Experiment

When I first started using Vim, I of course found the h, j, k, and l keys to be horribly unintuitive.
But what was worse was that I could not use these keys to move in insert mode.It was this latter issue that kept me resolutely married to my arrow keys.The centi-seconds of time it took for me to move my fingers to hit an arrow key seemed far more efficient than exiting into normal mode, using the h, j, k, and l to move to where I wanted, and then i again to get back into insert mode. And so, for the longest time, not only did I continue to use the arrow keys, but I had absolutely no desire to cease doing so, just to gain what would probably accumulate to be about an hour’s worth of saved time over the course of a year.

Recently, however, on a whim, just to see what the fuss was about and if I was correct in viewing the arrow-keys-Noooooooooo!-zealots as the computing equivalent of monodimensional cladist fundamentalists, I decided to indulge in what I initially viewed as a temporary experiment in masochism.

Yes, that’s right.

I disabled my arrow keys:

inoremap  <Up>     <NOP>
  inoremap  <Down>   <NOP>
  inoremap  <Left>   <NOP>
  inoremap  <Right>  <NOP>
  noremap   <Up>     <NOP>
  noremap   <Down>   <NOP>
  noremap   <Left>   <NOP>
  noremap   <Right>  <NOP>

It did not take long (less than an hour) for me to get used to the spatial directions mapped to the h, j, k, and l keys, and so within a day I was comfortable using these keys to move around in normal mode. But the inability to move around in insert mode was incredibly annoying. It got so frustrating that I considered switching back to using arrow keys permanently, but decided to stick with the pain of trying to work in this tedious and painful environment for a little longer before giving up.

The Great Results

After a couple of days, I came to the realization that I had started working with Vim in a different way. It happened gradually, and mostly unconsciously, so that I did not notice it as first. But I did begin to notice that I was no longer tripping up or stumbling quite as much when needing to move around my document. In fact, quite the opposite, I felt like I was zipping around to places much faster and much more efficiently than I ever did before when I was using the arrow keys.

This feeling was not due to the gains from those accumulated centi-seconds shaved off my editing time by not having to move off the home row so frequently.</ on p>

It was, instead, due to the consequence of being forced to exit insert mode when I was not actually typing text.

Being forced to return to normal mode right after I edited the text at a single point in the document (sometimes just a couple of characters, sometimes a word or two, sometimes short blocks or fragments), also resulted in me being forced to use the rich suite of powerful normal mode movement commands to get to exactly where I needed to be before resuming editing. This was like suddenly beginning to use the fifth and other gears while driving on an open highway, whereas before I had been grinding along for mile after laborious mile on first.

The most remarkable and unanticipated aspect of my new way of working with Vim was not so much my usage of the h, j, k, and l keys, and other relatively “dumb” movement keys (0, $, etc.). It was my much greater usage of “smart” movement keys and commands. Due to the shift in the dominant ecology (i.e., normal mode vs. insert mode dominant), my repertoire of idioms for movement within and across the buffer evolved, and simple up/down/left/right movements were replaced by far more efficient and powerful commands. For example, I would use f/F/t/T/; etc. to jump to the exact character on a line that I wanted to get to, instead of pecking away at the left/right keys to get there. I would use :150 or 150G to jump directly to line 150 instead of using the up/down arrow keys. I would use /def or ?def to jump directly to a function line instead of tapping away a directional key like a zombie trying to signal in pseudo-Morse. I would use marks (e.g., mm) to register an “anchor” into the document that I could jump back to directly (by using, for example, 'm), before moving off to temporarily work on some other part of the document.

It is true, of course, that none of these movements are in any way more efficient when, for example, to moving a couple of characters to the left or right or up or down of the current position while in insert mode. In fact, this “local neighborhood” (i.e., one or two characters/lines on either side of my current position) is exactly where the arrow keys do come into their own by allowing you to get to these places without leaving insert mode. It is only when we start dealing with positions more than a couple of offsets away from the current position that the normal mode movement keys and commands start to yield benefits.

But the fact is, while I had arrow keys enabled I simply found it difficult to consciously make it a point to limit my use of them only within the “local neighborhood” . When in insert mode, and I needed to get anywhere that I could see in the viewable buffer, without thinking about it I would start tapping away at the arrow keys, even if the destination was several to dozens of lines/characters away. So while I had learned and started using all those “smart” movement commands that I am using now within the first couple of months of using Vim, I was just not using them enough or in all the places that I should been using them.

Disabling the arrow keys, while making “local neighborhood” movement significantly more cumbersome than before, taught and continues to teach me to view the document as a landscape, across which I can visit any point that I want directly and precisely through Vim’s normal mode movement commands. I still have not completely internalized this perspective, and often find myself reaching for the arrow keys unconsciously while editing in insert mode. So, for the time being, until this regime has been exorcized from my muscle memory, I think I am going to keep my arrow keys disabled as motion keys.

Re-Educating the Arrow Keys to Work With You: Text Shifters Instead of Cursor Movers

However, that does mean that I have 4 decent keys on my keyboard not doing anything at all. That, to me, seems just as wasteful as any of the extra energy and time required to use them. And thus I decided to put those arrow keys to work as “text movers” rather than their default role as “cursor movers”.

Editing text is like doing surgery: when you start hacking away at a bit of code, you usually open up various gashes and slashes in the surrounding area. At least with modern medical practices, it is both customary and polite to sew up these gashes and slashes, and naturally we do the same when editing code: indentation has to restored (here is a tip: see what = and == can do for you in Vim), empty lines need to be closed up, etc. etc. We probably do this dozens of times during a particular edit session, and typically it involves a lot of moving around, deleting and inserting lines all over the place. Even with the power of Vim’s normal mode commands, it still takes a lot of keystrokes, tedium and hassle to achieve this.

And so I have remapped my arrow keys to do the following:

  • The Up arrow key deletes a blank line above the current line (a non-empty line will not be deleted), while the Down arrow key inserts a blank line above the current line. The result is hitting Up or Down moves the current line up or down, in both normal as well as insert mode.
  • Typing Ctrl-Up and Ctrl-Down, instead, deletes or inserts a blank line below the current line. The result is that the text below the current line moves up or down, respectively.
  • Typing Left de-dents the current line, while Right indents it. The result is that text shifts left and right, respectively.

The following code, included in your ~/.vimrc or otherwise sourced into your session, implements this (the actual functions that do the work are copied from here):

function! DelEmptyLineAbove()
    if line(".") == 1
        return
    endif
    let l:line = getline(line(".") - 1)
    if l:line =~ '^s*$'
        let l:colsave = col(".")
        .-1d
        silent normal! <C-y>
        call cursor(line("."), l:colsave)
    endif
endfunction

function! AddEmptyLineAbove()
    let l:scrolloffsave = &scrolloff
    " Avoid jerky scrolling with ^E at top of window
    set scrolloff=0
    call append(line(".") - 1, "")
    if winline() != winheight(0)
        silent normal! <C-e>
    endif
    let &scrolloff = l:scrolloffsave
endfunction

function! DelEmptyLineBelow()
    if line(".") == line("$")
        return
    endif
    let l:line = getline(line(".") + 1)
    if l:line =~ '^s*$'
        let l:colsave = col(".")
        .+1d
        ''
        call cursor(line("."), l:colsave)
    endif
endfunction

function! AddEmptyLineBelow()
    call append(line("."), "")
endfunction

" Arrow key remapping: Up/Dn = move line up/dn; Left/Right = indent/unindent
function! SetArrowKeysAsTextShifters()
    " normal mode
    nmap <silent> <Left> <<
    nmap <silent> <Right> >>
    nnoremap <silent> <Up> <Esc>:call DelEmptyLineAbove()<CR>
    nnoremap <silent> <Down>  <Esc>:call AddEmptyLineAbove()<CR>
    nnoremap <silent> <C-Up> <Esc>:call DelEmptyLineBelow()<CR>
    nnoremap <silent> <C-Down> <Esc>:call AddEmptyLineBelow()<CR>

    " visual mode
    vmap <silent> <Left> <
    vmap <silent> <Right> >
    vnoremap <silent> <Up> <Esc>:call DelEmptyLineAbove()<CR>gv
    vnoremap <silent> <Down>  <Esc>:call AddEmptyLineAbove()<CR>gv
    vnoremap <silent> <C-Up> <Esc>:call DelEmptyLineBelow()<CR>gv
    vnoremap <silent> <C-Down> <Esc>:call AddEmptyLineBelow()<CR>gv

    " insert mode
    imap <silent> <Left> <C-D>
    imap <silent> <Right> <C-T>
    inoremap <silent> <Up> <Esc>:call DelEmptyLineAbove()<CR>a
    inoremap <silent> <Down> <Esc>:call AddEmptyLineAbove()<CR>a
    inoremap <silent> <C-Up> <Esc>:call DelEmptyLineBelow()<CR>a
    inoremap <silent> <C-Down> <Esc>:call AddEmptyLineBelow()<CR>a

    " disable modified versions we are not using
    nnoremap  <S-Up>     <NOP>
    nnoremap  <S-Down>   <NOP>
    nnoremap  <S-Left>   <NOP>
    nnoremap  <S-Right>  <NOP>
    vnoremap  <S-Up>     <NOP>
    vnoremap  <S-Down>   <NOP>
    vnoremap  <S-Left>   <NOP>
    vnoremap  <S-Right>  <NOP>
    inoremap  <S-Up>     <NOP>
    inoremap  <S-Down>   <NOP>
    inoremap  <S-Left>   <NOP>
    inoremap  <S-Right>  <NOP>
endfunction

call SetArrowKeysAsTextShifters()

Using the arrow keys for cursor movement cripples you in terms of time and efficiency, due to the far superior alternatives found in normal mode. Using the arrow keys for shifting text, on the other hand, empowers you by wrapping up an often-used series of repetitive and stereotypical actions into a single keystroke.

Revisiting the “Don’t Use the Arrow Keys!” Dictum

Modified slightly to say “Don’t Use the Arrow Keys for Movement”, this dictum basically holds. But I think it utterly fails to capture the real issue at hand, and the customary justification offered for it (the whole “taking your hands off the home row is wasteful”) fails to make for a convincing argument.

Instead, I think that Vim tutorials, guides, advocates and propaganda everywhere should emphasize:

Don’t move around the document in insert mode!

This is because normal mode offers so much more power and flexibility to get to any part of your document precisely and quickly. The “Don’t Use the Arrow Keys for Movement” should be offered as a corollary to this, rather than a primary statement in its own right, because using arrow keys for movements leads you —| for psychological reasons —| to remain trapped in insert mode when you should be cruising and jetting about in normal mode.

One Comment

  1. I am coming back to Vim, after having used Kate (under KDE) for a while, and after working with Emacs after that, with a couple of stints in NetBeans and Eclipse. I was never what I would consider a Vim power user, but between the interesting things I’ve seen with Vim from a couple of presentations, and the fact that Vim hasn’t had a period of development stagnation like Emacs seems to have (I could be wrong about that, though), I’ve found myself drawn back to Vim.

    All that is to provide background for myself. As a Dvorak keyboard layout user, the claim that HJKL is on home-row doesn’t resonate at all–indeed, H is the only key of those on home row! This post, however, has helped me understand that I need to come to terms with other ways to move.

    Perhaps the best part of this journey, so far, is discovering that, with a couple of plugins, Vim can do what NetBeans can do, only better! And installing plugins isn’t so bad, either, when I consider that when I was somewhat required to use NetBeans at work, I had to install a handful of plugins for that, too, to get it up to speed. I’ve been in the process of creating a “dotfiles” repository; as I work on this, I can see that I’ll be able to do with Vim, what I was unable to do with NetBeans: set up an environment I like, then have it cloned an installed on another machine almost instantly…

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>