rails.vim is a Vim plugin allowing for
templated file creation and quick navigation to areas of a Rails codebase that
are context-based (e.g. navigating to a controller or migration) rather than
filename-based. With tab-completion, one could navigate to e.g.
app/models/person.rb
in Vim by running :Emodel person
(which
tab-completes!)
While there are built-ins for common Rails navigation in rails.vim, I found myself:
- looking for additional Rails navigation
- wishing for a way to navigate similarly in non-Rails applications
projectionist.vim
projectionist.vim is a Vim plugin that brings templates, commands, and file patterns forward in a more abstract way via a configurable JSON format.
Imagine JSON configuration for a more agnostic RSpec project, decoupled from
Rails, that lives in $APP_DIR/.projections.json
:
{
"spec/*_spec.rb": {
"command": "spec",
"template": [
"require \"spec_helper\"",
"",
"RSpec.describe {camelcase|capitalize|colons} do",
"end"
]
}
}
Within vim, then, one could type :Espec thing
and be taken to an existing
spec/thing_spec.rb
. Adding :Espec thing!
would create that file using the
template provided, with appropriate string transformations (so, the third line
of the file would describe Thing
).
Building JSON via Composable Project Types
The problem I face is I work with a few different languages and frameworks. While most of my time is spent in Rails, there are a handful of other types of projects I regularly work in.
I was looking for tooling that would build up these JSON files based on the project type, ideally in a composable way (e.g. Ruby + RSpec + Rails, assuming all project types were applicable), but nothing existed. I set out to build something from scratch, with three areas I'd identified:
- A directory of JSON files - one per project type - that could be combined
- A script to determine, based on a directory structure, what project type(s) were relevant
- A hook into zsh that would run this script and, if a
.projections.json
was generated, save to disk
The Directory of JSON files
I found building the JSON files for projectionist.vim the most straightforward
portion of this exercise. It seems unlikely there would be name conflicts
around e.g. "routes"
as you wouldn't have a Rails and Phoenix app coexist in
the exact same directory.
My list of JSON files can be found in my dotfiles.
Composing JSON Payloads
Determining applicable project types is naive and based on presence of particular files. While rudimentary, it suits my needs.
The mechanisms for this are:
- build an empty list of project types
- look for various different files, and push the corresponding project type onto the list
- map over these project types to the actual JSON files from the projections directory
- use
jq
to reduce the JSON files into a single JSON payload and write toSTDOUT
Roughly speaking, the net result is something like this:
#!/usr/bin/env bash
PROJECTIONS_PATH="$HOME/.projections"
list=()
if [ -f "mix.exs" ]; then list+=('elixir'); fi
__router=(lib/*/router.ex lib/router.ex)
if [ -f "app/Main.hs" ]; then list+=('haskell'); fi
if [ -f "Rakefile" ]; then list+=('rake'); fi
if [ -f "spec/spec_helper.rb" ]; then list+=('rspec'); fi
if [ -f "config/routes.rb" ]; then list+=('rails'); fi
__gemspec=(*.gemspec)
if [ -f "${__gemspec[0]}" ]; then list+=('ruby'); fi
files=''
count=0
while [ "x${list[count]}" != "x" ]; do
files="$files $PROJECTIONS_PATH/${list[count]}.json"
count=$((count + 1))
done
if [ $count -eq 0 ]; then exit 1; fi
jq -s 'reduce .[] as $item ({}; . * $item)' $files
The source of this file can be found in my dotfiles.
Hooking into Zsh to Write JSON on cd
ing into a Directory
The final step in wiring this together is hooking into zsh's navigation to build JSON files automatically.
zsh makes this possible with the
chpwd
function.
The relevant JSON generation machinery for zsh is:
function chpwd {
local v=$(projections)
if [[ $? -eq 0 ]]; then
echo $v > .projections.json
fi
}
The source of this can be found in my dotfiles.
Wrapping Up
Now, every time I cd
into a project, bin/projectionist
gets run, generating
a new .projections.json
file. This ensures the commands made available via
projectionist.vim are always relevant, making working with different types of
projects a breeze!