Home

vim

Hard Mode

Recently, while watching Corey Haines and Aaron Patterson pair-program, I heard Mr. Haines mention vim’s “hard mode”. Apparently, this is when you disable the motion commands h, j, k, and l.

It’s absurd how great this exercise is for increasing your knowledge of vim. There are so many better ways to do everything. Just like complete novices might map the arrow keys to Nop to force learning hjkl, mapping the hjkl keys to Nop forces you to learn all these other ways to move around and edit parts of the file.

The real philosophical shift is thinking in Text Objects rather than Lines and Characters. Words are things, sentences are things, method definitions are things, and these can all be manipulated or navigated through as such.

While you probably can’t fully internalize this concept without going through the exercise yourself, I would like to share a few of the very first “better ways” I’ve been finding while restricted in this way.

Imagine my cursor is a ways down the document, and I need to change the above header in some way. I’m staring at “Search”, I know I want my cursor there. I used to just tap k or maybe a few 10ks with a j or two. What was I thinking?

?Se

And I’m there. In this case, the capital “S” made this word rare enough that I didn’t have to type very much of it. Recognizing the relative frequency of words or characters can be a useful skill for quicker navigation. Drew Neil, author of practical vim, calls this “Thinking like a scrabble player”.

Use the Ex, Luke

Another thing I didn’t realize I do a lot is move to some far away line to copy it, only to come right back to paste it. Really? I’m going to type a bunch of js only to then type the exact same number of ks?

You could use search to get to the far away line then double-backtick to jump back, or you could do this:

:2,7co .

This takes lines 2 to 7 and copies them to here. Not only is this less key-strokes (a number which grows proportional to the distance between here and there), but I’d argue it also keeps your focus better.

You can actually cut out a lot of unnecessary motion using commands like this:

:20   " go to line 20
:20d  " delete line 20
:2,7d " delete lines 2 through 7

In any of these commands . can be used to mean the current line. If you really get frustrated, you could use :.+1 and :.-1 to move like j and k – but I wouldn’t recommend it.

Finding Character

It’s times like these that I try to find a good first concept. Something that’s going to be useful enough to get me further along the habit-building path, but simple enough that I don’t have to remember too much.

First, know that 0 puts you at the start of the line. This gives you a common reference to move from so you only have to think in one direction (for now). Second, know that f and t go to a letter (so fa to go to the next “a” in the line). The difference is t goes till the character, stopping with the cursor just before it and f puts the cursor right on top. You can then use ; to repeat the last search, moving a-by-a along the line.

Once you’ve gotten the hang of this, the capital versions, F and T do the same thing but backwards. , is the key to repeat the last backwards search, but so many people (including me) map that to Leader or LocalLeader that it’s difficult to rely on. I haven’t found a good solution to this, since the only other convention I know of is the default \ which I can rarely type consistently.

There’s a bit of stategy here. It’s true of most motions, but it’s most recognizable with f. You have two choices in approach: pick the letter that you want to be at (no matter what letter it is) and use ; to repeat the last f or t until it gets you there (regardless of how many key strokes that is), or you can choose a letter that appears first in the line (knowing that it will only take one stroke to get there) but which only gets you near your goal. These are the two extremes, finding the best middle ground (lowest overall keystrokes) for any given scenario is something worth mastering.

Word-wise

In addition to finding by character we can start to think in words. Again, we’re making it easy by always starting from 0. Given that, just use w to move word by word with the cursor on the front of each word or e to move word by word but with the cursor on the end of each word. Eventually, I’ll attempt to internalize the same commands in the other direction: b and ge.

All of these have capital versions (W, B, E, gE) which have the same behavior but work on WORDS not words.

The exact rules about words vs WORDs aren’t worth memorizing. WORDs are basically just a higher level of abstraction. For example, <foo-bar> is 5 words but it’s only one WORD.

Conclusion

So far, I’ve gotten myself to consistently use a number of new vim tricks:

  1. Use search to get where you want
  2. Use Ex commands to manipulate text not near the cursor
  3. Move by word, not by character

There’s still plenty to learn, but I’ve found that just these few simple ideas make me effective enough that I’m sticking with it and not just giving up in frustration.

16 Mar 2013, tagged with vim

Sharpening Your Tools

Regardless of what editor you choose or how deeply you decide to extend or configure it, the most important thing for a professional programmer regarding her tool set is awareness.

