# HG changeset patch # User Steve Losh # Date 1318103990 14400 # Node ID 9a45509b67138cc059395c261abac63daaedf278 # Parent 48c5cc292f9932847d71539c411d96d7172cf697 Tons more. diff -r 48c5cc292f99 -r 9a45509b6713 chapters/09.markdown --- a/chapters/09.markdown Sat Oct 08 00:51:14 2011 -0400 +++ b/chapters/09.markdown Sat Oct 08 15:59:50 2011 -0400 @@ -32,7 +32,7 @@ You've seen a bunch of simple mappings so far, so it's time to look at something with a bit more meat to it. Run the following command: - :nnoremap " viwa"hviwoi"lel + :nnoremap " viwa"hbi"lel Now *that's* an interesting mapping! First, go ahead and try it out. Enter normal mode, put your cursor over a word in your text and type `"`. Vim @@ -41,7 +41,7 @@ How does this work? Let's split it apart into pieces and think of what each one does: - viwa"hviwoi"lel + viwa"hbi"lel * `viw`: visually select the current word * ``: exit visual mode, which leaves the cursor on the last character of @@ -50,10 +50,7 @@ * `"`: insert a `"` into the text, because we're in insert mode * ``: return to normal mode * `h`: move left one character -* `viw`: visually select the word once again -* `o`: move the cursor to the opposite side of the visual selection -* ``: exit visual mode, which leaves the cursor on the *first* character of - the word this time +* `b`: move back to the beginning of the word * `i`: enter insert mode *before* the current character * `"`: insert a `"` into the text again * ``: return to normal mode diff -r 48c5cc292f99 -r 9a45509b6713 chapters/14.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chapters/14.markdown Sat Oct 08 15:59:50 2011 -0400 @@ -0,0 +1,130 @@ +Autocommand Groups +================== + +A few chapters ago we learned about autocommands. Run the following command: + + :autocmd BufWrite * :echom "Writing buffer!" + +Now write your current buffer with `:write` and run `:messages` to view the +message log. You should see the "Writing buffer!" message in the list. + +Now write your current buffer again and run `:messages` to view the message log. +You should see the "Writing buffer!" message in the list twice. + +Now run the exact same autocommand again: + + :autocmd BufWrite * :echom "Writing buffer!" + +Write your current buffer one more time and run `:messages`. You will see the +"Writing buffer!" message in the list *four* times. What happened? + +When you create an autocommand like this Vim has no way of knowing if you want +it to replace an existing one. In our case, Vim created two *separate* +autocommands that each happen to do the same thing. + +The Problem +----------- + +Now that you know it's possible to create duplicate autocommands, you may be +thinking: "So what? Just don't do that!" + +The problem is that sourcing your `~/.vimrc` file rereads the entire file, +including any autocommands you've defined! This means that every time you +source your `~/.vimrc` you'll be duplicating autocommands, which will make Vim +run slower because it executes the same commands over and over. + +To simulate this, try running the following command: + + :autocmd BufWrite * :sleep 200m + +Now write the file. You may or may not notice a slight sluggishness in Vim's +writing time. Now run the command three more times: + + :autocmd BufWrite * :sleep 200m + :autocmd BufWrite * :sleep 200m + :autocmd BufWrite * :sleep 200m + +Write the file again. This time the slowness will be more apparent. + +Obviously you won't have any autocommands that do nothing but sleep, but the +`~/.vimrc` of a seasoned Vim user can easy reach 1,000 lines, many of which will +be autocommands. Combine that with autocommands defined in any installed +plugins and it can definitely affect performance. + +Grouping Autocommands +--------------------- + +Vim has a solution to the problem. The first step is to group related +autocommands into named groups. + +Open a fresh instance of Vim to clear out the autocommands from before, then run +the following commands: + + :augroup testgroup + : autocmd BufWrite * :echom "Foo" + : autocmd BufWrite * :echom "Bar" + :augroup END + +The indentation in the middle two lines is insignificant. You don't have to +type it if you don't want to. + +Write a buffer and check `:messages`. You should see both "Foo" and "Bar". Now +run the following commands: + + :augroup testgroup + : autocmd BufWrite * :echom "Baz" + :augroup END + +Try to guess what will happen when you write the buffer again. Once you have +a guess in mind, write the buffer and check `:messages` to see if you were +correct. + +Clearing Groups +--------------- + +What happened when you wrote the file? Was it what you expected? + +If you thought Vim would "replace" the group, you can see that you guessed +wrong. Don't worry, most people think the same thing at first. + +When you use `augroup` multiple times Vim will *combine* the groups each time. + +If you want to *clear* a group you can use `autocmd!` inside the group. Run the +following commands: + + :augroup testgroup + : autocmd! + : autocmd BufWrite * :echom "Cats" + :augroup END + +Now try writing your file and checking `:messages`. This time Vim only echoed +"Cats" when you wrote the file. + +Using in Your Vimrc +------------------- + +Now that we know how to group autocommands and clear those groups, we can use +this to add autocommands to `~/.vimrc` that don't add a duplicate every time we +source it. + +Add the follow to your `~/.vimrc` file: + + augroup filetype_html + autocmd! + autocmd FileType html nnoremap f Vatzf + augroup END + +We enter the `filetype_html` group, immediately clear it, define an autocommand, +and leave the group. If we source `~/.vimrc` again the clearing will prevent +Vim from adding duplicate autocommands. + +Exercises +--------- + +Go through your `~/.vimrc` file and wrap *every* autocommand you have in groups +like this. You can put multiple autocommands in the same group if it makes +sense to you. + +Try to figure out what the mapping in the last example does. + +Read `:help autocmd-groups`. diff -r 48c5cc292f99 -r 9a45509b6713 chapters/15.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chapters/15.markdown Sat Oct 08 15:59:50 2011 -0400 @@ -0,0 +1,155 @@ +Operator-Pending Mappings +========================= + +In this chapter we're going to explore one more rabbit hole in Vim's mapping +system: "operator-pending mappings". Let's step back for a second and make sure +we're clear on vocabulary. + +An operator is a command that waits for you to enter a movement command, and +then does something on the text between where you currently are and where the +movement would take you. + +Some examples of operators are `d`, `y`, and `c`. For example: + + Operator + vvvvvv + dw " Delete to next word + ci( " Change inside parens + yt, " Yank until comma + ^^^^^^^^^^^ + Movement + +Vim lets you create new movements that work with all existing commands. Run the +following command: + + :onoremap p i( + +Now type the follow text into a buffer: + + return person.get_pets(type="cat", fluffy_only=True) + +Put your cursor on the word "cat" and type `dp`. What happened? Vim deleted +all the text inside the parentheses. You can think of this new movement as +"parameters". + +The `onoremap` command tells Vim that when it's waiting for a movement to give +to an operator and it sees `p`, it should treat it like `i(`. When we ran `dp` +it was like saying "delete parameters", which Vim translates to "delete inside +parentheses". + +We can use this new mapping immediately with all operators. Type the same text +as before into the buffer (or simply undo the change): + + return person.get_pets(type="cat", fluffy_only=True) + +Put your cursor on the word "cat" and type `cp`. What happened? Vim deleted +all the text inside the parentheses, but this time it left you in insert mode +because you used "change" instead of "delete". + +Let's try another example. Run the following command: + + :onoremap b /return + +Now type the following text into a buffer: + + def count(i): + i += 1 + print i + + return foo + +Put you cursor on the `i` in the second line and press `db`. What happened? +Vim deleted the entire body of the function, all the way up until the "return", +which our mapping used Vim's normal search to find. + +When you're trying to think about how to define a new operator-pending movement, +you can think of it like this: + +1. Start at the cursor position. +2. Enter visual mode (charwise). +3. ... mapping keys go here ... +4. All the text you want to include in the movement should now be selected. + +It's your job to fill in step three with the appropriate keys. + +Changing the Start +------------------ + +You may have already seen a problem in what we've learned so far. If our +movements always have to start at the current cursor position it limits what we +can do. + +Vim isn't in the habit of limiting what you can do, so of course there's a way +around this problem. Run the following command: + + :onoremap in( :normal! f(vi( + +This might look frightening, but let's try it out. Enter the following text +into the buffer: + + print foo(bar) + +Put your cursor somewhere in the word "print" and type `cin(`. Vim will delete +the contents of the parentheses and place you in insert mode between them. + +You can think of this mapping as meaning "inside next parenthesis", and it will +perform the operator on the text inside the next set of parenthesis on the +current line. + +Let's make a companion "inside last parenthesis" ("previous" woud be a better +word, but it would shadow the "paragraph" movement). Run the following command: + + :onoremap il( :normal! F)vi( + +Try it out on some text of your own to make sure it works. + +So how do these mappings work? First, the `` is something special that you +can ignore for now -- just know that it needs to be there to make the mappings +work in all cases. If we remove that we're left with: + + :normal! F)vi( + +`:normal!` is something we'll talk about in a later chapter, but for now it's +enough to know that it's a command that can be used to simulate pressing keys in +normal mode. For example, running `:normal! dddd` will delete two lines, just +like pressing `dddd`. The `` at the end of the mapping is what executes the +`:normal!` command. + +So now we know that the mapping is essentially just running the last block of +keys: + + F)vi( + +This is fairly simple: + +* `F)`: Move backwards to the nearest `)` character. +* `vi(`: Visually select inside the parentheses. + +We end up with the text we want to operate on visually selected, and Vim +performs the operation on it as normal. + +General Rules +------------- + +A good way to keep the multiple ways of creating operator-pending mappings +straight is the remember the following two rules: + +* If your operator-pending mapping ends with some text visually selected, Vim + will operate on that text. +* Otherwise, Vim will operator on the text between the original cursor position + and the new position. + +Exercises +--------- + +Create operator-pending mappings for "around next parenthesis" and "around last +parenthesis". + +Create similar mappings for in/around next/last for curly brackets. + +Create a "inside next email address" operator-pending mapping so you can say +"change inside next email address". `in@` is a good candidate for the keys to +map. You'll probably want to use `/...some regex...` for this. + +Read `:help omap-info` and see if you can puzzle out what the `` in the +examples is for. diff -r 48c5cc292f99 -r 9a45509b6713 outline.org --- a/outline.org Sat Oct 08 00:51:14 2011 -0400 +++ b/outline.org Sat Oct 08 15:59:50 2011 -0400 @@ -13,15 +13,6 @@ ** autocommands ** buffer-local abbreviations ** augroups -** wrapping up mapping -*** Mapping options -**** -****