Earlier quoted context omitted.
> things like S to replace the line 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 , dst), and indent (vii>) is stupidly powerful. Once the [verb][modifier]{object} pattern clicked…
Most people I know (including many linux users) regard me as a vim pro relative to them, and I still have barely an idea what most of these do. Or what surround is. Might want to explain a little for the rest of us mortals ;)
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
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 => change surrounding tag to be "" (closed by "")
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 => surround Word with and
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