3 ms·
> 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
by lloeki 4y ago
> 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<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.
- Aachen 4y agoMost 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 ;)
- tomn 4y agoSurround is a plugin which adds more text objects: https://github.com/tpope/vim-surround https://github.com/tpope/vim-surround 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.
- lloeki 4y agoSure. Here's a quick rundown of how it works (in my mind at least, might not be 100% technically exact): 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/tpope/vim-surround https://github.com/vim-scripts/argtextobj.vim https://github.com/vim-scripts/argtextobj.vim https://github.com/michaeljsmith/vim-indent-object https://github.com/michaeljsmith/vim-indent-object https://github.com/tpope/vim-commentary https://github.com/tpope/vim-commentary
- throwaway_au_1 4y ago>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.