9a45509b6713

Tons more.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Sat, 08 Oct 2011 15:59:50 -0400
parents 48c5cc292f99
children 484c72cf83cd
branches/tags (none)
files chapters/09.markdown chapters/14.markdown chapters/15.markdown outline.org

Changes

--- 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 <leader>" viw<esc>a"<esc>hviwo<esc>i"<esc>lel
+    :nnoremap <leader>" viw<esc>a"<esc>hbi"<esc>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 `<leader>"`.  Vim
@@ -41,7 +41,7 @@
 How does this work?  Let's split it apart into pieces and think of what each one
 does:
 
-    viw<esc>a"<esc>hviwo<esc>i"<esc>lel
+    viw<esc>a"<esc>hbi"<esc>lel
 
 * `viw`: visually select the current word
 * `<esc>`: 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
 * `<esc>`: 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
-* `<esc>`: 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
 * `<esc>`: return to normal mode
--- /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 <buffer> <localleader>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`.
--- /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<cr>
+
+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( :<c-u>normal! f(vi(<cr>
+
+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( :<c-u>normal! F)vi(<cr>
+
+Try it out on some text of your own to make sure it works.
+
+So how do these mappings work?  First, the `<c-u>` 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(<cr>
+
+`: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 `<cr>` 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...<cr>` for this.
+
+Read `:help omap-info` and see if you can puzzle out what the `<c-u>` in the
+examples is for.
--- 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
-**** <silent>
-**** <script>
-**** <unique>
-**** <expr>
-*** checking mappings
-**** map
-**** verbose map
 ** operator-pending maps
 ** status lines?
 ** a word on shortened command names