Hacker News new | past | comments | ask | show | jobs | submit login
A Vim Guide for Advanced Users (thevaluable.dev)
603 points by asicsp on Feb 27, 2021 | hide | past | favorite | 172 comments



The article mentions ”gn” in passing, but it doesn’t give it enough credit: this is an enormously useful motion, almost more useful on its own than the rest of the article.

The thing that makes it golden is that it is a motion, and thus combined with a command, it is repeatable! So, like:

1. Search for something (either using / or *).

2. Type ”cgn” in Normal mode (i.e. change next search match) and replace what you searched for with whatever

3. Hammer on ”.” (i.e. repeat last command) to replace other matches.

This replaces, like, 97% of uses of :s and is a much more convenient way to do search/replace, especially if you use * to search for the word under the cursor. It’s the bee’s knees!


This has one huge shortcoming: when the next match is off-screen, pressing . jumps immediately to the next match and replaces it, before you had the opportunity to review whether you want that.

To solve this I wrote a little plugin (that I finished as a plugin yesterday and uploaded just now): https://github.com/orlp/vim-quick-replace

It requires vim-repeat (and a patched one because the current one has an issue): https://github.com/orlp/vim-repeat

The nice thing is that it always jumps to the next match before replacing, and if you choose to replace the current match it immediately jumps to the next one, so that you can evaluate whether you want to replace it.

You can start in two quick ways: using the current word under the cursor or the current visual selection, so it's very flexible. For me replacing a variable name is often just putting my cursor on it, pressing space + r, typing the replacement name and then a couple .'s, n's and if I mess up some u's.

To see it in action: https://i.imgur.com/wEX6O1w.mp4


That's a good point about previewing. So instead of "cgn", it looks like you can do "gns", which highlights the match, then enters into substitution mode, and you type the replacement, then hit <ESC>. Then you can hit "n." "n." "n." repeatedly, where the "n" goes to the next match so that you can preview, and the "." applies the replacement. Not quite as efficient as a single ".", but the "n." sequence is a familiar one.


IMO the "correct" way to handle this is with vanilla vim's built-in macros. Find the next match, record a macro making the edit you want, then find the next match and finish recording the macro. The macro starts with the cursor over the match you want, the edit can be more complex than a simple replacement, and skipping is simply a matter of pressing `n` instead of `@@`.

Vim can also take a command that yields a list of filenames as a launch parameter via something like `vim $(ag -l target_string)`, letting you go through each file with a `:n`, letting you complete large refactors surprisingly quickly.


I'm not sure exactly what your plugin does, but are you aware of the "c" flag to substitute, eg

:%s/debug/dbg/c

vim will show the text which is about to be modified, and you have a prompt to confirm, skip, or quit (and some others).


Yes, I am aware, but it's not nearly as flexible and quick. It doesn't allow me to in the meantime move my cursor around, switch buffers, undo, open/close a fold, etc. It takes complete control of your workflow to temporarily put you in some special 'search/replace mode' in a very non-Vimlike manner. My plugin has a simple operation (if cursor is on a match, replace, regardless jump to next match) that gets repeated with ., combined with nN and u and you have complete control while also rapidly doing search/replace.

And you would still need some nice mappings to substitute the word/selection underneath the cursor with :%s///gc.


If using the [c] flag and you want to interrupt it, a convenient way of continuing where you left off (and not touching previous lines, which may have new matches, depending on the nature of the replacement) may be :.,$&&<Enter>, which means “operating on the range current line (.) to end of file ($), execute the last substitution (&) with the same flags as last time (&)”.

