Command-T Optimized
Command-T is a vim plugin for rapid file navigation using fuzzy path matching. It is one of the few plugins I find truly essential. Although profoundly useful, I find that there are a few customizations that make it unbeatable.
In order to make Command-T a little more friendly, I do the following:
- Make all Command-T actions relative the current git repo root
- Use the
.gitignore
to filter the file list - Setup a collection of maps for starting Command-T in subfolders
Command-T Relative the Git Root
The first thing I do in order to make working with Command-T more friendly is to make it work from the root of the current git repo. Reaching for Commend-T implies that I am in a project of some non trivial size, ie anything with one or more subfolders, then I can pretty safely assume I will have it in a git repo.
The following functions allow me to move the current working directory to the root of the git repo before starting up Command-T.
function! Git_Repo_Cdup() " Get the relative path to repo root
"Ask git for the root of the git repo (as a relative '../../' path)
let git_top = system('git rev-parse --show-cdup')
let git_fail = 'fatal: Not a git repository'
if strpart(git_top, 0, strlen(git_fail)) == git_fail
" Above line says we are not in git repo. Ugly. Better version?
return ''
else
" Return the cdup path to the root. If already in root,
" path will be empty, so add './'
return './' . git_top
endif
endfunction
function! CD_Git_Root()
execute 'cd '.Git_Repo_Cdup()
let curdir = getcwd()
echo 'CWD now set to: '.curdir
endfunction
nnoremap <LEADER>gr :call CD_Git_Root()<cr>
Using the .gitignore
By default, Command-T will use the vim wildignore
setting to filter files
from the Command-T list. This means that there will only be one global filter
used in every vim session to filter things out. This can be an issue when
working with things like an Octopress blog (like this one), a Rails 3.1 app, or
anything else that has build output or other numerous files that will be in the
gitignore.
I was able to track down a vim plugin create by Adam Bellaire that can parse a
gitignore and use it to define the vim wildignore
setting. His original
script can be found
here, but since it only
included a single function I just included that directly in my vimrc. What
follows is my version of Adam’s original script, wrapped up as a vim function:
" Define the wildignore from gitignore. Primarily for CommandT
function! WildignoreFromGitignore()
silent call CD_Git_Root()
let gitignore = '.gitignore'
if filereadable(gitignore)
let igstring = ''
for oline in readfile(gitignore)
let line = substitute(oline, '\s|\n|\r', '', "g")
if line =~ '^#' | con | endif
if line == '' | con | endif
if line =~ '^!' | con | endif
if line =~ '/$' | let igstring .= "," . line . "*" | con | endif
let igstring .= "," . line
endfor
let execstring = "set wildignore=".substitute(igstring,'^,','',"g")
execute execstring
echo 'Wildignore defined from gitignore in: '.getcwd()
else
echo 'Unable to find gitignore'
endif
endfunction
nnoremap <LEADER>cti :call WildignoreFromGitignore()<cr>
nnoremap <LEADER>cwi :set wildignore=''<cr>:echo 'Wildignore cleared'<cr>
Subfolder Focused Command-T Mappings
This last piece was inspired by a Destroy All Software screencast by Gary Bernhardt. I highly recommend anything by Gary Bernhardt.
This configuration involves setting up a series of normal mode mappings in my vimrc to start Command-T in the context of a subfolder. Most of the work I have been doing lately has been on Rails apps with heavy javascript. As a result of the consistent directory structure of Rails, this works great, but YMMV if you tend to work on different types of projects.
nmap <leader>gv :call CD_Git_Root()<cr>\|:CommandT app/views<cr>
nmap <leader>gc :call CD_Git_Root()<cr>\|:CommandT app/controllers<cr>
nmap <leader>gm :call CD_Git_Root()<cr>\|:CommandT app/models<cr>
nmap <leader>gj :call CD_Git_Root()<cr>\|:CommandT app/assets/javascripts<cr>
nmap <leader>gy :call CD_Git_Root()<cr>\|:CommandT app/assets/stylesheets<cr>
nmap <leader>gs :call CD_Git_Root()<cr>\|:CommandT spec<cr>
nmap <leader>gt :call CD_Git_Root()<cr>\|:CommandT app/assets/templates<cr>
nmap <leader>gl :call CD_Git_Root()<cr>\|:CommandT app/lib<cr>
All in all I am very happy with where I am at with file navigation. I barely have to think at this point to get to a file, and that’s the way I like it. You can checkout the full details of this and my other vim configs in my vimrc.
Room for Improvement
The one major issue I have with my current Command-t usage outlined above is
the need to explicitly fire the Command-t from gitignore function. I would like
to set this to fire based on an autocommand or by hooking into vims cd
and
trigger it automatically. I would imagine it would be necessary to cache the
current project in order to avoid doing this all the time. As of now I haven’t
felt a strong enough pull to figure this out, but I expect I will revisit it
someday.