Video

Want to see the full-length video right now for free?

Notes

Vim Integration

Seamless Navigation

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.

Layout Balancing

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

[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

Running Tests

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:

  1. Run the spec with <leader>t
  2. Observe that the spec failed but that the error message is not visible with the current pane size / orientation
  3. Run <leader>fr in Vim (mapped to Vim Tmux Runner's :VtrFocusRunner command). This makes the runner pane active and zoomed
  4. Read the now visible failure message
  5. Jump back into Vim, simultaneously un-zooming the runner pane, using Vim Tmux Navigator's C-\ binding for navigate to previous

[Vim Rspec]: https://github.com/thoughtbot/vim-rspec

Final Configuration

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"