(This is very unlikely to interest you, but I mention it for the sake of others. I’ve commonly seen people not really know about ranges especially, never stopping and thinking about what the % means (see `:help :%` if you don’t know), beyond possibly having encountered '<,'> by accident when pressing : in Visual mode (for explanation of these, see the help for :', '< and '>).)


You can just type n, followed by ., to view the next match before changing it.


I used to do that but my brain messes up "n." for replace, "n" for skip, whereas "." for replace, "n" for skip is really easy.


Is this much different from searching with * or :s for a match and use something like "ciwReplacement<Esc>", then "n." or just "n" if I don't want to change the match? I can imagine that the plugin works in more complicated cases, but most of the time I probably just want to replace a word with ciw or ciW.


Well, assuming you already have a plugin or mapping that makes * search your visual selection, it's similar.

You'd have to do *, then N to back to where you just were, then cgn, type your replacement, and then n., n., n., n (skip), n., etc.

With my plugin that just becomes one mapping, type your replacement, ., ., n (skip), ., etc.

It's just very smooth, which I like because I use this operation literally all the time. Hell, I'll even use it when changing a single variable, just to make sure there isn't another sneaky one in the file somewhere.


Can't you just press n to jump to the next match without substituting, then if you're happy to continue, press . and it'll replace it? I use n and p with cgn all the time.

Works a treat!


But if you're not sure you wanted it, you can just press u to undo and n to skip.

Since Vim keeps an undo tree, you are guaranteed not to lose anything anyway.


I used to do n.n.n.n. to do the same (i.e. /search something, then do the replacing with something like cw or 3s or ct' or whatever you want, and then hit n for next result and . to repeat the change). Using cgn is less flexible and you need to learn another thing (you know about n and . already by the time you get to cgn), and maybe it's just me but hitting n. repeatedly is practically equally fast as just hitting the dot.

It also has the downside that it doesn't match what is being shown: when I hit * on "overview" it highlights both "overview" and "Overview" (I have highlight search enabled), but cgn replaces only one of them. When the cursor is on Overview and I run "*cgnexample<esc>..." it will skip all the "Overview"s and only replace "overview".

But perhaps it's a bug in Vim since it works when I run * on the lowercase variant.


This might be the right audience for this little vimrc tidbit:

    "Press space to highlight current word
    nnoremap <silent><space>
        \ :let @/='\<<C-R>=expand("<cword>")<CR>\>'<CR>:set hls<CR>
This works just like *, except that it leaves your cursor where it is. You can then use n/p/whatever as normal.


> like * but leaves your cursor where it is

Ooooh that is great! I use *# every time and am then annoyed when the n key goes backwards instead of forwards and then I try to remember to do #* instead and pff... ^^ This will solve those issues!


This is useful enough that I wonder why you chose not to just map it to *. Is there a reason you did not?


Because <space> is even easier to hit :)

Edit: also because I try and keep my keybinds as close to default as I can, and <space> is oddly underused.


> But perhaps it's a bug in Vim since it works when I run * on the lowercase variant.

Per the help it's a feature, and it's infuriating! (From :help star)

> 'ignorecase' is used, 'smartcase' is not.

Here's your workaround:

    " By default, * and # respect 'ignorecase'. This mapping forces these commands
    " to search case sensitively, which is usually what you want when searching
    " code with * or #. May be less good for prose.
    nnoremap <silent> * /\C\<<C-R>=expand('<cword>')<CR>\><CR>
    nnoremap <silent> # ?\C\<<C-R>=expand('<cword>')<CR>\><CR>


Awesome, that's definitely something I'll add to my bag of tricks. But

> This replaces, like, 97% of uses of :s and is a much more convenient way to do search/replace, especially if you use * to search for the word under the cursor. It’s the bee’s knees!

I'm not really sure about that. I can definitely see it replacing :s for a small number of repetitions, but for a large number. I think using :s with a range is quicker.

Edit: typos, punctuation


I too wish to add this to my repertoire. I don't think it will replace :s, but occasionally I find myself copying a line to use as a template. When I go to change each line I'm changing a particular part to different things. Assuming I remember, this trick seems like a good approach.


Ooh, sounds interesting. I'll have to look up the details. I currently do this after a search with / and regex (if it is a bit complicated)

    :%s//replace/gc


This is what I do, I use regex's so often it's easier for me to read this even if it's a little bit more typing


Yes, and here's my favorite vim trick I ever invented - you can do use this after changing a word.

What I mean is: if I'm on a (say) a variable name, and I change it with "cw", then realize I actually need to change it in a few more places, I can use a little shortcut to change it in those places, without even thinking of it beforehand.

The shortcut is:

  " Make <leader>. repeat the last "change word" command.
  nnoremap <leader>. :let @/=@"<cr>/<cr>cgn<c-r>.<esc>
After any change command, just hit <leader>. to repeat it. You can thereafter hit "." to repeat again.


This is very useful, thank you! How would you go about pasting the "replace" part though?

I could only think of this rather awkward sequence:

1. Yank to named register

2. Search

3. "cgn"

4. Ctrl-R to paste from named register

5. Repeat

This is actually the biggest gripe I have with vim, even after almost 10 years of constant use. Vim always seems to overwrite the default register when I don't want it to. This makes replacing multiple bits of text with one pasted value awkward. I feel like I'm missing some essential shortcut or configuration that everybody else knows. It can't just be named registers, right?


You can slightly simplify your workflow using the yank register 0.

1. Yank using y without specifying a register

...

4. CTRL-R 0

Deletes and changes don't touch the 0-register. It always contains your last (unnamed) yank.


So I've been using vim for nearly twenty years and didn't know this. I also never touch visual mode make any advanced use of buffers or marks. I feel like some kind of caveman now.


Maybe you will like these little gems I came up with back when the gn patch was merged:

nnoremap <key> *``cgn

nnoremap <key> #``cgN

Hit <key> to change the current word and ... to repeat on the next occurrences.


Just tried this. Note for others: between 2. and 3. you need to get back to normal mode (which is kinda obvious but...)

Thank you for the tip.


That does sound useful, but I do this with `:s/foo/bar/gc`, then I press `y` each time, which is pretty similar to pressing `.` each time in your example. I do like that the `cgn` approach works better for something you searched for without knowing a priori that you were going to do a replacement on it.


I've done this by setting the / register with * as you mentioned, but I still use the :s command, type / and then use the ctrl-r / key combination to paste the contents of the / register, then type the second / and then the replacement.

The g and z set of commands are pretty useful in vim.


Thank you so much! I've been using Vim for 5 years and I always thought :s was one of the major pain points.


The author does mention this in an in-depth article on search: https://thevaluable.dev/vim-search-find-replace/

It's pretty comprehensive. Personally I'd just install Doom Emacs though :)


Oh man, I knew there had to be a way to do that but I couldn't find it, so I always resorted to counting the number of characters in the match and then use cl<num> to replace it if there was no convenient motion like some kind of barbarian.


The non-barbarian way to avoid counting characters when there's no convenient motion is visual mode. Hit v, then hit l until you've highlighted the part you want to change, then hit c to start changing it.


Oh I never thought about that either. Thank you.


I rarely actually do it because f and t motions, possibly with a number, can usually handle these cases. For example, c3tA to change up to but not including the third capital A after the current position


If it's more than one 'A' I'd use visual mode. I'm always impressed people can accurately count up to like three ;)


c<num>l is equivalent to <num>s (unless you've installed a plugin that binds s to something else).


Nice, it works in evil-mode on emacs as well! Although for emacs I would just use query-replace, which does the same thing with y/n confirmation, and it can also be run on just a selected region if desired.


Yeah I know the trick and I explain it a bit more in another article on my blog.

Most of the time it's more keystrokes than using :s though, so I'm not a big fan of it. But I see the appeal :)


I'm paranoid and I like seeing exactly what I'm replacing. In that case I still use :s but I always end it with :s/foo/bar/gc. Although I do see the appeal of cgn for simple replacements.


If you use Neovim, you can set the option inccommand:

set inccommand=nosplit

It will display the substitute directly in the buffer.


I was looking on how do this exact thing! Thank you! In IntelliJ I press Alt+j to add to selection then I can edit all matches. You just described how to do the same effect in Vim


Why is this preferable to :%s?


It composes better if you were searching for something and then want to replace it IMO. You basically just have to remember the search syntax and then you can reuse it for replacing.

It's also better if you don't actually want to replace in the whole file and there's no convenient shorthand for the range.

I very rarely use :s myself, I'm always too worry that I'm going to clobber something I didn't mean to, I prefer to search and manually repeat a substitution. s can do it with with the "c" modifier but I find that using normal edit commands is both more flexible and easier to remember.


It allows you to use a full Turing machine rather than restricting yourself to a Finite State Machine.


I find it more intuitive, and for a small number of iterations it's definitely faster.


As someone who uses this regularly, it's still not as convenient as multi cursors that most modern ides give you.

There's a vim extension for multiple cursors but it's a little buggy.


I dunno, I’ve also used both, and I keep coming back to the Vim/gn way of doing it, I just like it more. I see how people can feel otherwise, though.


Yeah, not everyone is the same, but there are definitely people interested in multi-cursor modal editors, enough to build their own, eg. vis⁰ — a light, fast, and simple text editor with a pretty standard vim normal mode but sam’s structural regex command language¹ for creating and procedurally editing selections in command mode², and kakoune³ — known for switching vi’s word order from verb noun to noun verb, so typical normal mode commands work by first creating one or more selections with motions (no need to press v), and only then operating on them. And of course Emacs has multi-selection modes and AFAIK they’re compatible with evil.

https://sr.ht/~martanne/vis/ ¹http://doc.cat-v.org/bell_labs/structural_regexps/ ²http://doc.cat-v.org/bell_labs/sam_lang_tutorial/ ³http://kakoune.org/


As someone who never used a multi-cursor IDE (I've seen other use it but not in a convincing way): what's something that you use this for that you don't think vim can do?

The main purpose I've seen is basically block mode (like ctrl+v) or (iirc) repeating things on multiple hits (like n. or indeed cgn).


Vim is getting more popular. Not just within vim, but outside vim! Practically every full-featured editor has a vi or vim mode, and when they say vi, usually they also mean vim.

Things that have nothing to do with vim but are inspired by vim are even sprouting up! And apparently it's OK to name something based on vim: https://groups.google.com/g/vim_use/c/EnSMrx_rGg4/m/d_nVlmLQ...

Edit: In Bram's reply he mentioned the household cleaner. Apparently it's similar to Ajax. That's so awesome. https://en.wikipedia.org/wiki/Vim_(cleaning_product)


I used to work near a gym called “Vim Fitness”[0] in Cambridge, MA and there were endless jokes about trainers calling out: “H! J! K! L!”

[0] https://vimfitness.com/


I bet it's constantly crowded since no one can figure out how to exit.


I work at a company called Vimcar which I like to describe as "like Emacscar but better." ;)


I definitely agree that it’s getting more popular.

I use vim inside VS code as my main editor and love the experience. It was also an easy way to step into learning vim.

I also have a vim product with vim in the name (it’s practically the whole name) and haven’t had any sort of trademark issues.


Yeah, the current situation, with no registered trademark for vim the editor, seems to be great for everyone! Vim is popular enough that it's unlikely to be taken. However, someone is currently trying to claim Screen for a screen sharing product despite a screen sharing tool called screen(1) having been around for decades, so nothing is impossible.


Practical vim by Drew Neil is hands down the best vim learning resource out there. Nothing even comes close.


Anyone looking to pick this up: I found the best way to consume it is to grab a couple tips at a time (it’s structured as a “recipe book”) and incorporate them into your workflow. For example, try Netrw for a week, then another week try out folds. You don’t have to read it cover to cover to get the full benefit of it.


This is exactly what I'll be doing with these articles for the next few weeks. Learning a few cmomands at a time gives them time to sink in and become habit.


It looks like a really good way to take the most out of these recipe resources. Thanks for that!


Drew Neil's Modern Vim is also wonderful. While it introduces a lot of plugins in an attempt to modernize the experience, it also includes some excellent notes on standard features(qf/sessions/etc).

I'll also note it covers both vim and neovim, with nice little asides explaining the differences where they matter.


I took Drew's Vim Masterclass with some coworkers years ago. I considered myself a fairly advanced Vim user at the time, and still learned some handy things during his class. It was a small group (we hired him just for our group of coworkers), and he took time to answer everyone's questions. He's got a great teaching style. I'm not sure if he still does the class, but if so, I recommend it for all Vim users.


This book is so good I actually bought it twice. The second time was the ebook edition just so I can pull it up next to Vim. Highly recommend it.


I thought it will be a 100 page book but hot damn. 487 Pages for what?


What are the high points? Would you expect it has much to add for someone who's been using vim for closer to 3 decades than 2?


In some ways it's quite conceptual about the best way to use vim. Inside the cover there's a quote "I thought I knew vim, but practical vim has massively improved my code-wrangling productivity".


Totally agree with that. That's my favorite book about Vim for sure.


Those commands may look useless at first sight, but combined can create powerful routines. As an example, my routine to refactor non-typesafe codebases.

- Find the pattern that you want to replace (the last dot is for current dir)

:grep -r 'pattern' .

- Open the quickfix window and check if matches are correct

:copen

- Find and replace the entries of the quickfix list

:cdo s/pattern/replacement/ | update


Thanks for sharing! It's always inspiring to see different workflows.


One thing I've never been able to figure out in Vim: why does O (insert new line above the current line and enter insert mode) often wait and hang? And when you start typing, it'll sometimes do random stuff? Is there a way to just make it behave like a true counterpart of o (insert new line below and enter insert mode)?



Thanks! I never use arrows in insert mode so that's an easy choice for me, but removing 'nocompatible' from my vimrc does nothing and setting 'compatible' instead has side effects like not showing what mode I'm in. Not sure I need it, but I'm also wondering what other side effects I'll run into. The help file mentions a ton of things that I frankly don't care enough to comb through. The only thing I randomly spotted (and knew what it meant without digging deeper) is that this also breaks backspace in insert mode. Overall it makes vim feel quite a lot like vi.

Instead, setting nocompatible and timeoutlen=1 (1ms I guess? Help file doesn't say) works fine in my terminal, I guess because it's not over any sort of network connection. I'll give this a go and see if I run into any issues when using these escape-based features (afaik I use those only rarely) over the network.


Woah, I just looked at the Vim help page for 'timeoutlen' and the fact that it's in milliseconds is only mentioned below, under 'ttimeoutlen'. The Neovim help page specifies that it's in milliseconds.

I think that the Stack Overflow answer was just saying that the default depends on 'compatible'; not that the recommended solution is to go back to 'compatible'.


You probably have a mapping for a multikey starting with O, I had this problem many times with different keys. You can search for it in `:map` or `:verbose map`. Or just `:map O` and tab for autocomplete (might work, not sure).


I think I remember having this issue in vim too. Don’t remember it ever crossing my mind since I’ve started using neovim, though, so maybe give it a try?


Just add this to your .vimrc

set timeout timeoutlen=1000 ttimeoutlen=100 " Fix slow O inserts


As far as I am concerned, this constitutes a good read for the VIM pro. It's a shame that critical comments tend to bubble up to the top on HN


This was a really nice refresher and I learned a few new cool tricks (like gx).

I'm a bit surprised that the 'u' flag for sort wasn't mentioned. That's the most useful one in my mind and I use it almost every time.


... I forgot it :D

I'll fix that. Thanks to point it out :)


Thank you for reading the comments and updating the article.


My thoughts exactly about ':sort u'


The u option making it keep only unique lines, for anyone else left wondering.

Personally, instead of re-learning how to do stuff with vim-sepecific commands, I just use the existing command line tool, especially since I don't do this on a daily/weekly basis (but I use sort in command line pipes at least weekly). The command is literally "sort" and for unique you just pass the -u flag, e.g. select something in Vim and type :!sort -u


Vim's sort is handy on Windows where the option to shell out usually doesn't exist. Additionally you have the full power of Vim regex to sort on subsets of lines.


On the other hand, the :! and ! commands are Vi compatible (though, as you say, you wouldn't get the additional features of :sort). So you get to pick who you're going to be compatible with: vi or Windows?


I can also do a subset of lines in the way I described, actually


The article mentions gI but that's 3 keystrokes (g, shift, i) when you can do 0i, and you probably want to know about 0 and i regardless, making the extra learning of gI unnecessary.

Unfortunately this doesn't help anyone because it's not as if by deleting gI from your memory you can more easily learn something else, but perhaps it's helpful for a future guide writer or perhaps someone knows why gI is useful.

Edit: The section "The Insane Expression Register" also doesn't make much sense to me. Why would I do this 'system("ls")' thing when there is '!ls'? Using vimscript instead of bash probably has advantages in, y'know, vim, but most people know their shell and probably very few people know vimscript (certainly not me) so I'm not quite clear on the advantages here.


The number of keystrokes is an interesting number to use because it is only roughly correlated with the time it takes to press the keys. Both 2dd and 2[] are three keys, yet the latter can be typed much faster than the former as it is spread over both sides of the keyboard and it doesn’t repeat a key (so you can “roll” your fingers over the [] which is faster than double pressing the d).

In the case of g-shift-I, I’d call it 2.5 keys. Assuming you press g and I with the right hand, the shift key hardly matters in terms of time. Of course, by the same logic, 0i is faster still.


Of course I agree with the main point that keystrokes is not the same as fast to type, 2dd would indeed take me longer than 2[].

For me, though, shift is annoying. The duckduckgo bangs I find a pain and I don't use them (shift+1 is fairly uncomfortable a keystroke compared to one of the symbols available without shift) but a lot of people advertise it as a handy feature. Timing the shift key correctly is something I get wrong more often than other keystrokes, perhaps because it's operated by the pinky (though letters like "p" in operate and "a" in and are also pinky-operated and those work fine for me, idk). For me g-shift-i it's really 3 keys, not 2.5. I'd even go so far as to say that 0i is more like 1.5 than 2, since they're so nicely next to each other. By moving my right ring finger to the 0, my index finger almost automatically falls on the i. Conversely, the g and then shift makes me have to stretch my hand slightly (I find that less comfortable than shifting its position), or if I were to use the right shift, then that would be quite a big movement and stretching back to reach the i (though I rarely use the right shift: my natural hand position (to not have my wrists at an angle without needing to use a split/angled keyboard) has my right pinky in the position of the p rather than the semicolon).


Re your edit: they explain it pastes the output of the command directly into your buffer, rather than temporarily showing you a shell with the output.


You can just do `:r !ls` to do that. Using <C-R>=system('ls') is way overkill.

That said I do agree that the expression register is amazing.


Oh, I missed that! Thanks for pointing that out :)


One difference is that `gI` is repeatable with `j.`, but `0i` is not.


Nice. I would love to see more advanced guides for vim.


Oh I don't want to stop there, especially when I see the crazy positive returns I get ;)

So it will come.


The number increment example doesn't seem to work for me. It says to start with this:

    1. a
    1. b
    1. c
The post says to "select in VISUAL mode the last two lines and hit `g CTRL+a`". So I guess with shift+v select the bottom two lines, and then I hit g, space, and Ctrl+a. I get:

    1. a
    2. b
    2. c
The post says that the last line should now start with 3 instead of 1, or in my case 2. The behavior is the same as when omitting g+space for me. Am I doing it wrong?


The docs for this is in `:help v_CTRL-A`.

It should actually say VISUAL BLOCK mode. This is where you use `CTRL-v` to select a block of text. You can read more in `:help blockwise-visual`.

Also see the 'nrformats' option: `:help 'nrformats'`. You can include alphabetic characters to do the same thing with A-Z, for example.

https://vimhelp.org/change.txt.html#v_CTRL-A


It seems to work in both visual block mode and visual line mode for me, for what it's worth. (When not pressing space as mentioned in another reply.)


Huh, learn something new everyday. Thanks!


That was a good point though, I should precise that it works for visual line and visual block :) thanks for that!


Don't press space. "g" and then "ctrl-a"


Thank you!


mmm sorry for that, maybe I should review the way I'm displaying the different keystrokes.


Did you try not pressing space?


evidently not :)


