Vim Hop/EasyMotion: The Power of Single-Char, Multi-Line Motion

f and t Motions

Learning Vim from scratch means learning the hjkl motions first. Then, the w(word) and e (end of word) motions. Eventually, with the f(find) and t(‘till) motions.

fand t are efficient but they have drawbacks:

  • could only be used on the current line
  • if there are non-unique characters, need to press ; repeatedly until you hit the target, or type a number which is non-ergonomic compared to letters.
  • needs F and T for backward motions
  • could have a hit/miss behavior depending on the character sequences

To illustrate, say, we want to go to the start of the word coordinates:

get_converted_coordinates

Here are some of the options when using for t:

  • fc;;
  • 3fc
  • to;;; (useo because cis the character before o)
  • 4to

We couldn’t use w or e here since point_converted and get_converted_coordinates are treated as one word/group.

EasyMotion

vim-easymotion revolutionized the motions in Vim. It adds lots of new motions, and also fixes the limitations of f and t:

  • could be used even beyond the current line
  • if there are non-unique characters, no need to press ; or a number
  • no need to memorize separate keys for backward motions
  • does not have a hit/miss behavior, even for complex character sequences

You could map s as a more powerful, bi-directional f:

Horizontal Motion

Using EasyMotion, you could type this sequence to go to the start of coordinates:

  • scd

sc: search the c character anywhere, which will display the a, s, d occurrences of c (see image below)

d: choose the occurrence of c with the d label.

scd is more convenient to type than fc;; or 3fc. Also, you could go anywhere in the line accurately and consistently with just 3 letters.

Vertical Motion

Using this code sample:

You could jump anywhere. Say, you want to go to the start of def get_foo . You’ll press sd to search for all d characters. It will then display the 3 possibilities:

Then, we choose d. Hence, we jumped precisely to method get_foo by just typing sdd. Suppose we want to copy/edit/delete/indent the method get_foo, we could also use the same motion by finding the end character of the region we want to operate on, which is the ' character in this case. This will give us 4 possibilities:

So, we type s since it’s the last character in method get_foo . The entire operations will be like this:

  • ys's: copy method get_foo
  • ds's: delete method get_foo
  • cs's: change method get_foo
  • >s's: indent method get_foo
  • =s's: align method get_foo

Other options for yanking operation:

  • yap: copy around paragraph, efficient but it’s not always reliable/applicable since there are gaps/empty lines in the method contents most of the time.
  • y4j: copy using downward motion (efficient but typing numbers is not ergonomic, also you need to type 2 digits for bigger code chunks)
  • Vjjjjy: copy using visual lines
  • y/def get_bar<CR>: copy using search (/)
  • ysdea: copy using Sneak in label-mode

I use Sneak before (i.e. searching using 2 chars instead of 1 char), but the annoying part is that it behaves like an f (inclusive) when jumping anywhere but behaves like a t (exclusive) when operating on it. So, you need to inspect/lookahead for the next target when doing operations (yanking, editing, deleting, etc), instead of focusing on the target boundary. It behaves like the native /. And this adds to the cognitive load, besides having to type more characters. However, the EasyMotion single-char search behaves consistently in jumping or operating mode: it’s always inclusive.

EasyMotion is so powerful that it inspired various popular projects as well: Vimium, Vimac, Onivim, VimMotion, etc. It’s in version 3 now and has undergone lots of refactoring/performance improvements throughout the years.

Hop

hop.nvim is a plugin created recently from scratch to modernize the EasyMotion, and to avoid issues with Neovim’s LSP/Treesitter. Hence, for Neovim users, this is now recommended over EasyMotion, and it’s more performant and lightweight. It has a single-char search mode also (via HopChar1):

With similar mapping to that of EasyMotion, and with the same examples above, you’ll have the same keystrokes. Need to add the omap, so that it will have inclusive behavior similar to that of EasyMotion when in operating mode, and need to use [<cmd>](https://github.com/phaazon/hop.nvim/issues/89#issuecomment-854648965) instead of the usual : so that the visual mode expansion will work as intended. The additional omap mapping is needed since there’s still ongoing discussion if both the f and t-like behavior must be supported in HopChar1 via configuration. Nevertheless, I use this plugin extensively everyday.

Customization

Hop offers some customization too. I use a Colemak keyboard layout, so I prefer the letters on the Home row first for better ergonomics. Likewise, you could set the color highlights. I use One Dark color scheme in my editor and terminal, so I use similar color in Hop for seamless blending.

Operating on Huge Blocks

What if you want to copy the entire contents of the class? Assuming you’re in the get_foo method, you could go to the start of the class definition by searching (s) for c character, then typing the a hint, that is, just typing sca in all:

This also demonstrates that you could go in backward motion with no extra mapping to memorize. After arriving at the target, you could now search for the last character of the class body which is ' with the g hint, which means just typing ys'g in all. This will now copy the entire class contents.

Single-char search is very powerful for working on arbitrary regions, but for known/predefined text objects, like inner indent, it’s more efficient to use the vim-indent-object. Assuming you’re in the get_foo method, you could do these:

  • yii: copy the similarly indented lines (e.g. all the methods of a class)
  • yai: copy the similarly indented lines and the line above it (e.g. the class name and all its methods)

Vim Indent Object is not efficient for isolated/smaller chunks though. That’s why I use both the Hop and Vim Indent Object plugins depending on the use case.

Key Takeaways

  • Single-char search is a potent motion, you could go anywhere/do anything with less keystrokes. You might want to still use f, t, w, and e though for short movements in the same line.
  • Using EasyMotion (Vim) or Hop (Neovim) makes it possible to do single-char search horizontally and vertically. These plugins have other powerful features but I only use/love the single-char motion.
  • Single-char search is better than using Sneak due to fewer keystrokes and inclusive operation.
  • Vim Indent Object is better than EasyMotion/Hop for operating on some known text objects involving indentation. Most of the time, you’ll work on arbitrary text objects though in which the EasyMotion/Hop really shines. And you couldn’t use Vim Indent Object for jumping.

Thank you for reading. If you found some value, kindly follow me, or give a reaction/comment on the article, or buy me a coffee. This will mean a lot to me, and encourage me to create more content.