I’m someone who recently started using vim and have been always trying to find new ways to do things efficiently in vim.
After all using vim I have realized its really powerful and can do a lot for you with few commands which I personally find it really cool.
So i would love to hear from the community what are you favorite vim tricks which have made you productive.
mine favorite was reading terminal output directly in vim for example if I wanna list of folders in my directory i can simply do
:read !ls
I particularly enjoy the Change In/Around commands:
ci"
replaces the inside of whatever double-quoted text block you are in,ca'
replaces the single-quoted text block including the quote characters, and there are variants for various brackets, braces, parens, and paragraphs!another fun fact about ci” is that you don’t even need to be inside a double quoted string or whatever… it will go from where you are on the line to the next double quoted string and let you change that.
so ci” on this at the | :
lor|em “ipsum” foo bar
would change it to this with you being in insert mode
lorem “|” foo bar
Best paired (…) with https://github.com/tpope/vim-surround which is the first plugin I miss on a fresh installation of vim.
See :h text-objects for more details. You can also create your own using
omap
.I think this is a great introduction to writing your own text objects:
https://web.archive.org/web/20201108142554/https://vimways.org/2018/transactions-pending (web archive link because the site currently has an out-of-date certificate)
I mention is below, but kana’s vim-textobj-user is also very helpful.
If you like text objects, I definitely recommend checking out these. (People may already know about them, but just in case…)
Oh wow, I always used
ct"
orxcf"
, but those obviously only work from the beginning of the block. These are great!Yup, we should learn about text objects sooner rather than later and hen get the kana/textobj-user plugin which will also give you a whole set of new text objects to play with. There are also alternate approaches like targets.vim.
g+
,g-
(mic drop)No, in all seriousness, the documentation sucks on this… So I’ll explain it how it was explained to me by a colleague:
Vim stores edit history and undo as a tree. For example, if you do
A
,B
,C
,D
, undo, undo,E
,F
, undo, undo, undo,G
, vim will store this history:g+
andg-
will navigate between the “tips” of this tree, so betweenD
,F
andG
. One goes back, the other forward. I never know which one is which betweeng+
andg-
to go the way I want, so I always try both until I realize it’s going the right way :) .The undo tree is one of the most import features that is missing from all „vim emulations“ in other editors like vscode, pycharm/goland, younameit.
Especially when you throw in a visualizer for it like mundo. Emacs has it as well.
It should be noted that you can use proper vim inside of vscode if you use the neovim plugin. Basically all of nvim in normal/visual mode is available without much compromise. you can even call visual studio apis in normal mode if you map it in a vimrc.
This is incredible. I’ve been frustrated when I’m at “G” realizing that no, I was right when I was at “D” and can’t go back.
It’s too bad that the emulator in VS Code can’t do this
if you use the neovim backend you can have the (almost) full power of neovim inside of vscode…
JetBrains IDEs have a local history feature for this. It’s fantastic, as it shows you a merge view between your current state and the selected history state. So you can still edit the current state and cherry-pick bits from the diff trivially.
This is a great tip. I also cannot recommend the plugin vim-mundo enough which builds on the builtin undo-tree to provide a panel that let’s one jump through undo histories, search within them and my favorite, diff them as well. Like a very convenient zero-cost mini git.
gq
to format selected paragraphs.Tangentially,
gq
has a “dumber” counterpartgw
, the difference between them beinggw
uses vim’s own formatting program butgq
can be customized to use a user-specified one. So that will let us use different formatters for different filetypes etc. which is nice.You can also choose an external command to use by setting
formatprg
. I usepar
, which has terrible documentation but works great.gw
will do the same asgq
, but always uses vim’s internal format engine, instead of whateverformatprg
is set to.Macros. They’re great because you don’t have to know fancy vim stuff to use them. You record a sequence of ordinary edit commands and play back the sequence. For example, to add quotes around a line, and then add quotes around the next 50 lines:
q
starts recording the macro to the named register (I just useq
as the register for expediency). The rest of the sequence adds the quotes, moves 1 line down, and ends the recording with a finalq
. Then run the recorded macro (@q
) 50 times. Moving down one line at the end puts the cursor in the right spot for the next macro run, so you can repeat the macro like this. You do need to be careful about what motions you use, e.g. usingf
/F
to move to an exact delimiter rather than usingh
/l
a fixed amount of times, but it’s usually easy to manage.You could also do this with
:norm
, but I didn’t want to type out an elaborate multi-line example that:norm
would struggle with on my phone. The equivalent:norm
, starting with a visual select:Notice the
<C-v>
before<ESC>
, necessary to insert a literal escape key into the command sequence. Also useful for inserting literal newlines in substitutions and the like.To add to this, it’s important to remember that you can invoke macros from within macros. This makes it quite easy to build up complex actions by recording macros for individual steps and then larger macros that invoke all of them. I treat the macro namespace as scratch space, but I’ll quite often end up with @e invoking @w, which invokes @q.
Oh, and @@ invokes the last macro, which is useful when you want to apply the same macro in a handful of different places.
seconding macros, it’s so satisfying to record one correctly that does some non-trivial repetitive editing to a bunch of lines and just watch it zip through the whole file
In case you didn’t know, you can edit the register if something went wrong. For example,
"wp
will paste the contents of"w
register - modify it and then put it back to that register (for ex: visually select and then"wy
or"wy
followed by motion command).And you can put it in your
vimrc
aslet @w = "..."
to load it at startup.Personally though, I prefer substitution command for cases like adding quotes to start/end of line.
Good tip 👍
I did know that but I prefer feeling subtly inadequate and then trying again until I get it right lol
After using vi(m) for twenty years, I’m still hopeful I’ll get to feeling just subtly inadequate.
This just gave me an idea! Instead of writing commands and then deciding on a set of bindings I could make my ftplugins preload some common editing sequences into macros and use the (probably lesser used) macro registers as bindings. Essentially giving me an extra mapleader.
<C-o
and<C-i>
to navigate the jumplist. https://medium.com/@kadek/understanding-vims-jump-list-7e1bfc72cdf0marks https://vim.fandom.com/wiki/Using_marks
It’s not native but I find fzf to be indispensible: https://github.com/junegunn/fzf.vim
Your example of shelling out with :read is good, but you can do more with the ! operator. In particular, note that it works in immediate mode and has the same flexibility as the yank/delete/change commands. So
dd
deletes a line and!!
prompts you for a command to run, passes the current line to that command on stdin and pastes the stdout where the line was, so I use!!ls
for your use case (if I don’t care about the current line I am on).All these commands take a ‘movement suffix’. So
dG
deletes from where you are to the end of file and!G
allows you to run a command on the rest of the file. This works with any movement command…. [*]This allows things like
!Gsort
to sort your file - basically you’ve got the unix pipeline + text handling toolset in your editor.I sometimes want to eyeball two files to see differences. I typically have
:map q :e#^M
so tapping ‘q’ switches me to the other file and another tap brings me back.With the
:<range>s
command, you can specify^
or$
to add text at beginning or end of line. The range can be%
for the whole file, or you can specify<from>,<to>
. From and to can be line numbers, or.
for current file or even a search, so:.,/foo/s/x/y/
means “search and replace x for y in the lines from here to the next occurrence of ‘foo’”.e.g. :%s/^/func /
to add a
func ` prefix to every line of the fileLastly, knowing that
.
repeats the last edit is very useful and helps make you careful about what is in the edit so you can usefully re-use it. (I also get a lot out of simple macros written with the map command. I typically use ‘,’ for a throwaway temp macro key, so::map , Ifunc ^VESCA() {^VESCj
does “prepend ‘func’, append ‘() {’ and move to next line.) (The ^VESC there is “type a literal control-V then hit the ESC key”, this allows you to embed an “escape to leave insert mode” in your macro).[*] the movement suffix is super useful if you don’t already know it. You can suffix
d
,y
,!
,c
etc (are there others?) with any vi movement command and it will apply the command to the region between the cursor and where the movement takes you. e.g:dk
deletes current and next line.df.
deletes from where you are on the current line up to and including the next.
char.d/foo^M
deletes from where you are to the next occurence offoo
.c3W
is “change three words”It’s also worth pointing out that
:sort
sorts without a shell, and tends to be in Vim emulations.gf
to navigate to the file under the cursor. Is very flexible in it’s configuration, and can be set per filetype. I find it really useful to navigate to includes.I really like having a custom dictionary I can append all my tech jargon into using
zg
command when spellcheck is enabled:set spell
. My .vimrc leverages the following (I wish I could recall where I originally got this technique from):I don’t use Vim a lot these days but several years ago, when I was an avid Vim user, one of my favourite Vim features was the blockwise-visual mode that can be started with the Vim key sequence
ctrl-v
. It is very nifty for making rectangular selections and move around columns of text.I use Emacs these days which offers a very similar functionality in the form of rectangular-mark-mode that can started with the Emacs key sequence
C-x SPC
.Long ago I installed a plugin named visualDrag.vim. I still have it installed and bound to my arrow keys in visual mode. It lets you use ctrl-shift-v to go into rectangular visual mode and then shift just the selected characters around, shift everything else to make it work.
I discovered the following quite late:
q:
opens a buffer with the history of last used commands. You can navigate then inside, as if it is text, i.e. you can even search. It is especially helpful when you need to repeat a command, but slightly different - in this case you go to the line with the command that you want to change, change it and press , it’s not even necessary to press first, just press enter.
q/
Like above, but for the search history.
set history=10000
I put this in $HOME/.vimrc to keep in history more than usual, for the above operations.
g8
Show the Unicode code point for a character (file must be in a Unicode encoding, for example utf-8).
Entering a Unicode character:
xp
Swap 2 consecutive characters (helpful for typos)
Filtering (piping) the buffer through a unix command (for example, to re-format and re-indent ugly XML):
:%!xmllint --format -
The above will take all the text in in the current buffer (the ‘%’ in the command), start the xmllint command (the ‘!’ in the command above) and feed the text into standard input to the xmllint command, and whatever xmllint outputs will be put in the buffer.
Using
.
to repeat the last command in normal mode.My favorite way to use this is to find and replace words in a file. Search for the word with
/
. Thencw
orciw
(change word or change in word). Pressn
to find the next instance of the word, and just press.
. Repeat this for every word in the file.Mine is moving ctrl to my capslock key, and then becoming proficient with the various
c-
prefixed keys like<c-w>
for window navigation and<c-p>
or<c-x><c-f>
and others for completion.It is even better if you move escape to capslock; For vim, escape is the main meta key.
I agree! I have my caps key mapped to esc on tap, and ctrl on hold.
If I had to only pick one between ctrl and escape I would pick ctrl for remapping caps lock. The more I use my mappings that begin with ctrl, the more I appreciate having a ctrl key within easy reach. Also useful for other programs. And
<c-[>
sends the escape key, so it’s not too hard to press escape when needed.I don’t think there are many situations where ctrl-c doesn’t do the same thing as escape. It’s not a bad muscle memory anyhow for a similar concept everywhere else. I don’t remember the last time I’ve reached for escape in vim…
imap kj <esc>
is my favorite. Your fingers never need to leave the home row. Add this to your .inputrc to get the same effect in bash:mapping kj and jk to esc is great because you just have to mash both them without worrying about the order… it’s liberating.
A similar thread: https://lobste.rs/s/6qp0vo/at_least_one_vim_trick_you_might_not_know
Related to the OP’s favourite — batch renaming!
:r !ls /foo/bar-*/*.txt
:%s/.*/mv “&” “&”/
Now use vim to manipulate dest path
:w !sh
I’ve been using GVim since 2007, but still kinda an intermediate level user. I don’t have a pressing need to dive deeper. I prefer using mouse, arrow keys, tab pages, etc and I don’t use any packages.
It is difficult to identify a particular trick to share:
Ctrl+x
Ctrl+l
to auto complete a line in Insert mode (I use it a lot, so I’ve mapped it toCtrl+l
)<count>g
followed byCtrl+a
in Visual mode to increment numbers as an arithmetic progressiongn
searches the last used pattern in the forward direction and visually selects the matched portion, especially useful when you combine it withcgn
(which you can then repeat with.
command):terminal
if you want a terminal session as a horizontal split window within Vim (use:vertical :terminal
if you want vertical split)I have a list of Vim resources here: https://learnbyexample.github.io/curated_resources/vim.html
Actually it is
{count}
followed byg<C-a>
not{count}g
followed by<C-a>
;)For that
gn
andcgn
trick, I have plugin for that.Oh, thanks for pointing out the difference, it helps to correct my mental model as well. I don’t know why I thought count prefix was specially implemented instead of the usual repeat what the command does.
Conversely, I hate the graphical Vims and mouse mode because I like having shared and private copy and paste. Within vim, I can use yank and put to copy and paste without affecting my system clipboard. I can also select text and copy it with my terminal and paste it in insert mode (optionally with
:set paste
if I want to preserve whitespace). It’s very common for me to keep something in my system clipboard that I want to paste multiple times into a vim session and not have it clobbered by deletions.Isn’t that the case with GVim too? That’s how it works for me anyway. I have to specifically use the
"+
register if I want it to be available with Ctrl+v elsewhere (browser for example). Or, do you mean the middle mouse button paste which works when you select text from any application?It’s been ages since I used GVim, I’ve mostly used MacVim and terminal vim with mouse-mode enabled. Moving my cursor (and leaving insert mode) when I select something is annoying. More generally, my hands don’t leave the keyboard when I’m using vim, moving one over to the mouse is a mode switch in my brain that corresponds to the ‘using more than one application’ mode, so I don’t want the mouse involved at all when I’m in vim and I don’t want mouse-related state to affect vim.
I interact with a lot of encoded formats like JSON Web Tokens (JWTs) as well as wanting to unpack URLs, so I’ve found it super handy to be able to pipe visually selected text into a command, i.e.:
(source)
Or to do the same with a visual selection:
(source)
g commands when using it from the colon mode (command mode? i’m not sure what that’s called) same way as you’d do a find and replace in a file
something like
%g/^ *$/d
will delete the empty lines in a file or%g/WARN/d
would delete warning lines in a loguseful for deleting all the lines that you don’t want to deal with in a log file or you can also perform other operations with it by replacing
d
.Given that you’ve mentioned a basic
vi(1)
command as your favourite, I’m going to do the same:It’s a repeat the last command, at least in nvi :^)
In terms of
:r!...
I use it daily, i.e.:So many…
https://github.com/junegunn/vim-easy-align is amazing for scratching that “my markdown tables all look like crap in source” itch
:%!python -mjson.tool
for trying to make sense of payloads in log messageshttps://github.com/preservim/nerdtree with indicators and webdevicons https://i.imgur.com/0gSouzm.png
Recording macros to mangle text when I’m too lazy to write code to do it…
this isn’t a command but check out goyo and limelight, which pair nicely when trying to emulate a distrationless writing tool like iA writer.
Pencil is suggested as well. I use Goyo + Limelight + Pencil to get a decent email composing environment for Himalaya and its Vim plugin.
oh wow, this is super sick. i normally tweak my config manually to get some of these results when i find myself doing some longer form writing. thanks so much for this shout!
Relative line numbers are nice. Instead of eyeballing how many lines you’re looking at, you can just look to see the exact number of lines you want to move or affect with whatever command you want to use.
I typically have both
number
andrelativenumber
enabled, which shows the absolute line number for the current line, and relative line numbers for the rest.Just as nice is knowing how to disable this. Relative line numbers are not workable when trying to do any sort of pair programming–especially over a voice call–as the other person will have an impossible time telling you what line to look at as you move around.
I use this one constantly:
It copies text you have (previously) copied at the right level of indentation. (
p
does the copying, then `[v`]= visually selects and realigns whatever you just copied. I map it aspi
(forput
andindent
), but you can make that whatever you prefer.)The lack of auto-completion in the command line can be annoying but a quick
<c-f>
will move everything typed so far over to the command-line window(q:
) where it is available.I recently found out about
set foldmethod=syntax
and:zM
: reset fold level (by default, collapse everything)zm
: fold more thingszr
: fold less thingsIt is useful for reading long source code files.
And its
wl-copy
andxclip
relatives. This copies the file, or the selected part if in visual mode to the clipboard.There are options to support this natively, but I usually opt for this rather than worrying about that specific flag.
On MacVim and NeoVim it works OOtB via
+
register. So"+y
will yank to system register.Late to the party, but very useful and not yet mentioned is
:cq
, which exits vim without saving and with exit status 1. Can be used to abort many things, e.g.git commit
or mail composition in mutt.I enjoy using
:cdo
https://vimhelp.org/quickfix.txt.html#%3Acdoset shm+=I to turn off the intro screen
syntax off to turn off syntax highlighting
set compatible to turn off yet more superfluities
then it’s almost like nvi (RIP) except you have to press ^R after u for multiple undo instead of .
:)