Thank you! Two things I really got from this:

Capital marks. I use ma or ms and `a `s a lot, but did not know you could do global marks with mA and `A. Very useful!

I've been using `` for a long time to mean "go back to the last place I was editing" but sometimes it gets overridden when navigating (I'm not super familiar with the jumplist, so maybe I need to get a better understanding) but `[ and `] are exactly what I've been looking for all these years.


> did not know you could do global marks with mA and `A.

Incidentally, the "Vimium" browser extension also supports those marks with the same syntax; they're an interesting alternative to conventional bookmarks for frequently accessed pages. https://github.com/philc/vimium/blob/master/README.md


I use global marks for things like my .vimrc (`V), .zshrc (`Z), and other config files that I edit semi-regularly. Definitely a useful tool for the box.


You should try g; and g,

You'll go through the change list. I think it's what you want.

I explain it in the article vim-intermediate on the same blog :)


Yes, thank you, I just read that article as well and that is what I was looking for =]


One of my favorites is the Vim's ability to split both horizontally ( <Ctrl>+<w>, then <v> ) and vertically ( <Ctrl>+<w>, then <s> ).


One of my goals is to become a really good vim power user. I am not able to pick up all of these but one here and there.

In the first command gf, once new file opened and then closed (wq), is there a a way to close such that the previous file that was originally opened to return? At moment returning back to shell prompt.

