diff --git a/chan.dev/src/content/posts/change-supermotion.md b/chan.dev/src/content/posts/change-supermotion.md new file mode 100644 index 00000000..b1ded21f --- /dev/null +++ b/chan.dev/src/content/posts/change-supermotion.md @@ -0,0 +1,523 @@ +--- +title: Change +date: 2024-10-15 +docs: + - ':help motion.txt' + - ':help text-objects' + - ':help change.txt' + - ':help change' + - ':help delete' + - ':help count' + - ':help visual.txt' + - ':help mini.ai' + - ':help mini.surround' +--- + + + +People suck at teaching Vim. + +Why? +Because it's good software. +Good software is composable. +And composability isn't naturally apparent virtue. + +So we'll learn a little differently. +Instead of working our way up from basics, +we'll treat each operation atomically and exhaustively. + +This guide throws you right into the deep end, with the exact commands you'll run for any given *change*. + +Worst case, you learn nothing. +Second-worst case, you learn one of Vim's best capabilities (and nothing else). +Best case, you master these individual commands and experience the strong undercurrent of composability in each lesson. + +Obviously I think you and I can get to that bast case scenario or I wouldn't have written this. + +Wanna learn some Vim? + +## Contents + +## Intent: the power of Vim + +We often need to change text inside quotes, delete extranious words, or manipulate sentences and paragraphs. +But in traditional editors we spend a lot of time making text selections with mouse. + +Intent is what mave Vim so powerful. + +If we want to change the text inside a quote, we don't need to visually select it first. +We communicate our full intent through operations (keyboard commands). +ci" does exactly what we want with just four characters. + +We'll cover 100 such operations in this guide with practical examples of where they're most useful in code. + +## How to read this guide + +Lessons in this guide include 3 parts: + +1. A description of the editing intent +1. An exhaustive table of Vim operations to achieve that intent +1. Notes on exceptions, shortcomings, and considerations + +We start with the base operation `ci` (change inner). +This is my favorite intent and Vim's most capable. + +"Change inner" is the termonoligy you'll find in the docs. +But I make an additional distinction of "inside". +"Inside" is used to distinguish "inner" selections that are inside quotes. + +Finally, every one of these commands is run in `Normal mode`. +In normal mode, you'll type each character in the operation. +Entry speed should not matter in stock Vim/neoVim. + +--- + +## Change inside quotes + +Change text inside the current (or next) pair of quotes. + +| Operation | Description | +| --- | --- | +| ci' | change inside single quotes | +| ci" | change inside double quotes | +| ci` | change inside backticks | + +*Note: The cursor does not need to be inside the target quotation to be changed. But it does need to be before the quotation.* + +--- + +## Change inside brackets, braces, and parentheses + +Change text inside the current (or next) pair of braces, brackets, and parentheses. + +| operation | Alternative | Alias | Description | +| --- | --- | --- |--- | +| ci( | ci) | cib | change inside parentheses | +| ci{ | ci} | ciB | change inside curly braces | +| ci[ | ci] | | change inside square brackets | +| ci< | ci> | | change inside angle brackets | + +Both opening and closing characters target the same selection. +But I recommend committing to the opening character. +Here's why: + +1. Committing to an operation creates stronger muscle memory. +1. Minimal, columnar-style keyboards often remove physical representation of closing characters. So choosing the right side could require additional keystrokes, if a columnar-style keyboard is in your future. + +*Note: Parentheses and curly braces have aliases that are easier to type.* + +--- + +## Change inner word, sentence, or paragraph + +Change the word, sentence, or paragraph under the cursor. +This works indifferent to the cursor's position inside that selection. + +| Operation | Description | +| :--- | :--- | +| ciw | change inner word | +| ciW | change inner WORD (includes punctuation) | +| cis | change inner sentence (determined by punctuation) | +| cip | change inner paragraph (determined by whitespace) | + +--- + +## Repeat operations + +It can be useful to repeat an operation. + +Prefixing an operation with a number will execute that operation the number of times spcefied. +This number is referred to as an operation's `count`. + +These are the operations from the previous lesson with various counts. + +| operation | Description | +| --- | --- | +| 2ciw | change inner word under cursor and the next | +| 3ciW | change inner WORD under cursor and next 2 WORDs (includes punctuation) | +| 3cis | change inner sentence under cursor and the next | +| 2cip | change inner paragraph under cursor and the next | + + +--- + +## Change around + +OK. Let's talk about text selection composition now. + +Vim operations are composable. +For the curious, the existance of an `inner` distinction suggests an "outer". + +And Vim delivers. + +This distinction is `around` (think "including"). + +- "Change around (including) double quotes." +- "Change around sentence (including whitespace and punctuation)." +- "Change around block (including curly braces)." + +For *every* `inner` operation we've learned, there is a corresponding `around` operation, which *includes* characters and/or whitespace. + +This table is every `ci` command we've learned, but using the `ca` (around) variation. + +| Operation | Alternative | Alias | Description | +| --- | --- | --- |--- | +| ca' | | | change around (including) single quotes | +| ca" | | | change around (including) double quotes | +| ca` | | | change around (including) backticks | +| ca( | ci) | cib | change around (including) parentheses | +| ca{ | ci} | ciB | change around curly braces | +| ca[ | ci] | | change around (including) square brackets | +| ca< | ci> | | change around (including) angle brackets | +| caw | | | change arround word (including whitespace) | +| caW | | | change arround WORD (including punctuation and whitespace) | +| cas | | | change around sentence (determined by punctuation. including whitespace) | +| cap | | | change arround paragraph (determined by whitespace. including whitespace) | + +--- + +## What is a text object? + +Text objects are an important concept in Vim. +And one you now have experience with. + +`i"` (inside double quotes) is a text object. +`ab` (around parentheses) is a text object. +`iW` (inner WORD) is a text object. + +Think of these as queries &mdash or text selections. +I've referred to them as "selections" up to this point. + +These text objects are composable. +They can be used with commands other than `c` (change). + +Let's learn how… + +--- + +## Delete, don't change + +Let's talk more command composition. + +Our base `c` (change) command is actually two commands in one: `d` (delete) followed by `i` (insert). +And anything composed can be decomposed. + +So what happens if we swap the decomposed `d` command, instead of the (composed) `c` command? + +We get a new set of operations that delete text objects *and* keep us in *Normal mode*. +This is helpful when text objects need only be deleted (not replaced). + +Let's look at the board to see every `c` operation, as a `d` operation. + +| Operation | Alternative | Alias | Description | +| --- | --- | --- |--- | +| da' | | | delete around (including) single quotes | +| da" | | | delete around (including) double quotes | +| da` | | | delete around (including) backticks | +| da( | ci) | cib | delete around (including) parentheses | +| da{ | ci} | ciB | delete around (including) curly braces | +| da[ | ci] | | delete around (including) square brackets | +| da< | ci> | | delete around (including) angle brackets | +| daw | | | delete around word (including whitespace)| +| daW | | | delete around WORD (including punctuation and whitespace) | +| das | | | delete around sentence (determined by punctuation. including whitespace) | +| dap | | | delete around paragraph (determined by whitespace. including whitespace) | +| di' | | | delete inner single quotes | +| di" | | | delete inner double quotes | +| di` | | | delete inner backticks | +| di( | ci) | cib | delete inner parentheses | +| di{ | ci} | ciB | delete inner curly braces | +| di[ | ci] | | delete inner square brackets | +| di< | ci> | | delete inner angle brackets | +| diw | | | delete inner word (including whitespace)| +| diW | | | delete inner WORD (including punctuation and whitespace) | +| dis | | | delete inner sentence (determined by punctuation. including whitespace) | +| dip | | | delete inner paragraph (determined by whitespace. including whitespace) | + + +--- + +## Count revisited + +Now that you know know about *commands* and *text objects*, +There are things you need to consider when using *counts*. + +Applying a count runs the command in sequence &mdash as if you typed it multiple, consecutive times. + +Consider 3ci" (3x, change inner double quotes). + +1. Delete existing text in the double quotes and places the insert cursor inside the quotes. +1. (Now inside quotes) perform the the same task — resulting in no descernable change. +1. (Still inside quotes) perform the the same task — resulting in no descernable change. + +This is even more confusing when manipulating words, sentences, and paragraphs. +Consider 3cis (3 times change inner word). + +1. Delete the sentence under the cursor, ignoring whitespace. +1. (Now over the remaining whatespace) perform the same task — deleting that remaining whitespace. +1. (Now with a new sentence under the cursor) perform the same task — deleting the next sentence (also ignoring whitespace). + +This effect repeats, feeling like an off-by-one error. + +For these reasons, repeating over `inner` operations is largely flawed. +So I favor `around` for when performing counted operations. + +*Note: See plugins for the ability to add a count to text objects.* + +--- + +## The composability of operations + +We now have 44 discrete operations, which can be repeated indefinitely. +That's a lot. + +So how can we think about them composably? +Let's start with anatomy. + +`() ` + +Let's break down a few as examples. + +ciw +| segment | value | description | +| --- | --- | --- | +| count| none | (default: 1x) | +| command| c | change | +| text object| iw | inner word | + +2cib +| segment | value | description | +| --- | --- | --- | +| count| 2 | 2x | +| command| c | change | +| text object | ib | inner parentheses | + +3dap +| segment | value | description | +| --- | --- | --- | +| count| 3 | 3x | +| command| d | delete | +| text object | ap | around paragraph | + +Every operation you've learned can be decomposed into these three segments. + +--- + +## Use plugins for extended functionality + +The [`mini.nvim/ai`][mini/ai] plugin includes additional selection tools and aliases. + +Check out their documentation for additional features. +Here are are a few operations that I use often. + +| Operation | Description | +| --- | --- | +| ci* | Change inside (arbitrary) `*` (useful in Markdown)| +| ci_ | Change inside (arbitrary) `_` (useful in Markdown, or renaming snake_case variables)| +| ci/ | Change inside (arbitrary) `/` (useful with Regex)| +| ci? | Change 2 | +| ciq | Change inside quotes (`'`, `"`, `) | +| cinq | Change inside next quotes | +| cilq | Change inside last quotes | +| cif | Change inside function call | +| cia | Change inside argument | +| c2ia | Change inside 2nd argument | +| d2aa | Delete 2nd argument in function call/definition | +| c2it | Change 2 inner tags (atomic selection) | + +--- + +## Take selection farther with motions + +We did not cover motions. + +Motions are are not text objects. +They are Vim's way of moving the cursor. +However, like text objects, they can be used with commands to perform operations. + +This is a comprehensive (but not exhaustive) list of motions composed with the `c` command. + +| command | alternative | alias | description | +| --- | ---| --- | --- | +| cb | | | change to beginning of the previous word | +| cB | | | change to beginning of the previous WORD (includes punctuation and whitespace)| +| ce | | | change to end of the word (includes punctuation and whitespace)| +| cE | | | change to end of the WORD (includes punctuation and whitespace)| +| cf `` | | | change through next (find) `` | +| cF `` | | | change through previous (find) `` | +| cgg | | | change to start of file | +| cG | | | change to end of file | +| ck | c⬆︎ | | change current and previous line | +| cj | c⬇︎ | | change current and next line | +| cl | c➡︎ | s | change character under cursor | +| ch | c⬅︎ | | change character before cursor | +| cH | | | change to top of screen (excluding padding) | +| cM | | | change to the middle of the screen | +| cL | | | change to bottom of screen (excluding padding) | +| cn | | | change to next `search result` | +| cN | | | change to previous `search result` | +| cp | | | change to previous `search result` | +| cP | | | change to previous `search result` | +| ct `` | | | change to next `` | +| cT `` | | | change to previous `` | +| cw | | | change to start of next word | +| cW | | | change to start of next WORD (includes punctuation and whitespace)| +| c0 | | | change (from cursor) to start of line | +| c$ | | C | change (from cursor) to the end of the line | +| c# | | | change to previous word under cursor (word, if none found) | +| c* | | | change to next word under cursor (word, if none found) | +| c% | | | change to matching character pair (forward or backward) | +| c^ | | | change to first non-whitespace character on the current line | +| c) | | | change to the beginning of the current sentence | +| c) | | | change to the end of the current sentence | +| c{ | | | change to the beginning of the current paragraph | +| c} | | | change to the end of the current paragraph | +| c- | | | change to the first non-whitespace character on the previous line | +| c+ | | | change to the first non-whitespace character on the next line | + +*Note: a few omissions: [[, [], ]], ][, etc. `:help object-motions.txt`* + +These are additional text object text selections. + +| command | alias | description | +| --- | --- | ---| +| cl | s | change character | +| cgn | | change the next search pattern match | +| cg_ | | change from cursor to the last non-blank character of the line | +| cc | | change current line | +| c$ | C | change from the cursor to the end of the line | + +There are no interactions with the `g` command prefix. Test. + +--- + +## Debug text object selections with Visual mode + +You may feel uneasy about text objects. +This is natural as we're used to visualizing our selections. + +This is where Vim's Visual mode comes into play. + +We can replace the `c` and `d` commands with `v` to first perform a visual selection. +Then, with that selection, post-fix the `c` or `d` command. + +Here are a few examples using Visual mode with text objects and motions: + +| operation | description | +| --- | --- | +| viwc | visual select inner word and change | +| vapd | visual select around paragraph and delete | +| vi"c | visual select inside double quotes and change | +| vt*d | visual select to `*` and delete | +| vT-c | visual select backward to `-` and delete | + +*Note: Count is not supported in Visual mode. This makes sense when you consider that count repeats commands, it does not compound them. Instead prefixing `v` with a number will select 11 × characters.* + +--- + +## Common workflows + +### HTML/XML + +| operation | description | +| --- | --- | +| cit | change inside tag | +| ci" | change inside double quotes | +| ct" | change to double quote (to beginning of string attribute) | +| cT" | change back-to doble quote (to beginning of string attribute) | +| ci< | change tag name | +| ct | change to space (handy for changing individual class names and other string attributes) | +| cT | change back-to space (handy for changing individual class names and other string attributes) | +| ciw | change string attribute segment (separated by non-word characters, e.g., `-`, `_`, `.`, etc.) | + +*Note: Remember that that these operations can use `d` (delete) and `v` (visual) commands and/or `a` (arround) text objects selections.* + +**Additional commands with [`mini.nvim/ai`][mini/ai]** + +| operation | description | +| --- | --- | +| c``it | change inside (ansestor) tags | +| cilq | change inside last quotes (universal) | +| c``inq | change inside next `` quotes (universal) | +| c``inB | change inside next `` curly brarces | + +```html title="sample.html" + + + title + + +
+

