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.
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.
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.
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 '>).)
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.
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.
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!
> 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.
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.
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?
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.
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.
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.
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
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.
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.
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
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.
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.
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.
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.
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.
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.
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".
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
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?
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?
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).
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?
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.
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/ .
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.
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.
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...
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 :)
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.
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).
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)
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:
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.
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.
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
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.
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!