DEV Community

Amin
Amin

Posted on

Thinking in motion — Neovim From Scratch

This episode is part of a serie called Neovim From Scratch where I show you the basics of how to use Neovim (and Vim) when editing text.

The NORMAL mode is the default mode you enter into once you start Neovim.

Once you read this article, you'll be able to confidently open up Neovim and use the whole keyboard as your playground to move like a ninja in your files with ease.

First off, let's start a brand new buffer by starting up Neovim.

$ nvim
Enter fullscreen mode Exit fullscreen mode

In this mode, we will change the mode into INSERT by pressing the i key.

Once that is done, we can start typing any text we want. Go and copy this text if you would like to follow along.

function sayHello(firstname, lastname) {
  if (firstname === undefined) {
    console.log("Hello, stranger!");
  } else if (lastname === undefined) {
    console.log(`Hello, ${firstname}!`);
  } else {
    console.log(`Hello, ${firstname} ${lastname}!`);
  }
}
Enter fullscreen mode Exit fullscreen mode

That's right! That is JavaScript code. I pick this language because it has several construct in its syntax and it will be interesting to see how you can move inside this piece of code that has the most widely used C-like syntax that most developers will be using in a day-to-day basis.

Of course, everything we see here can be applied to any language, or even text if you are a non-developer reading this article!

The basic movements that we can use and that everyone knows if you were using a text editor like VSCode is the arrow keys.

You can use them to move, whether you are in INSERT or NORMAL mode.

This is useful because it will be a first-step into the world of modal editing and movements.

In reality, if you really wish to move inside your editor faster, you should rather use the hjkl keys.

I'm pretty sure you've heard about this weird key combinations. These are the traditional keybinds that were introduced in Vi, later on in Vim and now in Neovim, that allow you to move in any of the direction you can using the arrow keys.

Here is a copmlete break down of these keys.

             Up
              |
              |
              v
Left ---> h j k l <--- Right
            ^
            |
            |
          Down
Enter fullscreen mode Exit fullscreen mode

Don't be afraid to use these keys!

Also, at first, you'll start to think about where you want to go and these key combinations will seem like fighting against your code just to move left, right, up or down.

It's okay to take some time to learn them. It took me a full week before starting to use them extensively.

And if you think they are too strange to use, you can always fallback to use the arrow keys anyway, there is no obligation!

Now for the question you might be asking: why on earth should I use these weird keys instead of the arrow keys?

Every letter of the keyboard is bound to a special command in Neovim. You'll start to realise soon that hitting a key combination like finding a character, or going into INSERT mode after moving to the end of a line, and then using the arrow keys back into NORMAL mode is a waste of time and energy because your hand has to do a full move to the left of your keyboard in order to reach those keys.

Plus, you risk having to look at your keyboard, losing some time too and some traction that you gained by being used to not look at your keyboard using the letter keys.

Historically, some keyboard didn't even have arrow keys! But it was way back then in a time were you and I were not born yet, but interesting fact!

Try to move inside the code snippet above. Try to reach an argument, then the second call to console.log and back to the function name. Don't hesite to use the i key in order to append some changes. Maybe you want to delete an argument, or add another one. You are encouraged to!

And if you mess up the code a little bit, no worries: everything we are doing here is inside a buffer, so no risk of messing up with an important file in your operating system. Remember that the buffer is living inside the memory of Neovim, not in your file system yet!

These key combinatios are keys to move around your files. But it would not be interesting if you could move just like your arrow keys. In fact, there is way more ways to move in a more efficient manner using Neovim and that is why I took this example to show you that you can be moving around a code like this more efficiently than in any text editor you've used before!

If you go back into NORMAL mode, there is a special key that you can use in order to move faster. For now, the l key was used to move from one character to another.

Now imagine having a super long text, wouldn't it be nice to be moving from words to words? You can do it using the w key.

What's cool about Neovim is that it is using keys that are easy to remember, so that you don't start remembering by heart every combinations, but rather its meaining. Here the w key stands for word so you can easily associate it in your head to remember it the next time you need it.

For instance, if you want to move from word to word inside the code above, your cursor would go in these specific places that I'll highlight for you below.

                              Here
                  Here         |
                   | Here      |
         Here      | |         |
Here      |        | |         |
|         |        | |         |
+function +sayHello+(+firstname+, lastname) {
  if (firstname === undefined) {
    console.log("Hello, stranger!");
  } else if (lastname === undefined) {
    console.log(`Hello, ${firstname}!`);
  } else {
    console.log(`Hello, ${firstname} ${lastname}!`);
  }
}
Enter fullscreen mode Exit fullscreen mode

I didn't highlight everything because I think you'll grasp the concept, and I encourage you to try it yourself so that you get a real feel of what this w key will do in NORMAL mode.

As you can see above, it moves the cursor from word to word. But what strange is that there is also non-word (special characters, or symbols) that are stopping the cursor whe using the w key.

This is because the w key will move to the next word, or symbol as well. It's like splitting the line into an array of words and symbols.

["function", "sayHello", "(", "firstname", ","]
Enter fullscreen mode Exit fullscreen mode

This is what the editor sees whenever you are using the w key.

If you trully want to move from non-whitespace word to another, there is another alternative using the W key. Remember, Neovim's keybinds are case-sensitive, so the w is different than the W keybind.

By using the W key, this is what we would be achieving if we were to use this inside this code.

                              Here
         Here                  |
Here      |                    |        Here
|         |                    |          |
+function +sayHello(firstname, +lastname) +{
  if (firstname === undefined) {
    console.log("Hello, stranger!");
  } else if (lastname === undefined) {
    console.log(`Hello, ${firstname}!`);
  } else {
    console.log(`Hello, ${firstname} ${lastname}!`);
  }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, there is a slight difference between w and W, so use them according to your needs. And if you don't know which to pick, use w since it is less abrupt than W. With time and experience, you'll start to want a faster way of moving around and you'll use the W seemlessly without even thinking about it.

The same thing applies for both the b and B keybinds. It allows you to essentially do the same, but in the opposite direction. Once you have move around using the w and W keys, try the other way around using b and B!

Now, lets's say that this function is asynchronous and that we needed to append the word async just behind the word function.

You could use the B key to quickly jump around to the first character of the line, and append the word async using the i key there if you were at the end of the first line.

In the same fashion, the e key will allow you to move to the end of a word (that is a way of remembering this keybind).

So if you want a visual representation, here it is.

                                      Here
                                  Here |
                         Here       || |
               Here       ||        || |
      Here      ||        ||        || |
       |        ||        ||        || |
       v        vv        vv        vv v
function sayHello(firstname, lastname) {
  if (firstname === undefined) {
    console.log("Hello, stranger!");
  } else if (lastname === undefined) {
    console.log(`Hello, ${firstname}!`);
  } else {
    console.log(`Hello, ${firstname} ${lastname}!`);
  }
}
Enter fullscreen mode Exit fullscreen mode

The double-pipes are no mistakes, this is actually where your cursor would go if you use the e keybind. In the same way as the w keybind, it will account for symbols.

If you instead wish to move from non-whitespace charcters to another, you can use the uppercased version E.

                                      Here
                                  Here |
                         Here        | |
                           |         | |
      Here                 |         | |
       |                   |         | |
       v                   v         v v
function sayHello(firstname, lastname) {
  if (firstname === undefined) {
    console.log("Hello, stranger!");
  } else if (lastname === undefined) {
    console.log(`Hello, ${firstname}!`);
  } else {
    console.log(`Hello, ${firstname} ${lastname}!`);
  }
}
Enter fullscreen mode Exit fullscreen mode

If you want to go from the beginning of the line to the end, you could use the arrow keys, or the l key to go to the very end of a line.

You can use the $ key in order to move forward to the end of a line.

This would move your character from the beginning of the line to the end as show below.

                                      Here
                                       |
                                       |
                                       v
function sayHello(firstname, lastname) {
  if (firstname === undefined) {                 
    console.log("Hello, stranger!");
  } else if (lastname === undefined) {
    console.log(`Hello, ${firstname}!`);
  } else {
    console.log(`Hello, ${firstname} ${lastname}!`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Note that if there are any blank characters, your cursor will move to those that are at the end of the line.

Now, let's say your wish was to go to the beginning of the line instead, you can use the 0 key. This one is easier to remember than the $ key since 0 is somehow the start of a number (if we forget signed numbers).

This would move your cursor as show below.

Here
|
|
v
function sayHello(firstname, lastname) {
  if (firstname === undefined) {
    console.log("Hello, stranger!");
  } else if (lastname === undefined) {
    console.log(`Hello, ${firstname}!`);
  } else {
    console.log(`Hello, ${firstname} ${lastname}!`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Interesting thing to note here is that if you use the 0 key for the line below the one that we are currently at, it will move to the very first character.

Here
|
|
|
+ if (firstname === undefined) {
Enter fullscreen mode Exit fullscreen mode

This is not very easy to explain using a code snippet so I hope that you understand that those space I added before the lines are simply here to allow for a gutter for this schema!

Again, I encourage you to try it so that it feels more natural than reading some text.

Moving to the beginning of a line can be quite interesting, but if we want to add something just before the if statement, we could instead use the ^ key in order to move to the first non-whitespace character of a line.

 Here
  |
  |
  v
  if (firstname === undefined) {
Enter fullscreen mode Exit fullscreen mode

Instead of moving to the first character (being the whitespace that is our indentation for this code), we moved right into the i letter of our if statement. So now it is easy to add something right before it.

Now, let's say we want to add something else to our condition on this line.

We could move just before the end paren by using the l key, or even better using the w key. This would work, but there is an even better way of doing it that will unlock a whole new world of movements for you.

You can use the t key. Think about it as the unTil keybind.

This key will accept a single character, and will move your cursor right before the character it has found. The first matching character is used, so take your time thinking about it if you need to.

So, if we were at the beginning of our if statement, our cursor would move as noted below.

                           Here
                            |
                            |
                            v
  if (firstname === undefined) {
Enter fullscreen mode Exit fullscreen mode

That way, we can easily go into INSERT mode pressing the i key and add something else to our condition! No more pressing the arrow key for ages like before.

Now, instead if you wish for removing this pair of parens because you forgot you were using Rust and that Rust allows for omiting the parens, you could use the f instead. Think about it as the Find keybind.

This keybind will allow you to do the same as the t key, except it will move your cursor right in the letter that you are searching.

So instead of using t, if we used the f keybind and provided the ) character, our cursor would move as shown below (if we start from the beginning or just before this character).

                            Here
                             |
                             |
                             v
  if (firstname === undefined) {
Enter fullscreen mode Exit fullscreen mode

This is a slight move, but you'll find it pretty useful once you start getting the hang of those keybinds.

Again, the t and f accept any key from your keyboard. If those keybind don't find any matching character, it will simply do nothing. No errors. You can still configure Neovim to tell you whenever these keybinds didn't find anything, but I find it not very useful, especially if you move around fast and you know what character are after your cursor.

As for many keys, these keys accept an uppercase version that will smiply do the opposite, meaning they will search for a character before the cursor, namely T and F.

Take a look around and try to move using those keybinds!

That's it! There were a lot of informations, because there is a plethora of ways to move inside of a Neovim buffer, but once you start to master those keybinds for moving around, you'll start to experience a new way of thinking about your motions inside of a file.

In the next episode, we'll talk about ways of going into INSERT mode using several others movements that will be pretty useful in order to move fast!

See you!

Top comments (0)