Want to see the full-length video right now for free?
Vim Tmux Navigator is a combination Vim plugin and tmux configuration that
allows for seamless navigation using C-h
, C-j
, C-k
, and C-l
. It was
originally presented as a gist by Mislav Marohnić and is summarized well
in a post on Giant robots.
I will defer to the official installation instructions, but once installed it enables intuitive navigation within and across the boundary between Vim and tmux. Far beyond being a "neat trick", I feel that the functionality Vim Tmux Navigator provides does a wonderful job of unifying tmux and Vim and making them feel like one cohesive environment.
Both tmux and Vim have a way to split their respective windows into multiple parts; "panes" in tmux's case and "splits" in Vim's case. As your comfort with these elements increases, you may find it difficult to make use of the available space.
The first configuration will instruct Vim to automatically re-balance the
visible splits as tmux panes are created, destroyed, or resized. Add this to
your ~/.vimrc
to make it active:
" automatically rebalance windows on vim resize
autocmd VimResized * :wincmd =
Just as with tmux pane zooming, it can be useful to focus on a single tmux
split briefly. Adding the following to your ~/.vimrc
will provide mappings
to "zoom" the current split and to re-balance them:
" zoom a vim pane, <C-w>= to re-balance
nnoremap <leader>- :wincmd _<cr>:wincmd \|<cr>
nnoremap <leader>= :wincmd =<cr>
[Vim Tmux Runner][] is a Vim plugin that allows Vim to "attach" to an adjacent tmux pane and interact with it, sending commands and even lines from Vim across to the tmux pane for evaluation.
Vim Tmux Runner has many commands and potential modes of interaction, and I recommend investigating it fully, but the following list highlights the commands used in this step of the tmux course:
:VtrSendLinesToRunner
- Send across the current line or visually selected
range of lines to the tmux pane for evaluation.:VtrAttachToPane
- Prompt for the pane number to which Vim Tmux Runner
should attach and send future commands.:VtrSendCommand
- Send a specific command, e.g. bundle exec rake
.:VtrSendFile
- Evaluate the current file with its default executable.:VtrFocusRunner
- Make the tmux runner pane active and zoom it. Useful
for review test failure output.:VtrOpenRunner [{optional-config}]
- Opens a tmux pane and attaches the
plugin to it. You can optionally provide a configuration specifying the
orientation, size, and initial command for the pane.As an example, specific variants of :VtrOpenRunner
can be mapped in Vim.
The following Vim key-binding for <leader>irb
will open a tmux pane on the
right, occupying 50% of the screen, and start ruby's irb
REPL.
nnoremap <leader>irb :VtrOpenRunner {'orientation': 'h', 'percentage': 50, 'cmd': 'irb'}<cr>
Vim Tmux Runner has been incredibly valuable to me over the years as a
mechanism for rapidly running tests (see the next section), experimenting with
new APIs, and generally keeping me in Vim while still taking advantage of
interactive execution contexts like the shell, irb
or pry
, and even ssh.
[Vim Tmux Runner]: https://github.com/christoomey/vim-tmux-runner
Now that we've seen Vim Tmux Runner and its ability to send commands from Vim to tmux, we can see how to leverage this to run tests.
The specific configuration I use relies on [Vim Rspec][], a Vim plugin for
determining the proper command to run the current spec or spec file, and then
hands that off to Vim Tmux Runner for execution. The configuration needed for
this is to add the following to your ~/.vimrc
after installing both Vim
Rspec and Vim Tmux Runner:
let g:rspec_command = "call VtrSendCommand('rspec {spec}')"
With that configuration in place, you can configure your preferred mappings to trigger spec runs via Vim Tmux Runner.
map <Leader>t :call RunCurrentSpecFile()<CR>
map <Leader>s :call RunNearestSpec()<CR>
map <Leader>l :call RunLastSpec()<CR>
map <Leader>a :call RunAllSpecs()<CR>
The workflow shown in the video with the spec error uses the following steps:
<leader>t
<leader>fr
in Vim (mapped to Vim Tmux Runner's :VtrFocusRunner
command). This makes the runner pane active and zoomedC-\
binding for navigate to previous[Vim Rspec]: https://github.com/thoughtbot/vim-rspec
The .tmux.conf
as of the end of step 4:
unbind C-b
set -g prefix C-s
bind-key -r C-s send-prefix
bind-key r source-file ~/.tmux.conf \; display-message "~/.tmux.conf reloaded"
is_vim='echo "#{pane_current_command}" | grep -iqE "(^|\/)g?(view|n?vim?)(diff)?$"'
bind -n C-h if-shell "$is_vim" "send-keys C-h" "select-pane -L"
bind -n C-j if-shell "$is_vim" "send-keys C-j" "select-pane -D"
bind -n C-k if-shell "$is_vim" "send-keys C-k" "select-pane -U"
bind -n C-l if-shell "$is_vim" "send-keys C-l" "select-pane -R"
bind -n C-\ if-shell "$is_vim" "send-keys C-\\" "select-pane -l"
set-option -g default-terminal "screen-256color"
set-option -g status-keys "emacs"
set-option -g status-left-length 50
set-option -g status-right ""
bind-key - split-window -v -c '#{pane_current_path}'
bind-key \ split-window -h -c '#{pane_current_path}'
bind -n S-Left resize-pane -L 2
bind -n S-Right resize-pane -R 2
bind -n S-Down resize-pane -D 1
bind -n S-Up resize-pane -U 1
bind -n C-Left resize-pane -L 10
bind -n C-Right resize-pane -R 10
bind -n C-Down resize-pane -D 5
bind -n C-Up resize-pane -U 5
bind c new-window -c '#{pane_current_path}'
set-option -g base-index 1
set-option -g renumber-windows on
bind-key b break-pane -d
bind-key C-j choose-tree
# Use vim keybindings in copy mode
setw -g mode-keys vi
# Setup 'v' to begin selection as in Vim
bind-key -t vi-copy v begin-selection
bind-key -t vi-copy y copy-pipe "reattach-to-user-namespace pbcopy"
# Update default binding of `Enter` to also use copy-pipe
unbind -t vi-copy Enter
bind-key -t vi-copy Enter copy-pipe "reattach-to-user-namespace pbcopy"