August 25, 2015
Automated Text Formatting in Vim
I constantly evaluate my workflow to discover areas of repetitiveness. If I’m continually repeating a particular process, I will work on automating it.
Recently, I grew tired of the manual editing process of WordPress posts. Before each post goes out on the Code School Blog, I have to (generally) perform the following set of actions to format the text:
- Remove wrapping
- Remove WordPress-generated classes in
- Clear the
- Remove double spaces
- Remove double newlines
- Remove any inline styles (e.g.
Because of this tedious, manual process, I spent time working on an automated solution. I tried writing a Ruby script to parse the pasted-in text, run the replacements, and then provide a “diff” of what was changed. However, this proved to be a bigger pain than I expected. I landed on using regular expressions and Vimscript for writing a function that runs a set of substitutions on a file.
Capture groups in Vim search
After some experimentation and Googling, I discovered you can use capture groups in Vim as part of the search string. For example:
This search in Vim will return all
span tags with their enclosing content, with the content in between the
<span></span> set to a capture group. The “gotcha” is, by default, Vim will search for the literal opening
( and closing
) parenthesis, not as a capture group. We have to escape the parenthesis characters with a backslash for Vim to treat the set of parentheses as a capture group.
This is the case with all regular-expression strings. Make sure to escape the characters so Vim knows not to search for the literal character.
Running a substitution
In Vim, we can run a substitution within a file like so:
This will replace the string “hello” with “goodbye.” The trailing
g will replace all instances within the file. There are additional flags, such as:
cto ask for confirmation before each substitution
ifor case insensitive
Ifor case sensitive
This will replace all instances of “hello” (case insensitive), and additionally ask for confirmation before each substitution.
Using a capture group
To use a capture group in the replacement, it looks like this:
\1 is referencing the first capture group in the search string which, in this case, is “hello”. All instances of “hello” will be replaced with “hello world!”.
Running a set of substitutions
This solution worked well for running a single substitution at a time, but we want to run all substitutions on a file with a single command. For that, we need a function.
function! DrewstroyBlogPost() " ... endfunction
Vimscript is a bit odd, but if you’ve written a function in any language, this should look familiar. Within the function, we use
exec to run a command:
Vim functions are capitalized to avoid confusion with the built-in functions.
In our case, we use it to run a set of substitutions:
" Convert <i></i> to <em></em> exec ':%s/<i>\(.*\)<\/i>/<em>\1<\/em>/ge' " Convert <b></b> to <strong></strong> exec ':%s/<b>\(.*\)<\/b>/<strong>\1<\/strong>/ge' " ...
e flag tells Vim that not finding a match is not an error.
Running the function
So now that we have the function in place, how do we call it? While in Vim, type the following:
Once we hit enter, the set of substitutions will run on the file. If we don’t want to run that command every time, we can use a mapping to trigger the function, like so:
nnoremap <leader>db :call DrewstroyBlogPost()<cr>
Now we can hit
<leader> (leader key) and
db to run the function. Simple as that.
Although this drastically improves the process, there is still repetitiveness. We have to:
- Copy the text in the WordPress Admin
- Open up a terminal
- Fire up Vim
- Paste in the file contents
- Manually skim the file to clean up anything that’s missed or incorrect
So how could this be automated? We might add functionality to:
- Run a bash command to set up a new file in a Git repo with the clipboard contents
- Run the function on the file
- Save and close the file
git diffto view what’s changed
This was just the first step, but these continual improvements will further refine the automation.
That’s All, Folks
I’m a big believer in automation. There are always ways to improve efficiencies. I highly recommend keeping a document for anything you repeatedly do. Evaluate the document frequently, and spend time to improve your efficiency. It’s worth it.
If you want to see the full function, here’s a GitHub Gist.