learnvimscriptthehardway / chapters / 31.markdown

Basic Regular Expressions

Vim is a text editor, which means that a great deal of your Vimscript code will be dedicated to working with text. Vim has powerful support for regular expressions, but as usual there are some quirks.

Type the following text into a buffer:

max = 10

print "Starting"

for i in range(max):
    print "Counter:", i

print "Done"

This is the text we'll use to experiment with Vimscript's regex support. It happens to be Python code, but don't worry if you don't know Python. It's just an example.

I'm going to assume that you know the basics of regular expressions. If you don't you should stop reading this book and start reading Learn Regex the Hard Way by Zed Shaw. Come back when you're done with that.

Highlighting

Before we start we need to turn on search highlighting so we can see what we're doing. Run the following command:

:set hlsearch incsearch

hlsearch tells Vim to highlight all matches in a file when you perform a search, and incsearch tells Vim to highlight the next match while you're still typing out your search pattern.

Searching

Put your cursor at the top of the file and run the following command:

/print

As you type in each letter, Vim will start highlighting them in the first line. When you press return to execute the search all the instances of "print" will be highlighted and your cursor will be moved to the next match.

Now try running the following command:

:execute "normal! gg/print\<cr>"

This will go to the top of the file and perform a search for "print", putting us at the first match. It does this using :execute "normal! ..." which we saw in the previous chapter.

To get to the second match in the file you can just add more commands onto the end of the string. Run this command:

:execute "normal! gg/print\<cr>n"

Vim will put the cursor on the second "print" in the buffer (and all the matches will be highlighted).

Let's try going in the opposite direction. Run this command:

:execute "normal! G?print\<cr>"

This time we move to the bottom of the file with G and use ? to search backward instead of forward.

All of these searching commands should be familiar -- we're mostly going over them to get you used to the :execute "normal! ..." idiom, because it will let you do anything you know how to do in vanilla Vim in your Vimscript code.

Magic

The / and ? commands actually take regular expressions, not just literal characters. Run the following command:

:execute "normal! gg/for .+ in .+:\<cr>"

Vim complains that the pattern is not found! I told you that Vim supports regular expressions in searches, so what's going on? Try the following command:

:execute "normal! gg/for .\\+ in .\\+:\<cr>"

This time Vim highlights the "for" loop as we expected in the first place. Take a minute and try to think about what exactly changed before moving on. Remember that execute takes a String.

The answer is that there are two reasons we needed to write the command like we did:

  • First, execute takes a String, so the double backslashes we used turn into single backslashes by the time they get to normal!.
  • Vim has four different "modes" of parsing regular expressions! The default mode requires a backslash before the + character to make it mean "1 or more of the preceding character" instead of "a literal plus sign".

You can see this a bit easier by just running the search in Vim directly. Type the following command and press return:

/print .\+

You can see the \+ working its magic now. The double backslashes were only used because we were passing the pattern as a String to execute.

Literal Strings

As we mentioned in the chapter on Strings, Vim allows you to use single quotes to define a "literal string" that passes through characters directly. For example, the string 'a\nb' is four characters long.

Can we use literal strings to avoid having to type those double backslashes? Think about this for a minute or two before you move on, because the answer is a bit more complicated that you might think.

Try running the following command (note the single quotes and single backslashes this time):

:execute 'normal! gg/for .\+ in .\+:\<cr>'

Vim moves you to the top of the file but doesn't move you to the first match. Is this what you expected?

The command doesn't work because we need the \<cr> in the pattern to be escaped into a real carriage return character, which tells the search command to actually run. Because we're in a literal string, it's the equivalent of typing /for .\+ in .\+:\<cr> in vanilla Vim, which obviously isn't what we want.

All hope is not lost, though! Remember that Vim allows you to concatenate strings, so for larger commands we can use this to split apart the string into easier to read chunks. Run the following command:

:execute "normal! gg" . '/for .\+ in .\+:' . "\<cr>"

This concatenates the three smaller strings before sending them to execute, and lets us use a literal string for the regex while using normal strings for everything else.

Very Magic

You may be wondering about Vimscript's four different modes of regex parsing and how they're different from the regular expressions you're used to from languages like Python, Perl or Ruby. You can read their documentation if you really want to, but if you want the sane, easy solution just read on.

Run the following command:

:execute "normal! gg" . '/\vfor .+ in .+:' . "\<cr>"

We've split the pattern out from the rest of the command into its own literal string again, and this time we started the pattern with \v. This tells Vim to use its "very magic" regex parsing mode, which is pretty much the same as you're used to in any other programming language.

If you simply start all of your regular expressions with \v you'll never need to worry about Vimscript's three other crazy regex modes.

Exercises

Read :help magic carefully.

Read :help pattern-overview to see the kinds of things Vim regexes support. Stop reading after the character classes.

Read :help match. Try running the :match Error /\v.../ command a few times by hand.

Edit your ~/.vimrc file to add a mapping that will use match to highlight trailing whitespace as an error. A good key to use might be <leader>w.

Add another mapping that will clear the match (perhaps <leader>W).

Add a normal mode mapping that will automatically insert the \v for you whenever you begin a search. If you're stuck remember that Vim's mappings are extremely simple and you just need to tell it which keys to press when you use the mapped key.

Add the hlsearch and incsearch options to your ~/.vimrc file, set however you prefer.

Read :help nohlsearch. Note that this is a command and not the "off mode" setting of hlsearch!

Add a mapping to "stop highlighting items from the last search" to your ~/.vimrc file.

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.