--- a/chapters/32.markdown Tue Oct 25 20:55:03 2011 -0400
+++ b/chapters/32.markdown Tue Oct 25 23:21:53 2011 -0400
@@ -1,5 +1,5 @@
-Case Study: GrepMotion, Part One
-================================
+Case Study: Grep Operator, Part One
+===================================
In this chapter and the next we're going to walk through creating
a fairly-complicated piece of Vimscript. We'll talk about several things we
@@ -145,15 +145,24 @@
:nnoremap <leader>g :execute "grep! -R " . shellescape("<cWORD>") . " ."<cr>
Try it out again and nothing will seem to happen. Vim has filled the quickfix
-window with the results, but we haven't opened it yet. Let's add that as the
-finishing touch. Run the following command:
+window with the results, but we haven't opened it yet. Run the following
+command:
- :nnoremap <leader>g :execute "grep! -R " . shellescape("<cWORD>") . " ."<cr>:copen<cr>
+ :nnoremap <leader>G :execute "grep! -R " . shellescape("<cWORD>") . " ."<cr>:copen<cr>
Now try the mapping and you'll see that Vim automatically opens the quickfix
window with the search results. All we did was tack `:copen<cr>` onto the end
of our mapping.
+As the finishing touch we'll remove all the grep output Vim displays while
+searching. Run the following command:
+
+ :nnoremap <leader>G :silent execute "grep! -R " . shellescape("<cWORD>") . " ."<cr>:copen<cr>
+
+We're done, so try it out and admire your hard work! The `silent` command just
+runs the command that follows it while hiding any messages it would normally
+display.
+
Exercises
---------
@@ -171,3 +180,5 @@
Add a height to the `:copen` command in the mapping we created to make sure the
quickfix window is opened to whatever height you prefer.
+
+Read `:help silent`.
--- a/chapters/33.markdown Tue Oct 25 20:55:03 2011 -0400
+++ b/chapters/33.markdown Tue Oct 25 23:21:53 2011 -0400
@@ -0,0 +1,255 @@
+Case Study: Grep Operator, Part Two
+===================================
+
+Now that we've got a preliminary sketch of our solution, it's time to flesh it
+out into something powerful.
+
+Remember: our original goal was to create a "grep operator". There are a whole
+bunch of new things we need to cover to do this, but we're going to follow the
+same process we did in the last chapter: start with something simple and
+transform it until it does what you need.
+
+Before we start, comment out the mapping we creating the previous chapter from
+your `~/.vimrc` file -- we're going to use the same keystroke for our new
+operator.
+
+Create a File
+-------------
+
+Creating an operator will take a number of commands and typing those out by
+hand will get tedious very quickly. You could add it to your `~/.vimrc` file,
+but let's create a separate file just for this operator instead. It's meaty
+enough to warrant a file of its own.
+
+First, find your Vim `plugin` directory. On Linux or OS X this will be at
+`~/.vim/plugin`. If you're on Windows it will be at TODO. If this directory
+doesn't exist, create it.
+
+Inside `plugin/` create a file named `grep-operator.vim`. This is where you'll
+place the code for this new operator. When you're editing the file you can run
+`:source %` to reload the code at any time. This file will also be loaded each
+time you open Vim just like `~/.vimrc`.
+
+Remember that you *must* write the file before you source it for the changes to
+be seen!
+
+Skeleton
+--------
+
+To create a new Vim operator you'll start with two components: a function and
+a mapping. Start by adding the following code to `grep-operator.vim`:
+
+ nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
+
+ function! GrepOperator(type)
+ echom "Test"
+ endfunction
+
+Write the file and source it with `:source %`. Try it out by pressing
+`<leader>giw` to say "grep inside word". Vim will echo "Test" *after* accepting
+the `iw` motion, which means we've laid out the skeleton.
+
+The function is simple and nothing we haven't seen before, but that mapping is
+a bit more complicated. First we set the `operatorfunc` option to our function,
+and then we run `g@` which calls this function as an operator. This may seem
+a bit convoluted, but it's how Vim works.
+
+For now it's okay to consider this mapping to be black magic. You can delve
+into the detailed documentation later.
+
+Visual Mode
+-----------
+
+We've added the operator to normal mode, but we'll want to be able to use it
+from visual mode as well. Add another mapping below the first:
+
+ vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
+
+Write and source the file. Now visually select something and press `<leader>g`.
+Nothing happens, but Vim does echo "Test", so our function is getting called.
+
+We've seen the `<c-u>` in this mapping before but never explained what it did.
+Try visually selecting some text and pressing `:`. Vim will open a command line
+as it usually does when `:` is pressed, but it automatically fills in `'<,'>` at
+the beginning of the line!
+
+Vim is trying to be helpful and inserts this text to make the command you're
+about to run function on the visually selected range. In this case, however, we
+don't want the help. We use `<c-u>` to say "delete from the cursor to the
+beginning of the line", removing the text. This leaves us with a bare `:`,
+ready for the `call` command.
+
+The `call GrepOperator()` is simply a function call like we've seen before, but
+the `visualmode()` we're passing as an argument is new. This function is
+a built-in Vim function that returns a one-character string representing the
+last type of visual mode used: `"v"` for characterwise, `"V"` for
+linewise, and a `ctrl-v` character for blockwise.
+
+Motion Types
+------------
+
+The function we defined takes a `type` argument. We know that when we use the
+operator from visual mode it will be the result of `visualmode()`, but what
+about when we run it as an operator from normal mode?
+
+Edit the function body so the file looks like this:
+
+ nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
+ vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
+
+ function! GrepOperator(type)
+ echom a:type
+ endfunction
+
+Source the file, then go ahead and try it out in a variety of ways. Some
+examples of the output you get are:
+
+* Pressing `viw<leader>g` echoes `v` because we were in characterwise visual
+ mode.
+* Pressing `Vjj<leader>g` echoes `V` because we were in linewise visual mode.
+* Pressing `<leader>giw` echoes `char` because we used a characterwise motion
+ with the operator.
+* Pressing `<leader>gG` echoes `line` because we used a linewise motion with the
+ operator.
+
+Now we know how we can tell the difference between motion types, which will be
+important when we select the text to search for.
+
+Copying the Text
+----------------
+
+Our function is going to need to somehow get access to the text the user wants
+to search for, and the easiest way to do that is to simply copy it. Edit the
+function to look like this:
+
+ nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
+ vnoremap <leader>g :<C-U>call GrepOperator(visualmode())<cr>
+
+ function! GrepOperator(type)
+ if a:type ==# 'v'
+ execute "normal! `<v`>y"
+ elseif a:type ==# 'char'
+ execute "normal! `[v`]y"
+ else
+ return
+ endif
+
+ echom @@
+ endfunction
+
+Wow. That's a lot of new stuff. Try it out by pressing things like
+`<leader>giw`, `<leader>g2e` and `vi(<leader>g`. Each time Vim will echo the
+text that the motion covers, so clearly we're making progress!
+
+Let's break this new code down one step at a time. First we have an `if`
+statement that checks the `a:type` argument. If the type is `'v'` it was called
+from characterwise visual mode, so we do something to copy the visually-selected
+text.
+
+Notice that we use the case-sensitive comparison `==#`. If we used plain `==`
+and the user has `ignorecase` set it would match `"V"` as well, which is *not*
+what we want. Code defensively!
+
+The second case of the `if` fires if the operator was called from normal mode
+using a characterwise motion.
+
+The final case simply returns. We explicitly ignore the cases of
+linewise/blockwise visual mode and linewise/blockwise motions. Grep doesn't
+search across lines by default, so having a newline in the search pattern
+doesn't make any sense!
+
+Each of our two `if` cases runs a `normal!` command that does two
+things:
+
+* Visually select the range of text we want by:
+ * Moving to mark at the beginning of the range.
+ * Entering characterwise visual mode.
+ * Moving to the mark at the end of the range.
+* Yanking the visually selected text.
+
+Don't worry about the specific marks for now. You'll learn why they need to be
+different when you complete the exercises at the end of this chapter.
+
+The final line of the function echoes the variable `@@`. Remember that
+variables starting with an `@` are registers. `@@` is the "unnamed" register:
+the one that Vim places text into when you yank or delete without specify
+a particular register.
+
+In a nutshell: we select the text to search for, yank it, then echo the yanked
+text.
+
+Escaping the Search Term
+------------------------
+
+Now that we've got the text we need in a Vim string we can escape it like we did
+in the previous chapter. Modify the `echom` command so it looks like this:
+
+ nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
+ vnoremap <leader>g :<C-U>call GrepOperator(visualmode())<cr>
+
+ function! GrepOperator(type)
+ if a:type ==# 'v'
+ normal! `<v`>y
+ elseif a:type ==# 'char'
+ normal! `[v`]y
+ else
+ return
+ endif
+
+ echom shellescape(@@)
+ endfunction
+
+Write and source the file and try it out by visually selecting some text with
+a special character in it and pressing `<leader>g`. Vim will echo a version of
+the selected text suitable for passing to a shell command.
+
+Running Grep
+------------
+
+We're finally ready to add the `grep!` command that will perform the actual
+search. Replace the `echom` line so the code looks like this:
+
+ nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
+ vnoremap <leader>g :<C-U>call GrepOperator(visualmode())<cr>
+
+ function! GrepOperator(type)
+ if a:type ==# 'v'
+ normal! `<v`>y
+ elseif a:type ==# 'char'
+ normal! `[v`]y
+ else
+ return
+ endif
+
+ silent execute "grep! -R " . shellescape(@@) . " ."
+ copen
+ endfunction
+
+This should look familiar. We simply execute the `silent execute "grep! ..."`
+command we came up with in the last chapter. It's even more readable here
+because we're not trying to stuff the entire thing into a `nnoremap` command!
+
+Write and source the file, then try it out and enjoy the fruits of your labor!
+
+Because we've defined a brand new Vim operator we can use it in a lot of
+different ways, such as:
+
+* `viw<leader>g`: Visually select a word, then grep for it.
+* `<leader>g4w`: Grep for the next four words.
+* `<leader>gt;`: Grep until semicolon.
+* `<leader>gi[`: Grep inside square brackets.
+
+This highlights one of the best things about Vim: its editing commands are like
+a language. When you add a new verb it automatically works with (most of) the
+existing nouns and adjectives.
+
+Exercises
+---------
+
+Read `:help visualmode()`.
+
+Read `:help c_ctrl-u`.
+
+Read `:help operatorfunc`.
+
+Read `:help map-operator`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/chapters/34.markdown Tue Oct 25 23:21:53 2011 -0400
@@ -0,0 +1,97 @@
+Case Study: Grep Operator, Part Three
+=====================================
+
+Our shiny new "grep operator" is working great, but part of writing Vimscript is
+being considerate and making your users' lives easier. We can do two more
+things to make our operator play nicely in the Vim ecosystem.
+
+
+Saving Registers
+----------------
+
+By yanking the text into the unnamed register we destroy anything that was
+previously in there.
+
+This isn't very nice to our users, so let's save the contents of that register
+before we yank and restore it after we've done. Change the code to look like
+this:
+
+ nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
+ vnoremap <leader>g :<C-U>call GrepOperator(visualmode())<cr>
+
+ function! GrepOperator(type)
+ let saved_unnamed_register = @@
+
+ if a:type ==# 'v'
+ normal! `<v`>y
+ elseif a:type ==# 'char'
+ normal! `[v`]y
+ else
+ return
+ endif
+
+ silent execute "grep! -R " . shellescape(@@) . " ."
+ copen
+
+ let @@ = saved_unnamed_register
+ endfunction
+
+We've added two `let` statements at the top and bottom of the function. The
+first saves the contents of `@@` into a variable and the second restores it.
+
+Write and source the file. Make sure it works by yanking some text, then
+pressing `<leader>giw` to run our operator, then pressing `p` to paste the text
+you yanked before.
+
+When writing Vim plugins you should *always* strive to save and restore any
+settings or registers your code modifies so you don't surprise and confuse your
+users.
+
+Namespacing
+-----------
+
+Our script created a function named "GrepOperator" in the global namespace.
+This probably isn't a big deal, but when you're writing Vimscript it's far
+better to be safe than sorry.
+
+We can avoid polluting the global namespace by tweaking a couple of lines in our
+code. Edit the file to look like this:
+
+ nnoremap <leader>g :set operatorfunc=<SID>GrepOperator<cr>g@
+ vnoremap <leader>g :<C-U>call <SID>GrepOperator(visualmode())<cr>
+
+ function! s:GrepOperator(type)
+ let saved_unnamed_register = @@
+
+ if a:type ==# 'v'
+ normal! `<v`>y
+ elseif a:type ==# 'char'
+ normal! `[v`]y
+ else
+ return
+ endif
+
+ silent execute "grep! -R " . shellescape(@@) . " ."
+ copen
+
+ let @@ = saved_unnamed_register
+ endfunction
+
+The first three lines of the script have changed. First, we modified the
+function name to start with `s:` which places it in the current script's
+namespace.
+
+We also modified the mappings and prepended the `GrepOperator` function name
+with `<SID>` so they could find the function. If we hadn't done this they would
+have tried to find the function in the global namespace, which wouldn't have
+worked.
+
+Congratulations, our `grep-operator.vim` script is not only extremely useful,
+but it's also a considerate Vimscript citizen!
+
+Exercises
+---------
+
+Read `:help <SID>`.
+
+Treat yourself to a snack. You deserve it!
--- a/outline.org Tue Oct 25 20:55:03 2011 -0400
+++ b/outline.org Tue Oct 25 23:21:53 2011 -0400
@@ -31,7 +31,7 @@
** DONE execute
** DONE execute normal!
** DONE basic regexes
-** TODO Case Study: GrepMotion
+** DONE Case Study: GrepMotion
** TODO lists
** TODO looping
** TODO dictionaries
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/proofreader-volunteers.txt Tue Oct 25 23:21:53 2011 -0400
@@ -0,0 +1,6 @@
+@rhdoenges
+@elazar
+@chartjes
+@robhudson
+@cemicolon
+@execat