Ode to Vimprev
Table of Contents
Introduction #
Vim is great.
…
Well, for those who didn’t gag, we can continue. Vim
is a powerful editor that supports many features and customizations. Today I want to discuss some custom tooling stuck in my toolbelt for a long time and how it could add value to yours.
My first introduction to vimprev
was a few different ways:
Simply put, vimprev
is a minor code addition to the configuration files of vim
that create “mode-like” configurations for specific use cases. Originally, vimprev
was used in web application reconnaissance to quickly view HTTP requests that were saved to the file system. It added functionality by rebinding common keys to make an intuitive experience for an otherwise multi-step process.
Using a tool called meg
, you could request many paths on many webservers and save the files locally. This way, you could make the requests once but then dig through the results in an interactive way. You can browse files forwards and backward all at once by hitting space
and backspace
.
vimprev $(find . -type f )
Components of Vimprev #
The configuration of vimprev
is relatively simple as it only contains two major parts:
~/.vimrc
" vimprev
if $VIMENV == 'prev'
noremap <Space> :n<CR>
noremap <Backspace> :N<CR>
endif
/usr/bin/vimprev
#!/bin/bash
VIMENV=prev vim $@
Reaching Greater Heights #
One of my favorite parts about vimprev
is that it added functionality to an already friendly tool. Nothing new had to be learned, installed, or maintained. All that needed to happen was an addition to the configuration files I already kept.
The original creator of vimprev
,
@tomnomnom, also had multiple configurations for other use cases. This inspired me to revisit vimprev
and see how it could add more value.
One of the first things I wanted to be able to do was pass text streams to vimprev
and have it open valid file paths. This way, the tool fits into processes better and aligns with UNIX’s design philosophy.
ls | vimprev
find . -type f -name "*.config" | vimprev
Typically vimprev
was stored as a bash script in /usr/bin
, but in my
Dotfiles article, I introduced using an alias file so we can just add it there as another function and have it be included in my .zshrc
and .bashrc
files.
In ~/.sh_aliases
:
# vimprev functions
vimprev(){
if [[ $# -ge 1 ]]; then
VIMENV=prev vim $@
else
VIMENV=prev vim $(cat -)
tput reset
fi
}
This small change allows input to be read from stdin
when no arguments are provided and otherwise use standard functionality. We also got to shave off that extra file in /usr/bin/
.
Next, we must reflect on commonly repeated tasks and see where we could add value. One of my favorite things about vim
is that it is potent, but I tend to forget keybinds and other things as I switch between tasks, and over time knowledge is forgotten. The best tools should be intuitive, and our new functionality should reflect that.
Vimspell #
One thing I always return to is spelling and grammar. I prefer to write out everything entirely and then review for errors. Typically this involves transferring text to other applications, but vim
has its built-in spell-check. So let’s design a mode that makes reviewing documents for spelling errors painless.
The process will be as follows:
- Pass an additional argument to
vimprev
, which will set theVIMENV
environmental variable. - When
vim
loads the.vimrc
file, the keybindings will change based on theVIMENV
value and trigger different “modes”.
This way, we can use the same keybindings we know from vimprev
but extend the functionality to new things like spell checking.
In .sh_aliases
:
vimprev(){
if [[ $# -ge 1 ]]; then
if [[ $1 == "sp" || $1 == "spell" ]]; then
shift
VIMENV=spell vim $@
else
VIMENV=prev vim $@
fi
else
VIMENV=prev vim $(cat -)
tput reset
fi
}
In .vimrc
:
" vimspell
if $VIMENV == 'spell'
set spell
set spell spelllang=en_us
noremap <Space> ]s
noremap <Backspace> [s
noremap <Enter> z=
endif
Then we can hit the space
key to go to the next misspelled word and backspace
to return to the previous. Then when we hit the enter
key, a prompt will appear with all the new word suggestions. Better yet, if you press 1 <ENTER>
or 2 <ENTER>
, it automatically autocorrects to the first or second suggestion.
vimprev sp spelling.txt
Vimtoken #
Another task I constantly do is parsing and tokening data into smaller chunks. This could be hashes, URLs, or just random data needing to be transformed. Typically I use awk
, cut
, and sed
to chain together commands until I get the token I need. This would be another excellent use case for a new vimprev
mode.
In .sh_aliases
:
vimprev(){
if [[ $# -ge 1 ]]; then
if [[ $1 == "tk" || $1 == "token" ]]; then
shift
VIMENV=token vim $@
else
VIMENV=prev vim $@
fi
else
VIMENV=prev vim $(cat -)
tput reset
fi
}
In .vimrc
:
" vimtoken
if $VIMENV == 'token'
noremap <Space> :%s//\r/g <cr>
endif
Now to parse tokens I can do it visually by using the built-in search feature to highlight the deliminator then simply press space
to replace the deliminator with a new line. The best part is this also supports regex so I can get visual confirmation that I am not making any errors as I work through parsing.
Once I have my token selected I can press space
to split once per line or keep mashing space
until everything has been tokenized. From there using :sort u
within vim
will automatically sort and remove duplicate entries.
$ cat urls.txt
website.com/home
website.com/home/page/1
website.com/about
website.com/services
$ vimprev tk urls.txt
$ cat urls.txt
1
about
home
page
services
website.com
Application #
This tool has been in my toolkit for a few years now because of how flexible and truly simple it is. Without needing to install or maintain new code, you can extend a familiar tool in brand-new ways without needing to learn or memorize anything new. Hopefully, this has inspired you to revisit your workflows, consider brushing the dust off your favorite tools, and look at them in a new way.
Final Configuration #
.vimrc
" vimprev
if $VIMENV == 'prev'
noremap <Space> :n<CR>
noremap <Backspace> :N<CR>
set noswapfile
endif
" vimspell
if $VIMENV == 'spell'
set spell
set spell spelllang=en_us
noremap <Space> ]s
noremap <Backspace> [s
noremap <Enter> z=
endif
" vimtoken
if $VIMENV == 'token'
noremap <Space> :%s//\r/g <cr>
endif
.sh_aliases
# vimprev functions
vimprev(){
if [[ $# -ge 1 ]]; then
if [[ $1 == "sp" || $1 == "spell" ]]; then
shift
VIMENV=spell vim $@
elif [[ $1 == "tk" || $1 == "token" ]]; then
shift
VIMENV=token vim $@
else
VIMENV=prev vim $@
fi
else
VIMENV=prev vim $(cat -)
tput reset
fi
}