Also curious if there is a way to open the file as a new split screen rather than replacing the entire window?


> is there a a way to close such that the previous file that was originally opened to return?

If I understand the question correctly then the answer is ":bd" after ":w"riting the file. If you just want to switch between buffers (switch between the two files) then use ":b#". You can also list open buffers with ":ls"

Someone recently told me of :tabedit, though, and that is lookin' way nicer to me. But I haven't gotten used to it yet or know how to make it work with gf or other handy things.

As for split screen, those are hotkeys I have yet to learn. If someone has an answer to that question, please reply here so the parent commenter and I both see it :)


"Split screen" as in make a new window? Sure - `:vs` ("vertical split"), or `:vs filename.txt`. Or there's the "Windows" section on https://thevaluable.dev/vim-intermediate/ .


great got both answered perfectly!


wq writes the current file and closes the current window (which quits vim if you happen to be on the last window). The command to close the current buffer is :bd(elete).

If you want to return to the previous file without closing the new file, you can just use :bp(rev) so that the new file also stays in memory.

Relevant man page is available at :help buffers.

>Also curious if there is a way to open the file as a new split screen rather than replacing the entire window?

CTRL-W f

Another remark: vim's shortcuts are organized in a way that you are often able to guess what the correct shortcut should be. CTRL-W is the common prefix for commands regarding split windows CTRL-W v/s for splits, CTRL-W CTRL-W cycle through windows. So naturally you can guess that CTRL-W f should be open file in split and CTRL-W ] should be jump to definition in split.


