Skip to main content
  1. Posts/

Ode to Vimprev

·6 mins
linux dotfiles
Introduction>

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>

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>

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>

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 the VIMENV environmental variable.
  • When vim loads the .vimrc file, the keybindings will change based on the VIMENV 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>

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>

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>

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
}