Codementor Events

How to Refactor Code with Regex in Vim

Published Feb 28, 2019

Learn how to refactor code with regex in Vim in this article by Ruslan Osipov, a software engineer at Google, an avid traveler, and a part-time blogger. He started publishing personal Vim notes in 2012, and became increasingly interested in the intricacies of the editor and its applications in optimizing development workflows.

Search or replace with regular expressions

Regular expressions (or regexes) are wonderful and you should know how to use them. Vim, as is custom among regex implementations, has its own flavor of regex. However, once you learn one, you're comfortable with all of them. First, let's talk about the regular (that is, the normal) search and replace command. Note that you can find the necessary code files for this article at https://github.com/PacktPublishing/Mastering-Vim/tree/master/Chapter06.

Search and replace

Vim supports search and replace through the :substitute command, most often abbreviated to : s. By default, : s will replace one substring with another in a current line. It has the following format:

:s/<find-this>/<replace-with-this>/<flags>

The flags are optional. To try it, open animal_farm.py, navigate to the line containing cat (for example, with /cat), and execute the following:

:s/cat/dog

This replaces the first occurrence of cat in the current line:

1.png

Now, let's look at the flags you can pass to the substitute command:
• g—global replace: replace every occurrence of the pattern, not just the first one
• c —confirm each substitution: prompt the user before replacing the text
• e —do not show errors if no matches are found
• i —ignore case: make the search case-insensitive
• I —make the search case-sensitive

You can mix and match these (except for i and I) as you see fit. For example, running 😒/cat/dog/gi will turn the string cat.Cat() into dog.dog(). :substitute can be prefixed by a range, which tells it what to operate on. The most common range used with :substitute is %, which makes 😒 operate on the current file. For instance, if we wanted to replace each instance of animal in a file with creature, we would run the following:

:%s/animal/creature/g

If you try it on animal_farm.py, you'll see that every instance of animal was replaced with creature:

2.png

The :substitute command conveniently tells us how many matches were replaced in the status line at the bottom of the screen. It seems as if we just completed a very simple case of refactoring! :substitute supports more ranges. Here are some common ones:
• numbers—a line number
• $—the last line in the file
• %—a whole file (this is one of the most used ones)
• /search-pattern/—lets you find a line to operate on
• ?backwards-search-pattern?—does the same thing as the previous flag, but searches backwards

Furthermore, you can combine the ranges with a ; operator. For example, 20;$ will let you search from line 20 until the end of the file. To demonstrate, the following command will search for and replace every instance of animal with creature from line 12, up to and including the line where it encounters dog:

:12;/dog/s/animal/creature/g

Two instances of animal were replaced on lines 13 and 14 but not on lines 10 or 21:

3.png

You can also select a range in a visual mode and run 😒 without any explicit ranges to operate on a selected text. See :help cmdline-ranges for more information on ranges. Most often, you will find yourself replacing all occurrences in the whole file by running the following:

:%s/find-this/replace-with-this/g

When replacing text, you may want to only search for the whole word. You can use < and > for this purpose. For example, given the following file, we can search for /animal (:set hlsearch is enabled to highlight all results), but we also get results we're not exactly interested in, such as animals:

4.png

However, if we search for /<animal>, we'll be able to match whole words only, without falsely detecting animals, as follows:

5.png

Operations across files using arglist

Arglist allows you to perform the same operation on multiple files without having to have them preloaded in buffers first. Arglist provides a few commands, as follows:
• :arg defines the arglist.
• :argdo allows you to execute a command on all the files in the arglist.
• :args displays the list of files in the arglist.

For example, if we wanted to replace all instances of animal in every Python file (recursively), we would do the following:

:arg **/*.py
:argdo %s/\<animal\>/creature/ge | update

This command work as follows:
• :arg <pattern> adds a set of files matching a pattern to the arglist (each argument in arglist also has a corresponding buffer).
• **/*.py is a wildcard for every .py file, recursively starting with the current directory.
• :argdo executes a command on every item in the argument list.
• %s/<animal>/creature/ge replaces every occurrence of animal with creature, in every file, without raising errors if the matches are not found.

:update is equivalent to :write, but it only saves the file if the buffer has been modified. You need to use :update in arglist commands, because Vim doesn't like when you switch buffers without saving their contents. An alternative would be to use :set hidden to silence these warnings and save all files at the end by running :wa.

Give it a shot and you'll see that every occurrence of a word has been replaced (you can check by running git status or git diff if you have a repository checked into Git). You can also view the contents of the arglist by running the following without any arguments:

:args 

Arglist is actually left over from the Vi days—it was used in a similar way to how we use buffers today. Buffers expand upon an argument list: every arglist entry is in the buffer list, but not every buffer is in the arglist.

Technically, you can also use :bufdo to perform an operation on every open buffer (since arglist entries are reflected in the buffer list). However, I would advise against it, since there is a risk of running a command on buffers you unintentionally had open before populating the argument list.

If you found this article interesting, you can explore Mastering Vim to make Vim (or its fork, Neovim) your first choice when writing applications in Python and other programming languages. Mastering Vim, reviewed by Bram Moolenaar, the creator of Vim, covers usage of Vim and Neovim, showcases relevant plugins, and teaches Vimscript.

Discover and read more posts from PACKT
get started
post commentsBe the first to share your opinion
Show more replies