This is not an answer to your first question, but I find that Ctrl-O and Ctrl-I are much more useful for getting back to your previous location -- which is normally what I want.


The section on the quickfix list should include cbuffer, which puts the contents of the current buffer in the quickfix list.


That's a good point. I'll add it :)


I can't get my location list to fill up. `:lex system("ls")` then `:llist` works (files are listed). But after `:lex system("ag something")`, `:llist` doesn't show anything.


What an amazing guide, as well as the others. I think these are the best Vim guides I've ever read. Vim user for years.


Wow, thanks a lot for the kind words!


For my daily tasks, VSCode works better than Vim. But, sometimes, I'll have to do a macro in Vim, end up staying there for a couple of days and I have to admit: using those commands, motions and macros is pretty fun. It feels like gamifying work for me, adding an extra layer of enjoyment to the frequently boring tasks.

Eventually I notice that I'm playing with Vim instead of finishing tasks, and go back to VSCode...


Is there a good way by default to wrap a visual selection in braces/parens/brackets?



Great plugin. Everything Tim Pope produces is gold. All Vim users should check out his whole collection on GitHub.


A fairly easy way to do it is either

c()^[P or c(^r")

The first leaves you in normal mode the second in insert mode. I recommend vim-surround by tpope though.


I used vim-surround for a long time, but I prefer vim-sandwich now: https://github.com/machakann/vim-sandwich


gf is a game changer! Great article – hard to soak up so much knowledge at once. Bookmarked.


I agree, it's a bit dense, but like somebody else was saying on this thread, I think picking one or two things from it and trying to include it in your worfklow for a limited period of time (say one week) will work better than trying to learn everything at once.

Then you can try new stuff the next week, and so on. Rinse and repeat :)


gx is cool, but seems broken on mac at the moment: https://github.com/vim/vim/issues/4738


It also seems broken on linux (i'm running vim 8.2 on ubuntu 18.04), sadly. Lots of github threads about it.


Towards the end of the comments in that ticket there is a solution that worked for me on mac.


Thanks for the tips! Very interesting read.


Everyone who uses Vim is an advanced user. The rest of us have to be content learning how to get back out of it without closing the terminal.


This question of what mean "intermediate", "advanced", "expert", or "master of the universe" is purely subjective to me. I've named these articles like I did to create some sort of progression, that's all.

So yes, I think you're right, like everybody :D


How does gI differ from I?


the article has a tricky typo here. it says "The keystroke `I` insert text after the first non-blank characters of the line."

but it should say _before_ the first non-blank, which is equivalent to _after_ the last leading blank.


Very good point. I'll fix that. Thanks!


`gI` will always go to column 1 whereas `I` will go first non-blank character


in other words, I = ^i and gI = 0i


The same way that 0 differs from ^.


Nice, but still wondering if I should know vim since nano is enough for me :)


I recommend learning the basics. It's great being able to navigate around a file with the keyboard in ways other than arrow keys to move a line at a time. Stuff like hitting "%" to move between matching parenthesis, { and } to move between paragraphs, etc. And if you do any kind of development, the automatic indentation (and < and > to indent and outdent ranges) are hugely useful. All of these are also things you can use in IDEs like VS Code with their vim modes.

The stuff in this article is pretty neat but (even after using vim for about 20 years) most of it doesn't click for me as easily. Eg I don't use marks; I wouldn't think ahead to leave a mark or remember afterward which letter I used for it. Kudos to people who can gain productivity by using this stuff, but I'd need something other than an explanation of what the commands do to be effective with it. I do at least try to use the quickfix stuff; that's a pretty big productivity boost over manually going to the right file and line to fix an error spotted by my compiler.


> I do at least try to use the quickfix stuff; that's a pretty big productivity boost over manually going to the right file and line to fix an error spotted by my compiler.

I've mapped cnext and cprev to tab and shift-tab, to make it easy to bounce through things.

Also, in my shell I have defined

    alias cbuf='vim - -c cbuffer!'
which lets me do things like "make | cbuf" or "git grep -n foo | cbuf", which is great.

Tangentially, all of this plays well with some things I've built out for dealing with diffs (https://github.com/dlthomas/diff-kit) - dklocs will turn a unified diff into location-prefixed changed lines, dkcov will find the intersection of a diff and a coverage report (very useful for answering the question "how did I break that test?"), either of which can be piped right into cbuf.


I like that mapping. Typing ":cnext" and ":cprev" is quite awkward. Next time I use vim's quickfix list I'll try tab + shift-tab.

I've been using VS Code + its Vim plugin recently anyway because I wanted to try out rust-analyzer and couldn't get it to work well with vim. (iirc I tried both YouCompleteMe and coc.nvim. Despite the name, the latter on classic vim; I haven't tried neovim yet. I thought they were supposed to be async, but at least one of them made vim hang for a long time when opening a fresh project, which drove me nuts.)

Neat idea also with the alias. Do you also have alias make='make 2>&1' or something? [edit: maybe make() { /path/to/make "$@" 2>&1 }] It doesn't work for me otherwise since compiler/make errors go to stderr (as they should).


> Do you also have alias make='make 2>&1' or something?

Ah, no, I probably just did `make 2>&1 | cbuf`; haven't actually been on a project using make in a while.


They aren't comparable.


I know the basics because vi ships with practically ever *nix (unlike Emacs), so it's there for when I need to edit config files, but I do 97% of my real work in something more like IntelliJ, so there just aren't big returns for getting better at vim.


My professor takes an excrutiatingly long time to edit files in class using keyb + mouse in IntelliJ. I am frequently waiting on him to finish up because of the vim plugin for IntelliJ. The speed of editing with vim, once you learn it, is shocking.


The Vi bindings in IntelliJ-based IDEs are pretty damn good in my experience. Certainly I feel a lot happier { and }'ing my way around, and having my `ce` and `gg` and `C`, in Rider.


You should just try seriously. First because many CLI have vim-like keystrokes, second because when you're lost on a remote server or on a docker container without nano you can use vi (close enough), and third because it might change your life :D


Can you tell me how to exit nano without writing changes? Every time some silly default server kicks me into nano I'm absolutely lost and ctrl+c doesn't give you help text like in vim iirc. (Not kidding)


It's CTRL+X and then N to not save changes


Thanks! Gonna try to remember that combo.


You don't even need to remember it. At the bottom it lists keyboard shortcuts, and on the left is "^X Exit". When you press it with a modified file, it gives you:

    Save modified buffer?                                                                               
    Y Yes
    N No           ^C Cancel


It appears in the table of commands at the bottom of the screen by default, doesn't it?


I don't remember exactly, I just remember having a hard time exiting nano but not vim (also not before I knew vim: it just tells you what to do when you press the usual ctrl+c). From the sibling comment to yours, it says ^X which probably wasn't clear to me is supposed to be ctrl instead of shift. With hindsight it seems quite obvious, I'll admit.


Even as a daily emacs user I got value out of learning vi a while ago. It's better for some kinds of editing and generally works better than the emacs bindings for job interview browser apps.


Superb submission! I've been using vim for over a decade and I never knew about gx or gf. Thanks!


Related, gF will also scroll down to the line number if specified as: File.txt:45

Very handy when using a terminal from within vim and looking at compiler errors/warnings or log files etc.


Nice one. Didn't know that. I'll add it to the article :)


I was meant to write a blog post like this since forever. Anyway, if you want really advanced stuff this[0] is definitely a way to go. As for ```gx``` since I'm using aerc as my email client, a trick I found very useful to open links using only the keyboard is to open an email then rq (reply with quote) search for the link and gx.

0: https://www.ele.uri.edu/faculty/vetter/Other-stuff/vi/009-in...

Edit: add the bit about aerc


Do you have any more aerc tricks? I use it and do like it but there's a whole lot of basic things even that I don't know how to do. And some things like saving file attachments that I'm not even sure if it supports or if I'm just clueless


You can browse the different parts of your email using Ctrl+J/K when you reach the attachment you can save it with :save


Wow this looks super interesting. Thanks for sharing :)


No problem, I also wrote a blog post about Vim sessions[0]. I think it definitely counts as intermediate/advanced vim material. Great work with your article.

0: https://rmpr.xyz/Vim-Session/


Nice! I did that for managing my vim sessions:

" Save session nnoremap <leader>ss :mksession! $VIMCONFIG/sessions/ " Reload session nnoremap <leader>sl :so $VIMCONFIG/sessions/


Same here, with vim it's so easy to so learning once you can navigate comfortably, but I'm really glad I know this now!


Thanks for the kind words! I'm happy it helps.


Use nano




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: