Vim Best Practices For IDE Users Programming
Vim Best Practices For IDE Users Programming
If you don’t have time to read the Vim User Manual, I sifted through it
for you with a focus on IDE users.
1.5K 16
Introduction
Vim is more than just a text editor. It’s a powerful tool that can boost your
productivity and creativity.
You’ve probably heard the endless debates about Vim vs NeoVim, Vim vs
Emacs, Vim vs IDEs, and editors vs IDEs. You’ve also probably wondered if
you should customize Vim to act like an IDE or your IDE to act like Vim.
What if I told you that modern IDEs can emulate most of the Vim features
you love, even some advanced ones? In this guide I’ll show you 90 tips to get
familiar with Vim, focusing on Vim emulation on popular IDEs like VS Code
and IntelliJ IDEA.
You’ll see that only a few of the frequently used features are not supported,
and I’ll point them out for you. By the end, you’ll have a better idea of what
Vim emulation can do, and you can use this knowledge to decide which
environment is best for you.
If you are experienced with Vim, you can skip ahead and skim through the
headlines. If something piques your interest, feel free to deep dive.
Resources
Other resources that you might want to keep in mind include:
Vim tutor. An official 30-minute interactive tutorial built into Vim itself.
(Now it’s built into the JetBrains Vim plugin too.)
If you are using VSCode, there are two options: The first is the standard Vim
plugin, which is very popular these days. The second is vscode-neovim,
which creates a Vim mode by embedding NeoVim itself. Note that the
standard Vim plugin also has an experimental embedding of NeoVim to
enable some features.
It is worth noting that the JetBrains team has also considered embedding
NeoVim, but it is not likely to happen in the near future, and they have their
reasons.
In addition, many users praise the Vim plugin for Visual Studio, but I do not
use that IDE myself, so I will not include it in this guide.
Maybe you were wondering what those funny symbols on the edge of your
screen are when you open Vim on a terminal.
They are useful to differentiate empty lines from the abyss after the file
ends. Sure, IDEs use line numbers to convey the same information, but Vim
is classy enough to predate innovations like “line numbers are on by
default.”
Most GUI editors are what is called “modeless,” in which all commands can
be performed using the same set of keys at all times. They provide a more
intuitive and user-friendly interface, especially when paired with
innovations such as the mouse.
However, advanced users claim that modal is faster and more powerful, even
if there is a certain delay introduced by changing mode.
In Normal mode the characters you type are commands. In Insert mode the
characters are inserted as text.
Vim starts in Normal mode. There are several ways to go to Insert mode, the
most common is the i command (i for Insert). Then you can enter text. To
go back to Normal mode (no matter what mode you are in), press <Esc> .
Some users find that the <Esc> key can be difficult to reach, so they remap it
to a more accessible key like “Caps Lock.” I use a keyboard with a thumb
cluster, so I have my <Esc> key there.
The normal in other GUI editors is that you can enter text whenever you
want. But in Vim, “normal” is typing commands. Welcome to the new
normal.
To better tell that you are in Insert mode, Vim displays an --Insert-- text at
the bottom of the screen and changes the look of the caret. The IDEs display
it too.
Ah yes, the dreaded “hjkl” is a true test of courage. It’s the way to tell adults
from kids in the Vim world.
Historically, this comes from the keyboard Bill Joy was using when he
created “vi”, the predecessor of Vim in 1976:
ADM-3A keyboard (image credit vintagecomputer.ca)
The modern arrow keys are also available for Vim, but a lot of people think
that the “hjkl” keys are more accessible for touch typing, as you don’t have to
move the hands from the “home row.”
This is also true for non Qwerty users: Most people agree that the default
locations of the “hjkl” key are also comfortable in both Colemak and Dvorak.
Are you still on Qwerty and don’t touch type? Consider upgrading your
keyboard skills with this guide:
In modern times, you’ll probably see this as Ctrl + A , but Vim likes to notate
it as CTRL-A .
In Vim, the case of {char} is ignored; thus CTRL-A and CTRL-a are equivalent.
Additionally, Vim has a U (undo line) command to undo all changes made to
a line in one step. Note that the U command is a change by itself, which can
be undone and redone with u and CTRL-R .
IDEs on Vim mode allow you to use either u / CTRL-R or CTRL-Z / CTRL-SHIFT-Z .
It’s up to you to go “full Vim” or to keep using familiar shortcuts. In this case,
I prefer the Vim way because the u key is very ergonomic.
Some VSCode users have encountered problems with Vim’s undo stack being
out of sync with the IDE’s stack.
But often the commands using the same character are related. In those cases
there’s no rule of thumb, but you can think of the lowercase one as the
“obvious” command, and the capitalised one can be either an “augmented”
version ( a and A) or a “reverse” version ( o and O ).
The g-prefixed commands tend to be the ones you use less.
All good things must come to an end, including the “I can’t exit Vim” meme.
To exit, use the ZZ command. This command writes the file and exits.
If you want to throw the changes but continue editing the file, the :e!
There’s also ZQ , which does the same as :q! . Pick your poison!
On IDEs, these commands won’t allow you to close the IDE itself. They just
attempt to close the current file.
IDEs tend a lot more towards constant auto-save, so these commands are
more permissive than their terminal Vim counterparts because you are
more likely to find that your changes are already saved when you try to close
a file.
On a JetBrains IDE, :help will show the help on a browser, but the
implementation is inconvenient because it lands on a search page. Currently,
the VSCode Vim plugin doesn’t support :help .
For now, I recommend opening Vim on the terminal if you just want to use
:help . Or you can go to vimhelp.org.
If any of these commands reach the end of the line, it will move into the next
or previous line accordingly.
The commands on the previous section work on “words,” soon we’ll see
other commands that work on uppercase “WORDS”
But what is the difference between the two? A good rule of thumb is “word
until punctuation, WORD until whitespace.” But if you want the gory
technical details, read on:
A word is:
A sequence of letters, digits, underscores, and some special ASCII
characters (as specified in the default value of the iskeyword option:
@,48–57,_,192–255 ).
Or an empty line.
A WORD is:
Or an empty line.
For example, the line abc@#$ def%^& contains 4 words and 2 WORDS:
Sometimes you will want to include a dash in keywords, so that commands
like w consider “upper-case” to be one word. You can do it by modifying the
isKeyword option like this:
:set iskeyword+=-
16. Moving to the end or start of a line: “$”, “^” and “0”
The $ command moves the cursor to the end of a line.
The ^ command moves to the first non-blank character of the line. The 0
For some mysterious reason, the 0 and ^ commands don’t take a count. No
one knows why. I guess this is the kind of bizarre behavior that you have to
expect when using a 50 year old editor. In any case, most people don’t even
use the $ count.
Any of these four commands can be repeated with ;. , repeats the search
in the other direction.
Now, if you are not on a parenthesis or bracket, pressing % will look for the
next parenthesis forward on the current line, and try to jump to it’s match.
This behaviour is a bit quirky for my tastes, so I try to only press % when I’m
on top of a parenthesis already.
With a count, the G command takes you to a specific line. 17G takes you to
line 17.
Another way to move to a line is using the % command with a count. For
example 50% moves you to halfway the file.
To navigate to the top, middle, or bottom of the visible screen, press H (high),
M (middle) or L (low). By default there is a scroll offset setting that prevents
the cursor from reaching the very edge of the screen. I like this default, but
you can change it if you want. On JetBrains you can change the scrolloff
option directly in the .ideavimrc file, which we will discuss later on.
20. Telling where you are: the mystical power of line numbers.
Showing the line number in your editor? Are innovations like this really
possible in our day and age?
They sure are, you can enable them by running the command :set number ,
If you miss your prehistoric coding experience, just run :set nonumber . Since
“number” is a boolean option, prepending “no” to its name switches it off.
Some advanced users like to set the option :set relativenumber , which
shows the line numbers relative to the line that has the cursor. This is useful
for vertical navigation with j and k preceded by a count: You can just type
the relative number as the count. Personally, I prefer other ways to navigate
vertically, as I’ll show you later.
Again, on IDEs you don’t have to worry about :set number because line
numbers are on by default. Both IDEs support :set relativenumber .
(backwards) and CTRL-F (forwards). But I don’t recommend these last two,
they are pretty dizzying in practice.
22. Aligning the cursor with the viewport: “zz”, “zt”, “zb”
Sometimes your cursor is close to the bottom of your viewport but you want
it on the center. Easy, just press zz . Your cursor will remain on the same line
but the viewport itself will scroll so that the line with the cursor is at the
center of the screen.
Similarly, the zt command puts the cursor line at the top, and zb at the
bottom.
To search for a string, use the / command. If you want to find the word
“include”, for example, type /include , then press <Enter> to execute the
command.
To find the next occurrence of the same string use the n command.
Search has a built-in history that works just like a shell history. If you press
<Up> on the middle of typing a search, you will land on the previous search
that matches what you typed so far.
24. Searching for the word under your cursor: “*”, “#”
If you want to search for the next occurrence of the word under your cursor,
press * . To search for it backwards, do #.
25. Searching for whole words
If you type /the it will also match “there”. To only find words that end
in “the” use /the\> .
The \> item is a special marker that only matches at the end of a word.
Similarly \< only matches at the beginning of a word. Thus to search for
the word “the” only, use /\<the\> .
Notice that the * and # commands use these start-of-word and end-of-word
markers to only find whole words.
The annoying thing here is that the highlight remains even after you are
done with the search. But you get used to it. It’s no big deal.
And make sure you have the option :set incsearch on, which will highlight
the results while you are still typing the search. No one can live without this
amenity.
At this point, you might be wondering why you should use the Vim search
instead of your built-in IDE search. Well, Vim search can be faster because of
its modal shortcuts, but the main advantage is that Vim search is a Vim
motion, and motions enable advanced changes, as we’ll see in the next
section.
Now that you have a lot of options that you like, it might be a good idea to
write them into a .vimrc file so that they load every time you start Vim.
If you are using Vim mode on a JetBrains editor, it comes with it’s own
.ideavimrc . It creates it for you in your home directory.
If you are using Vim mode on VSCode, you need to change the settings
manually, but there’s experimental support for a .vimrc file too.
If you are familiar with regular expressions, you’ll find that they work pretty
much as you would expect.
For instance, the ^ character matches the beginning of a line, and the $
character matches the end of a line. The . (dot) character matches any
character.
Keep in mind that, because of the regular expression support, the characters
.*[]^%/\?~$ have special meanings. If you want to use them in a normal
search you must “escape” them by putting a \ in front of them.
If you use the same command a second time you will jump back again. That
is because the ` command is a jump on itself.
Generally, every time you do a command that can move the cursor further
than within the same line, this is called a “jump”. j and k are not
considered to be a jump, even when you use a count to make them move the
cursor quite a long way away.
Pressing `` repeatedly will bounce you back and forth between two single
positions.
The CTRL-O command jumps to older positions (Hint: O for older). CTRL-I
then jumps back to newer positions (Hint: for many common keyboard
layouts, I is just next to O). These commands even take you across files.
marks the place under the cursor as mark “a”. You can place 26 marks (a
through z).
You can’t see them, it’s just a position that Vim remembers. Remember, Vim
predates innovations like visual cues. In this case though, their invisibility is
a practical delight, as setting invisible marks and using them with ease and
grace in your complex navigations allows you to revel in your Vim skills.
If you insist on visualizing marks, there are Vim plugins like vim-signature.
Later we’ll see that IDEs have some support for visualizing marks too.
Alternatively, the command ‘{mark} moves you to the beginning of the line
containing the mark. This differs from the `{mark} command, which also
moves you to the marked column.
[ Start of the last change, and ] end of the last change. (You can use
these to go to the edges after a large insert.)
“ The cursor position when last exiting the current file. (A useless mark.
You would use it to resume work when reopening a file, but every IDE —
and even Vim — does this for you automatically, so don’t bother)
You see, there’s an alternative way to move in Vim that’s very popular, and
that’s the EasyMotion plugin.
This plugin is a bit old these days, and a lot of people prefer descendant
plugins like vim-sneak and leap.nvim, but they all operate on similar
principles.
The basic idea is that, when you want to navigate somewhere, you type the
key to activate “EasyMotion”, and then you type the characters on the screen
that you want to jump into. If more then one destination is possible, you are
prompted to type an automatically generated label that uniquely identifies
your target.
It is my opinion that this is the single best way to navigate on text files using
your keyboard, even if purist would like to see me six feet underground for
this statement.
And I’m not the only one, as both Vim modes in JetBrains editors and
VSCode support this. I heavily encourage you to enable it and never look
back.
You might wonder, which key should you use to activate “EasyMotion”? It’s
up to you, but the acclaimed author of leap.nvim suggests that the s key is a
reasonable choice for this kind of motion, even if you lose the “s = cl”
shortcut, which we’ll see in the next section.
The 4w command, for example, moves the cursor over four words. The d4w
If you use the “e” command to move to the end of a word, Vim guesses that
you do want to include that last character in the deletion.
The reference manual calls this “exclusive” when the character isn’t included
and “inclusive” when it is.
command deletes from the cursor to the end of the line. This is an inclusive
motion.
2w move two words (they are deleted and Insert mode started)
You will have noticed something strange: The space before “human” isn’t
deleted. The c operator works just like the d operator, with one exception:
cw . It actually works like ce , change to end of word. This is an exception
that dates back to the old Vi. Since many people are used to it now, the
inconsistency has remained in Vim.
35. More changes: “cc”, “x”, “X”, “D”, “C”, “s”, “S”
Like dd deletes a whole line, cc changes a whole line.
Just like d$ deletes until the end of the line, c$ changes until the end
of the line.
Some operator-motion commands are used so often that they have been
given a single-letter shortcut:
x stands for dl (delete character under the cursor)
You could do the same with cl or s , but with r you don’t have to press
<Esc> to get back out of insert mode.
The . command works for all changes you make, except for u (undo), CTRL-
You start Visual mode by pressing v . You move the cursor over the text you
want to work on. While you do this, the text is highlighted (using the full
power of your GPU). Finally type the operator command.
For example, to delete from the middle of one word to the middle of another
word:
Open in app
1
Search Write
If at any point you change your mind, press <Esc> and you’ll leave Visual
mode.
Actually, this mode is quite good for inserting text on several lines
simultaneously. In a sense, it’s a precursor of multi-caret editing. We’ll show
you how it works later.
When using blockwise selection, you have four corners. o only takes you to
one of the other corners, diagonally. Use O to move to the other corner in
the same line.
If you delete an entire line, the p command puts the text line below
the cursor. If you delete part of a line (a word, for instance), the p
There is an easier way: yanking. The y operator copies or “yanks” text into a
“register”. Then a p command can be used to put it. We’ll take a deeper look
into registers later on, but you can think of them as “places to keep stuff”.
Yanking is just a Vim name for copying. The c letter was already used for
the change operator, and y was still available. Calling this operator “yank”
made it easier to remember to use the y key.
The yy command yanks a whole line, just like dd deletes a whole line.
And remember, for the love of God, please enable the “yank highlighting”
plugin, which allows you to visualize the thing that you just yanked. This is
vital for getting a hang of the yank. Both VSCode and JetBrains editors
support it.
What is this weird "* syntax? Well, as we said, Vim has registers. The
general syntax to get the content from a specific register is "a , where a is
the register symbol. In this case, star ( * ) is the symbol for the “selection
register,” a special register that’s connected to the system clipboard.
If the cursor is in the middle of a word and you want to delete that word,
usually you need to move back to its start before you can do dw .
stands for “A Word”. Thus daw is “Delete A Word”. To be precise, the white
space after the word is also deleted (or the white space before the word if at
the end of the line).
Using text objects is the third way to make changes in Vim. We already had
“operator-motion” and “Visual mode”. Now we add “operator-text object”.
The magic is that it doesn’t matter where in the object the cursor
is. As long is it’s somewhere inside of it, we can select it as a text object.
You can also use text objects in Visual mode. It will include the text object in
the Visual selection. Visual mode continues, thus you can do this several
times.
You can find a long list of text objects here. Check it out! There’s a lot of
really useful text objects, like blocks of all kinds ( () , {} , [] , <> ) and quoted
strings. For example, you can use di( to delete everything inside a pair of
parentheses, and da" to delete everything in a double-quoted string,
including the quotes.
You might be wondering what’s the meaning of “rc” at the end of so many
configuration files. Well, it’s an ancient Unix convention with an even older
origin: The “RUNCOM” program, a predecessor of the “shell” created in 1963 for
an operating system you never heard of, CTSS.
In any case, vimrc is the place to put all your favorite options and mappings.
The vimrc file can contain any of the commands that you type on Command-
line mode. Vim will automatically execute the vimrc commands when it
starts.
" This is an example vimrc file.
" You can write comments by starting a line with double quotes (")
In JetBrains editors, there is a very capable .ideavimrc file in which you can
add both options and mappings. This file is placed in your home directory,
but you can edit it quickly with a button on the status bar. Also, .ideavimrc
comes pre-loaded with some options taken directly from Vim’s official
defaults.vim , a file with nice improvements for people who don’t care about
backward compatibility with vi.
Neither VSCode nor JetBrains editors support this feature, although there’s
an open issue for VSCode. But to be fair, I think this IDE-like behavior is
better served by an IDE feature that preserves the local history of your files:
On JetBrains this is called “Local History”, and on VSCode it’s called
“Timeline View”.
But if you don’t use the original Vim at all, you don’t need to worry about this.
This is the magic of extensible tools for power users: As long as you can
think in the tool’s language, you can extend it. No one man should have all
that power, but here we are.
Suppose, for example, that you need to surround certain words with curly
braces. With the :map command, you can tell Vim to do this when pressing
F5:
After you execute the :map command, all you have to do to put {} around a
word is to put the cursor on the first character and press F5.
In this example, the trigger is a single key, but it can be any string. Keep in
mind that when you use an existing Vim command, that command will no
longer be available. One key that can be used without conflicts is the
backslash ( \ ). You can follow it with other characters:
:map \p i(<Esc>ea)<Esc>
:map \c i{<Esc>ea}<Esc>
The :map command (with no arguments) lists your current mappings for
normal mode. The command with no arguments is supported by JetBrains
but not yet by VSCode.
Instead of dealing with plugins and packages directly, many Vim and Neovim
users prefer to use package managers like vim-plug and lazy.nvim.
The plugin ecosystems and communities of both Vim and NeoVim are strong
and active.
For Vim mode, the situation is much simpler as fewer plugins are available.
In JetBrains, you can simply check the list of available plugins and follow the
instructions for each one. Same for VSCode.
You can check the full alphabetical list of options in the documentation.
Lord have mercy.
In any case, expect only some options to be useful to you, and many won’t
work on Vim mode.
But Vim is not like that. The cursor just stays there. To fix it, do this:
set whichwrap=b,s,<,>,h,l,[,]
:set iskeyword&
To check what’s the current value of an option, append a question mark: :set
Here’s a funny quote from the user manual: “Displaying text in color takes a
lot of effort. If you find the displaying too slow, you might want to disable
syntax highlighting for a moment.” Lol, I’m so glad to live in the future.
Yet, there are some nice developments in the field of efficient syntax
highlighting: cutting-edge Vim users use tree-sitter, which provides state-of-
the-art syntax highlighting. And it’s made in Rust, which makes it cool.
You might be interested to know that many people create and share different
color schemes for Vim and NeoVim. Some of these are so beautiful that they
make you forget you’re using a terminal:
Source.
Another funny tidbit from this section of the docs is :TOhhtml , a weirdly
capitalized command that turns whatever file you open into an HTML file
with syntax highlighting. You could use it to share code with others, but I
have no idea what’s a reasonable use case for it.
59. Vim file commands that you can’t use in Vim mode
Vim has a lot of commands for working with multiple files and switching
between them: :edit , :find , :hide , :next , :previous , :args , :first , :last
and :saveas .
No Vim mode fully supports these commands. And frankly, they don’t make
much sense on IDEs anyway. At least, they won’t be practical until the IDEs
implement related features like filename tab completion.
Naturally, your IDE already supports working with multiple files, usually
through more sensible shortcuts and GUIs.
That works within one file. Each file has its own set of marks, they are local
to the file.
There are also marks with an uppercase letter. These are global, and they
can be jumped to from any file.
It’s often useful to think of a simple connection between the mark letter and
where it is placed. As a boomer example, you can use the H mark in a header
file, M in a Makefile, and C in a C code file.
VSCode shows the marks on the gutter by enabling the “Show Marks in
Gutter” setting.
JetBrains shows the global marks on the gutter as they are integrated
with the IDE’s “Mnemonic Bookmarks” feature, which is basically the
same feature. There’s an issue to also show the lowercase marks on the
gutter.
Here we will use the registers named a to z (but just like Skywalker, soon
you’ll find out there are others). To copy a sentence to the y register, type
“fyas . The yas command yanks a sentence like before (yas queen!). It’s the
“f that tells Vim to place the text in the f register.
You can then paste from a specific register with “fp . The text stays in the
register until you yank something else into it, so you can put it in as many
times as you desire.
As a reminder, the star ( * ) register is a special register that allows to yank
and put to the system clipboard (by doing "* ).
register.
The :registers command shows you the text on every register. This works
on both VSCode and JetBrains.
Most editors can be split into different rectangular regions. On Vim, these
regions are called “windows,” a term that should not be confused with the
concept of overlapping windows on modern GUIs like operating systems.
To open a new window, you can use the :split command (or CTRL-W s ),
which splits the screen horizontally into two windows showing the same file.
To split them vertically, use :vsplit or CTRL-W v .
CTRL-W w can be used to jump between all the open windows. It wraps
around once it reaches the last window.
You’ll notice that all the window commands on Vim have the form CTRL-W
{char} , in which you must press CTRL-W first and then another character.
( CTRL-W CTRL-{char} does the same thing, in case you let go of the CTRL key a
bit later.)
The :only command (or CTRL-W o ) closes all windows except the current
one.
For any tmux user out there: Mouse resizing also works on tmux, but you
need to enable it.
If what you want to do is move the windows themselves because they are out
of order, do CTRL-W followed by the uppercase version of the navigation keys
(H, J , K , L ). Unfortunately, this is not supported in VSCode or JetBrains.
Still, it’s worth remembering the CLI command vimdiff (or nvim -d for
NeoVim) if you want to quickly see the diff between two files in your
terminal. It’s better than the colorless diff command.
But Vim does the opposite — It has tabs that contain splits (aka windows):
This is just a design difference. There’s no one correct way to design the
layout of an editor. Both have advantages and disadvantages.
In fact, Vim only visually doesn’t have tabs in each window — Vim users can
still switch what file each window is displaying, and they usually don’t care
about amenities like having a tab bar in each split.
You actually have more freedom in Vim because a file doesn’t belong to
some window; they’re entirely independent. Any window can display any
file.
The Vim tab system is not really a different or opposite way of doing it. It’s an
additive feature on top of the usual windows: You can think of Vim tabs as
different layouts (arrangements of windows) for different contexts. In fact a
similar feature is called “Layouts” on JetBrains.
This diagram shows how Vim users think about their editing environment. It
shows the distinction between layout elements (tabs and windows) and files
(aka buffers):
Naturally, Vim mode plugins don’t support Vim-style tabs as they just don’t
exist in IDEs. Still, the following Vim commands for tabs have been
repurposed by both VSCode and JetBrains to work on the IDE-style tabs:
gt Goes to the next tab. If preceded by a number, it goes to the tab with
that number. (The first tab is 1, not 0.)
If you want to close a tab, use any of the Vim commands that close files,
because IDE tabs are basically just files.
As an IDE user, you are even deeper into the GUI life, so this information
won’t affect you except for the satisfaction of knowing that even an old-
school editor like Vim is flirting with the powers of graphics and non-
monospaced fonts.
Select mode is like Visual mode, because it is also used to select text. But
there is an obvious difference: When typing text, the selected text is deleted
and the typed text replaces it.
You need to enable it to use it, but it’s enable by default on Windows when
trying to select with the mouse.
That’s where macros, or “command recording,” come in. There are three
steps:
You can now execute the macro by typing the command @{register} .
Let’s look at an example. You have a list of filenames that look like this:
stdio.h
fcntl.h
unistd.h
stdlib.h
#include "stdio.h"
#include "fcntl.h"
#include "unistd.h"
#include "stdlib.h"
Easy, just type these commands:
5. a”<Esc> — Append the double quotation mark ( “ ) to the end of the line.
Now that you have a macro in your a register, it’s time to run it by typing the
command @a three times. The @a command can be preceded by a count, so
in this case you can type 3@a .
If you have done @a once, you can run that macro again with @@ . That’s a bit
easier to type. If you now execute register b with @b , the next @@ will use
register b.
By the way, macros are recursive: You can put macros inside of macros (even
if it’s the same macro).
“np — Put the text from the n register. You now see the commands you
typed as text in the file.
{edits} — Change the commands that were wrong. This is just like
editing text.
This trick is a great example of the Unix and Vim philosophies: If you know
how to modify text, and if everything is made out of text, then you can do
anything.
Suppose you have recorded a macro in register c. It works properly, but you
would like to add a search for a word. This can be done with qC , which
appends to the c register: qC/word<Enter>q .
This works with macros, yank and delete commands. For example, “AY will
yank a line and append it to the a register.
The simplest way occurs by default. Vim will automatically preserve your
registers and restore them at startup. (Not supported by VSCode)
The second way (especially if you think you might overwrite it by accident) is
to use a :let command in your .vimrc . For example: let @a=’0fa’ .
:[range]substitute/from/to/[flags]
This command changes the from string to the to string in the lines specified
in [range] section.
And sure, you can record :substitute (or any command-line command) in
your macros.
to (y/n/a/q)?
If you are substituting with a from or to part that includes a slash, you need
to put a backslash before it. A simpler way is to use another character
instead of the slash. A “plus”, for example: :s+one/two+one or two+
They are called Ex commands because they originally came from the Ex text
editor — a descendant of the legendary ed editor — made by the same author
of Vi.
We’ll see more Ex commands later on. But I’ll continue to refer to them as
command-line commands in this article.
Some commands (not :substitute ) work on the whole file when you do not
specify a range. To make them work just on the current line, you can use the
. (dot) address.
The $ character refers to the last line. For example, to substitute in the lines
from the cursor to the end, do :.,$s/yes/no/ .
The % range that we used before, is actually a short way to say 1,$ .
Suppose you are editing a chapter in a book, and want to replace all
occurrences of “grey” with “gray”. But only in this chapter, not in the next
one. You know that only chapter boundaries have the word “Chapter” in the
first column. This command will work then:
:?^Chapter?,/^Chapter/s=grey=gray=g
You can see a search pattern is used twice. The first ?^Chapter? finds the line
above the current position that matches this pattern. Similarly, /^Chapter/ is
used to search forward for the start of the next chapter. Note that the
matching ? and / at the end of the search patterns are only used to separate
the patterns from everything that follows.
To avoid confusion with the slashes, the = character was used in the
substitute command here. A slash or another character would have worked
as well.
Here, for example, we search for a pattern and then use the line above it:
/Chapter/-1
And here we specify the range that starts three lines below the cursor and
ends five lines before the last line in the file: .+3,$-5
81. Other range tricks: Marks, visual mode, and number of lines shortcut
Instead of figuring out the line numbers of certain positions, remembering
them and typing them in a range, you can use marks: :’t,’b
You can select text with Visual mode. If you then press : to start a colon
command, you will see this: :’<,’>
The ‘< and ‘> are actually marks, placed at the start and end of the Visual
selection. you can mix the marks with other items if you want: :’>,$
One cool trick: When you know how many lines you want to change, you can
type the number and then :. For example, when you type 5: , you will get:
:.,.+4 . Thus, it spans five lines.
:[range]global/{pattern}/{command}
You can use any command that starts with a colon. A special one is the
:normal command, which allows you to use normal mode commands.
Inception!
Suppose you want to change “foobar” to “barfoo”, but only in C++ style
comments. These comments start with // . Use this command:
:g+//+s/foobar/barfoo/g
Since the pattern we are looking for contains a slash, this uses the plus
character to separate the pattern. Next comes the substitute command that
changes “foobar” into “barfoo”. The default range for the global command is
the whole file. Thus no range was specified in this example.
The global and normal commands are supported by JetBrains but not by
VSCode.
The command I{string}<Esc> inserts the text {string} in each line, just
left of the visual block. As you type, the text appears on the first line only.
After you press <Esc> to end the insert, the text will magically be inserted in
the rest of the lines contained in the visual selection.
The A command works the same way, except that it appends after the right
side of the block.
If you have a block selected, you can use $ to make the block extend to the
end of each line.
The Visual block c command deletes the block and then throws you into
Insert mode. C is the same but additionally deletes everything from the
block to the end of the line.
To fill the entire selection with one character, use r . To make all characters
uppercase, use U. For lowercase use u . To swap case do ~ . To join all the
lines in the use J. Note that these commands work also on visual and visual
line modes.
In normal mode, press << or >> . This only shift the current line.
In any visual mode, press < or > to shift all the lines in the selection.
The downside of the method above is that the selection is lost after shifting.
A modern innovation on graphical editors is that the selection is preserved
after every indentation, giving you the chance to play around until you find
the spacing that you like.
Here’s a common addition to .vimrc , which adds this conventional behavior:
The :write command is usually what you run to save the current file.
Additionally, you can run :.,$write tempo.txt to write a range of lines
(which could be, for example, the current selection as we saw before) into an
external file.
You can also do :write !wc to send a range of the current file as the input for
an external command.
You wouldn’t want this for code, as formatters like Prettier usually handle it
in a standard way. But it’s very useful to make you paragraphs inside
comment blocks more readable.
To make this happen while inserting text, set the textwidth option: :set
textwidth=72 .
As with any option, you can check the current value with :set textwidth .
Now lines will be broken to take only up to 72 characters. Vim does this by
automatically inserting line breaks as you type.
But when you insert text halfway through a line, or when you delete a few
words, the lines will get too long or too short. Vim doesn’t automatically
reformat the text. To tell Vim to format the current paragraph, do gqap .
the text object that stands for “a paragraph”. A paragraph is separated from
the next paragraph by an empty line.
Also, it’s worth noticing that having a textwidth on VSCode doesn’t change
the insert behavior — It only affects the qg command. I think this is a
reasonable approach, as automatic indentation might be better handled by
the IDE itself.
You have text with section headers in lowercase. You want to make the word
“section” all uppercase. Do this with the gU operator. Start with the cursor in
the first column:
You can also use g~ to swap case. All these are operators, thus they work
with any motion command, with text objects and in Visual mode.
To make an operator work on lines you double it. The delete operator is
d, thus to delete a line you use dd . Similarly, gugu makes a whole line
lowercase. This can be shortened to guu . gUgU is shortened to gUU and g~g~
to g~~ .
Closing thoughts
These tips should get your Vim senses tingling. After enough practice, you’ll
find that the techniques covered here are enough to bring your editing
powers to the next level of productivity and comfort.
Stay tuned for part two, in which we’ll move to more advance Vim territories,
without needing to abandon the convenience and safety of your IDE.
Thanks for reading! If you enjoy humorous tech stories like these and want to
support me to keep writing forever, consider signing up to become a Medium
member. It’s $5 a month, giving you unlimited access to stories on Medium. If you
sign up using my link, I’ll earn a small commission. You can also follow me on
Medium and Twitter.
Responses (16)
Respond
John Davis
Mar 16, 2023
Wow! Really Wow! This is impressive. I've used vi and vim since late 80s and Ive never seen such a nice
concise summary of usage such as this. I appreciate it and thanks for the good work. Tip of the hat to you
Sebastian.
64 Reply
Dmitry Kankalovich
Apr 1, 2023
This article is a monumental work which doesn't have more claps only because it takes a very dedicated
person to read it all the way. So here I am clapping as much as possible. Thanks
69 Reply
Manoj Rewani
Nov 9, 2023
hard work done.. impressive work
31 Reply
Fired From Meta After 1 Week: How To Update Your Status During
Here’s All The Dirt I Got Standup Like a Senior Engineer
This is not just another story of a disgruntled A status update is where you can showcase
ex-employee. I’m not shying away from the… how well you manage ambiguity and is an…
See all from Sebastian Carlos See all from Better Programming
Lists
This new IDE just destroyed VS Go 1.24: The Ultimate Update That
Code and Copilot without even… Makes Development Faster and…
Wow I never thought the day I stop using VS Go 1.24 (scheduled for release in February
Code would come so soon… 2025) brings a variety of powerful new…