Page title

+
+ + +``` + +### React + +*All operations from HTML section apply.* + +| operation | description | +| --- | --- | +| cib | change inside parentheses | +| ciB | change inside curly braces | +| ca"{ | change attribute in quotes and insert `{` to interpolate variable. | + + + +**Additional commands with [`mini.nvim/ai`][mini/ai] and [`mini.nvim/surround`][mini/surround]** + + +| operation | description | +| --- | --- | +| cif | change function call arguments | +| cia | change argument under cursor (or next) | +| ``cia | change argument at `` position | +| sr"\` | surround replace `"` with ` to allow interpolation | +| ``dia | change argument at position ` `| +| daa | (ripple) delete first argument | +| d``aa | delete argument at position `` | +| c``inB | change inside next `` curly brarces | +| srq? | replace surrounding quotes (universal) with `` ({\`, \`}) (for changing string to variable interpolation) | + +```jsx +function(a,b,c) { + return a + b + c; +} + +function({a,b = 1, ...c}) { + return a + b + c; +} + +export default function (a, b, c) { + return ( + + title + + +
+

Page title

+
+ + ) +} +``` + +### Markdown/prose + +[[: go to previous heading +]]: go to next heading +ci*/ci_: change inside em/strong +ci*/ci_: change inside em/strong +cis/cas: change sentance +cip/cap: change paragraph + +[mini/ai]: https://github.com/echasnovski/mini.nvim/blob/main/lua/mini/ai.lua