You need to be acutely aware of situations where you do something repeatedly or slowly that you should automate or speed up. Quality editors like vim or emacs expose powerful constructs for doing tasks incredibly quickly. Ensuring you know all of these built-in constructs is the most common area where I see programmers (myself included) failing at this. It’s too easy to fall into the habit of always using the slow way that might’ve been easiest to guess the first time you had to do something, or to install (or even write) some large plugin to give you a workaround to do the same thing the editor already provides (albeit by some obscure combination of constructs).

Embrace Defaults

First of all, learn and try to embrace the default behavior of your editor. Try to lean on the side of less configuration, and absolutely never remap or override default behavior.

  1. You’ll feel comfortable on any system with that tool installed.
  2. Your tool will be faster to use.
  3. You’ll find that many initially uncomfortable defaults actually make a lot of sense, probably for reasons not immediately obvious to you.

That said, there’s nothing wrong with creating additional behavior for (or extensions to) your tools to make the things you tend to do most often easier. So here are some vim tricks that I’ve recently added to my setup which you might find useful:

Windows

I used to rely on my window manager to work with multiple windows, running multiple instances of my editor for each file I needed to work with at the same time. That was horrible of me. Not only does it use more resources than necessary, but it was also making moving code between files cumbersome and preventing me from exploring multi-file manipulation commands.

This also meant that when I moved to a system without a good window manager (like OS X), I began trying to fill that void with tools like tmux or iTerm splits. Guess what? Vim can split and manage windows just fine all by itself. Having all the open files in the same session also means I can y and p text between them, use commands like :bufdo or :tabdo to run commands over all open files, etc.

So get into the habit of using :split and :vsplit to open files you need to work on side by side. Use :tabopen to keep files open “in the background”. And don’t be afraid to vim file1 file2 to get things open in multiple buffers at once.

When you do have multiple files open in panes of one buffer together, the following mapping will then make it easier to move around:

nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-h> <C-w>h
nnoremap <C-l> <C-w>l

Now you just Ctrl-h to focus left window, Ctrl-j to focus bottom, etc.

This is pretty standard stuff that I’ve had in my vimrc for ages, but recently, when watching a destroy all software screencast, I found something amazing:

set winwidth=84
set winheight=5
set winminheight=5
set winheight=999

It’s hard to describe what this does, but I’ll try. When a pane is split vertically between two or more windows, the focused window will always have at least 84 columns, the other(s) will shrink to accommodate. This doesn’t have much of an effect for me since my terminal usually has enough room to accommodate what I’m doing without squeezing anything in that direction, but what is amazing is what these settings do for horizontally split, vertically stacked windows.

Basically, all non focused windows shrink down to 5 lines and the focused window takes up everything that’s left. This is an amazing workflow b/c it allows you to have a lot of files open at once, but they don’t get in your way. You can still maintain focus on one at a time. Just try it, it’s awesome.

Stamp Out Annoyances

You have to always be on the lookout for things that you do repeatedly or unnecessarily. You should be deeply bothered by any action you’re required to take that the computer could be doing for you.

For example, at least twice a day, I begin editing a file in a non-existent directory. This isn’t just a mistake, I want that file at that path, I just don’t care that the directories don’t exist yet. It should be obvious to my tool that I want them created.

function! Mkdir()
  let dir = expand('%:p:h')

  if !isdirectory(dir)
    call mkdir(dir, "p")
    echo "created non-existing directory: " . dir
  endif
endfunction

autocmd BufWritePre * call Mkdir()

Never again.

Another thing to watch out for is a simple and/or common action taking far too many steps or context switches. For example, I’m constantly creating a file at the wrong path. This happens most often when I’m adding a rails test for some controller a few directories deep. I’ll forget to put the _test on the end of the file almost always.

Normally, I would have to save the file, remember that nested path, execute a mv command (and try to get it correct), then reopen the file.

" Based on https://github.com/km2r/vim-currentfile
function! Rename(dest)
  if &modified
    echoe "buffer is modified"
    return
  endif

  if len(glob(a:dest))
    echoe "destination already exists"
    return
  endif

  let filename = expand("%:p")
  let parent   = fnamemodify(a:dest, ":p:h")

  if !isdirectory(parent)
    call mkdir(parent, "p")
  endif

  exec "saveas " . a:dest
  call delete(filename)
endfunction

command! -nargs=1 -complete=file Rename call Rename(<f-args>)

