what is this barbaric default binding, s/S shall be bound to the indispensable vim-surround! ;)
built in text objects are already nice combined with modifiers (e.g cw, ciw, caw, cW, ciW, caW), ramping it up a little bit with argument (ca, cia, daa), surround (cs"', cs([, ci", ca", viWS<tag>, dst), and indent (vii>) is stupidly powerful.
Once the [verb][modifier]{object} pattern clicked for me my mind was hopelessly infected and I could not ever go back to non-vim editors.
To me that's the risk of learning vim: if it starts clicking for you (which it may not, as pure personal fit, not elitism) then it becomes excruciatingly annoying to edit non trivial amounts of text and not have vim.
I can relate to this. Writing code without Vim feels like punching in a dream. Make sure you're working in an environment where you're allowed to use such tools.. I'm currently not and it stinks.
cs"': change surrounding quotes, e.g. "foo" -> 'foo'
cs([: change surrounding brackets, e.g. (foo) -> [ foo ]
( cs]) would omit the spaces)
ci": change in quotes, e.g. "foo" -> "" (in insert mode between the quotes)
ca": change around quotes, e.g. x"foo"y -> xy (in insert mode between x and y
viWS<tag>: add tag around current word using visual mode, e.g. foo -> <tag>foo</tag>
dst: delete surrounding tag, e.g. <tag>foo</tag> -> foo
It's really good. I'm not familiar with argument.
Vim normal mode commands have something like this structure, a bit like a sentence:
[verb][modifier]{object}
There can be a bit of implicit in this sentence, e.g [verb] and [modifier] are optional. {object} is mandatory, final, and "commits" the normal mode command without having to press something such as Return.Let's look at some basic movements:
w => word (== [A-Za-z0-9_]), forward lookup
W => Word (== \S i.e not whitespace), forward lookup
b => word, backward lookup
B => Word, backward lookup
Without [verb] it's like the implicit [verb] of the sentence is "move", so typing just these indeed results in pure movement: w => move (from cursor position to) one word forward
W => move (from cursor position to) one Word forward
b => move (from cursor position to) one word backward
B => move (from cursor position to) one Word backward
With a count modifier 2w => move (from cursor position to) two words forward
2W => move (from cursor position to) two Words forward
2b => move (from cursor position to) two words backward
2B => move (from cursor position to) two Words backward
With [verb], things are applied to the object. Let's look at some verbs: d => delete
c => change (== "delete + enter insert mode", exit with ESC)
A repeated verb is a shortcut to mean "whole line" dd => delete line
cc => replace line
d2d => delete two lines
Back to the sentence. It's a bit like the movement implicitly defines a "text range" object: "from current cursor position to movement target". So: dw => delete (from cursor position to) one word forward
dW => delete (from cursor position to) one Word forward
db => delete (from cursor position to) word backward
dB => delete (from cursor position to) one Word backward
The interesting part about c instead of doing d+i is that it is going to be "atomic" (end these with ESC to exit insert mode): cwfoo => replace (from cursor position to) one word forward with "foo"
cWfoo => replace (from cursor position to) one Word forward with "foo"
cbfoo => replace (from cursor position to) word backward with "foo"
cBfoo => replace (from cursor position to) one Word backward with "foo"
Which is really cool because now you can move to another position and press . (dot) and it will repeat the last normal mode command, dynamically/semantically, like a mini macro. Combine that with search, and incremental search/apply is n to go to the next occurence and . to apply, or n again when I want to skip. Also, u for undo goes back from atomic change to atomic change.Similar to wWbB there's "find" and "find until" (n is an optional count, defaults to 1):
[n]f{char} => find nth occurence of character, forward lookup, after character
[n]F{char} => find nth occurence of character, backward lookup, after character
[n]t{char} => find nth occurence of character, forward lookup, before character
[n]T{char} => find nth occurence of character, backward lookup, before character
So e.g some examples, combining two sentences: F,dt) => move back to previous comma, then delete (from cursor position to) until next parens
t)dF, => move to last parens, then delete (from cursor position to) up to previous comma
These are useful to e.g delete arguments! e.g with the cursor somewhere inside parens here: foo(42, 123, 345)
Note that depending on what the initial cursor position is within parentheses the result will differ. Also note that this is good for simple arguments, we'll address more complex ones later.Words can receive a "inside" or "around" modifier:
iw => one word, up to word boundaries
iW => one Word, up to Word boundaries
aw => one word, up to word boundaries, plus trailing whitespace
aW => one word, up to word boundaries, plus trailing whitespace
What's cool about this is: you don't have to position at word beginning, and it can handle whitespace as you see fit.So given this text:
foo bar baz
Doing diw with the cursor inside bar would delete "bar" but leave you with two spaces while doing daw would delete "bar " thus leaving you with only one.Now, i and a modifiers don't just take words:
i" => inside double quotes, excluding quotes
i' => inside single quotes, excluding quotes
a" => inside quotes, including quotes + trailing whitespace
They're also smart about delimiters (also works with a but by now you should get the idea): i< => inside a < > pair
i{ => inside a { } pair
i[ => inside a [ ] pair
i( => inside a ( ) pair
a< => including < > pair
Guess what these do: ci<span
da[
These are all built-in, let's go further with tpope's vim-surround: s" => surrounding quotes
s< => surrounding <> pair
s[ => surrounding [] pair
Which you can use this way: ds" => delete surrounding quotes
cs"' => change double quotes to single quotes
cs(( => change closest parens around to parens with one space inside
cs() => change closest parens around to parens without space inside
cs2() => change 2nd level outwards parens to parens without space inside
dst => delete surrounding tag
cst<foo> => change surrounding tag to be "<foo>" (closed by "</foo>")
None of these affect the enclosed text, only the surrounding items.Visual mode is entered by pressing v (or V for visual line mode, or ^v for visual block mode) then moving around, and the object will be the highlighted area. Since {object} is what ends the vim normal mode sentence things are a bit different in visual mode:
viWS( => surround Word with parens + inner space
viWS<foo> => surround Word with <foo> and </foo>
Usage of visual mode and visual line mode is greatly improved with % (move to matching delimiter), especially with vim-endwise (which makes % work on more per-filetype things such as tags in HTML, and def/end in Ruby)Other nice plugins are argtextobj:
dia => delete argument but not comma, properly handling parens and commas as argument delimiters as well as nesting
daa => delete argument + one comma (leading comma, unless first arg then trailing comma) + surrounding whitespace
and vim-indent-object (cool in general, but stellar on yaml or python): cii => replace indented block, leaving out surrounding lines
cai => replace indented block + surrounding lines + one line above
caI => replace indented block + surrounding lines + one line above + one line below
vii> => visual line select indented block, then increase indent
vii< => visual line select indented block, then increase indent
viigc => visual line select indented block, then comment (using vim-commentary)
vii:norm i# => visual line select indented block, then prepend line with # (pure vim)
via= => fix indentation
viagq => hard wrap lines
I often do the "comment" one using visual block mode (^v) and moving around with % then do I# (insert # at line beginning) when I can rely on delimiters. Same for indent increase/decrease and fix, only with visual line mode.Wrapping one's head around this vim interactive "sentence" dialect of normal mode is not that hard but usually badly explained (I hope I did a slightly less bad job at that!), but it's key to unlock some of vim's insanely composable superpowers.
The nice thing is that by understanding how these work together, I can replicate the behaviour of these plugins in a plugin-less vim (at a cost of a bit more thinking+typing), so I save time when I have the plugins but gracefully degrade to something still more efficient than painstakingly moving my cursor around and deleting/changing every text character by hand.
Next step: macros!
qa => start recording macro in register "a" (can be about any other key)
q => end macro recording
@a => play macro in register "a"
@@ => play last macro again (such a timesaver)
And a little bonus: "ap => paste macro from register "a" (which you can then edit!)
"ayy => yank line back in register "a"
All of this combined is ridiculously powerful, to the point I can barely hold nervous laughter at some hilariously complex refactoring I did.A few links:
https://github.com/tpope/vim-surround
https://github.com/vim-scripts/argtextobj.vim
I want to add that the converse is true too: there are a lot of plugins (for IDEs) that do a lot of what Vim can do. I personally use the neovim integration for VSCode and it's really nice.
Probably you replied around the same time as I edited in:
>> Try it for one month and see if you want to go back to standard editors. Odds are, you'll want the best of both worlds, start looking for vim key binding plugins in IDEs, and end up like me with missing vim after all because those keybind plugins are usually not so great
It might just be me who got too used to vim bindings by now so that the suboptimal implementations are annoying. Or maybe the vim bindings in VSCode are particularly good, I haven't tried those. (I've tried them in real visual studio in ~2015 and it was so bad that I ended up using plain visual studio instead, then went back to plain vim on linux after my C# internship.)
Apparently the use of hjkl has to do with the fact that on some keyboards, those WERE the arrow keys!
That's because you're doing it wrong! Hjkl are fallbacks to move a few characters, it has only a minor advantage over arrow keys are is probably not worth the effort of re-learning how to move a cursor.
I would not recommend learning that as your first vim move. Just stick to cursors for moving individual characters at first.
The real magic is in going back/forward words (b/w/e) without needing to reach for control at the edge of your keyboard, jumping to an exact point you need (f/F/t/T) if it has a unique character (like the ')' within 'if (uid == 0)'), moving between paragraphs ({/}), going to matching parens/braces/brackets (%), going to the next/previous word under the cursor (*/#), etc. This is what will make you faster, not the switching from arrows to hjkl.
And that's just cursor moving. For editing, things like S to replace the line or C to replace the rest of the line after the cursor, di) to cut the part within parentheses and p (paste) it elsewhere, repeating the previous action with dot, searching with n/N instead of a function key, all those things are what makes vim nicer as a text editor. One of the least-appreciated things that I also use a lot is filename completion with ctrl+xf (sysadmins, take note!).
But I don't recommend learning everything at once. If you know how to go into insert mode (i), how to leave insert mode (escape), and how to save a file, then you basically have the equivalent of a text editor, especially with mouse mode enabled (the default nowadays, I think). From there, learn one command every week. Takes 1 minute per week and you'll know more vim than 99% of developers within a year.
Or go a bit faster and try to learn one command every morning, or every second day perhaps. Try it for one month and see if you want to go back to standard editors. Odds are, you'll want the best of both worlds, start looking for vim key binding plugins in IDEs, and end up like me with missing vim after all because those keybind plugins are usually not so great ^^'
> mostly using Java IDEs, and lately some vscode (for go, typescript, rust)
Do you need intelligent autocompletion, debugging, and other such IDE features?
There are plugins that, so far as I know, can do a lot of what an IDE can do, but I have never bothered to set them up myself. It all seems hacky and niche, but others can tell you more if this is the type of things you're looking for. I am not a real developer myself but I write a lot of scripts (things small enough to keep a full understanding in my head) as well as documents (reports for pentests) in vim, and for that it is perfect.