With this, I just :Rename to/what/it/was/supposed/to/be_test.rb and I can continue editing straight away.

Admittedly, I should just make an “add _test to the end of this file” mapping to fully satisfy my most common use case.

These were just a few examples of simple tweaks to speed up your editing flow, the important thing is to be very aware of the things your doing and not fall into bad habits. It’s a fine line between bloat and convenience so be cognizant of that as well.

Most importantly, keep your tools sharp.

01 Oct 2012, tagged with vim

Vim Registers

When you use an extremely powerful text editor such as vi, vim, or emacs, there are often times where you’ll discover a feature or command that literally changes the way you write text. It’s not a very large leap to say that, for a developer, that can be life-changing.

I’ve recently made one such discovery via vim’s :help registers command. So I’d like to boil it down a bit and share it here.

Pasting in Vim

Often times when idling in #archlinux, someone will ask about pasting in vim.

Answers typically range from :set paste, to S-<insert>, etc, but one staple response is "*p and "+p.

These commands will take the contents of your X11 selection (currently highlighted text) and clipboard (text copied with C-c) respectively and dump it into your buffer.

I’ve heard these commands several times but I could never remember them. The reason is because I didn’t really know what they did. I mean, obviously I knew that they pasted into vim from said locations, but I didn’t know what those three command characters meant. Today, I decided to find out.

Registers in Vim

Vim has a number of what’s called registers, they’re just dumping grounds for text. Vim uses these to store different snippets of text for different reasons in very auto-magical ways. For instance, this is how undo is implemented in vim.

If you understand how vim is storing this text and how to read and write from these registers yourself, it can really help your work flow.

Here’s the list reproduced from :help registers:

  1. The unnamed register ""
  2. 10 numbered registers "0 to "9
  3. The small delete register "-
  4. 26 named registers "a to "z or "A to "Z
  5. four read-only registers ":, "., "% and "#
  6. the expression register "=
  7. The selection and drop registers "*,“+ and "~
  8. The black hole register "_
  9. Last search pattern register "/

Editing commands (think d, y, and p) can be prefixed with a register to tell vim where to read or write the text you’re working with.

The unnamed register is the default and holds the most recently deleted or yanked text; it’s what’s called upon when you just type p without specifying a register.

Now, have you ever dded something, dded something else, but then realized you really want to p that first thing you deleted?

Up until now, I would u back two steps and re-order my deletes so the text I wanted to p was the one most recently dded.

I should’ve known that vim had a much more powerful way to deal with this. Registers 0 through 9 hold that list of deleted text. In my case I could’ve simply done "1p to put not the most recently dded text (which is "0p, ""p, or just p), but the text one step before that.

The 26 named registers are meant to be used purposely by you to store snippets as you work. Calling them as a vs A simply means replace or append.

Ever wonder how the . command actually works in vim? Yeah me either. Anyway, it’s just the read-only register ". that holds your most recent action. Typing . just tells vim to call it up and execute it.

And finally, the explanation for "*p and "+p, the selection and drop registers. They work just like any other and store the contents of the X11 selection and clipboard. That way, calling "*p simply dumps the register into your buffer.

What’s more, you can use Ctrl-v to highlight a visual block, then type "+y to put that text into your clipboard to go paste it somewhere.

Another neat trick is the last search pattern. You can actually write to that register with what’s known as a let-@ command. That way, if you’re using hlsearch, you can tell vim to highlight words without actually searching for them (and possibly moving your cursor).

:let @/ = "the"

I’ll let you :help yourself regarding the other registers.

07 Nov 2010, tagged with vim

MapToggle

This snippet, when added to one’s ~/.vimrc, allows the toggling of commonly used options (i.e. things like hls or wrap) with a single keypress.

First, you’ll have to define the actual function:

function! MapToggle(key, opt)
  let cmd = ':set '.a:opt.'! \| set '.a:opt."?\<CR>"
  exec 'nnoremap '.a:key.' '.cmd
  exec 'inoremap '.a:key." \<C-O>".cmd
endfunction

command! -nargs=+ MapToggle call MapToggle(<f-args>)

Then, map keys to that function:

MapToggle <F4> foldenable
MapToggle <F5> number
MapToggle <F6> spell
MapToggle <F7> paste
MapToggle <F8> hlsearch
MapToggle <F9> wrap

You’ll even get a nice notification in your vim command prompt when you toggle the setting

09 May 2010, tagged with vim