June 14, 2017

A few Vim & tmux mappings

UPDATED SEPTEMBER 2021

Power Vim usage involves mappings, usually many of them. This post will list some of my most used mappings. Note, this post is not intended to be about convincing anyone to change their mappings, rather the intention is to simply provide yet another resource of possibilities.

Both Vim and tmux mappings will somewhat be intermingled in this post since I often use both as a unified whole.

I will let others explain the benefits of tmux and Vim together: vim + tmux: A Perfect Match, and Benefits of using tmux

The mappings discussed in this post are backed into my vim and tmux configurations.

tmux prefix

tmux operations are usually invoked with a <prefix> plus <command> key combination.

The default tmux prefix is Ctrl-b. That is an awkward combination to type with one hand, hence many folk change it to Ctrl-a. In my case I find Ctrl-w even nicer to type.

In ~/.tmux.conf:

unbind-key C-b
set -g prefix C-w

Vim Leader

The Vim leader is a user definable key that is used as a prefix for custom mappings. Think of it as being similar to Alt or Ctrl, but in addition to those modifiers.

The default Vim leader key is backslash which, much like the tmux default prefix, I find to be an awkward key. The two most common replacements are Space and ,.

I like comma as my leader key, in ~/.vimrc:

let mapleader = ","

Use vi bindings in tmux

In ~/.tmux.conf:

setw -g mode-keys vi
unbind-key [
bind-key Escape copy-mode
unbind-key p
bind-key p paste-buffer
bind-key -Tcopy-mode-vi v send -X begin-selection
if-shell 'case "`uname`" in *Linux*) true;; *) false;; esac' \
    'bind-key -Tcopy-mode-vi Enter send -X copy-pipe-and-cancel "xclip -selection primary -i -f | xclip -selection clipboard -i"' \
    'bind-key -Tcopy-mode-vi Enter send -X copy-pipe-and-cancel  "reattach-to-user-namespace pbcopy"'
if-shell 'case "`uname`" in *Linux*) true;; *) false;; esac' \
    'bind-key -Tcopy-mode-vi y send -X copy-pipe-and-cancel "xclip -selection primary -i -f | xclip -selection clipboard -i"' \
    'bind-key -Tcopy-mode-vi y send -X copy-pipe-and-cancel  "reattach-to-user-namespace pbcopy"'

The above configuration is compatible with tmux version 2.4 and above.

When using Vim with tmux, it makes sense to configure vi style bindings in tmux. The above configuration allows for Vim style visual selection and yanking when in tmux copy mode.

tmux pane splitting

In ~/.tmux.conf:

unbind-key %
bind-key | split-window -h
unbind-key '"'
bind-key - split-window -v

Nice pane creation in tmux, use <prefix>-| to create a vertical pane and <prefix>-- to create a horizontal pane. The direction of the line indicates the type of pane that will be created.

Vim splitting

In ~/.vimrc:

nnoremap <silent> <Leader>s :split<CR>
nnoremap <silent> <Leader>v :vsplit<CR>
nnoremap <silent> <Leader>q :close<CR>

Simple split windows in Vim: use <Leader>s to create a horizontal split, <Leader>v to create a vertical split and <Leader>q to close a split.

Seamlessly navigate (Neo)vim splits and tmux panes

In ~/.vimrc:

nnoremap <A-h> <C-w>h
nnoremap <A-j> <C-w>j
nnoremap <A-k> <C-w>k
nnoremap <A-l> <C-w>l

Plug 'christoomey/vim-tmux-navigator'
if &term == 'screen-256color'
    let g:tmux_navigator_no_mappings = 1
    nnoremap <silent> <A-h> :TmuxNavigateLeft<cr>
    nnoremap <silent> <A-j> :TmuxNavigateDown<cr>
    nnoremap <silent> <A-k> :TmuxNavigateUp<cr>
    nnoremap <silent> <A-l> :TmuxNavigateRight<cr>
endif

In ~/.tmux.conf:

is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
    | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'"
bind-key -n M-h if-shell "$is_vim" "send-keys C-h"  "select-pane -L"
bind-key -n M-j if-shell "$is_vim" "send-keys C-j"  "select-pane -D"
bind-key -n M-k if-shell "$is_vim" "send-keys C-k"  "select-pane -U"
bind-key -n M-l if-shell "$is_vim" "send-keys C-l"  "select-pane -R"

(Neo)vim splits divide up a workspace, similarly tmux panes divide up a terminal window. From a usability perspective we want to use the same mappings to navigate between splits and panes.

The above configuration provides Alt-h (left), Alt-j (down), Alt-k (up), and Alt-l (right) navigation between (Neo)vim splits and tmux panes.

Note, Alt based mapping may not work with Vim (last time I checked), but they do work out-of-the-box with Neovim.

The vim-tmux-navigator plugin is a key requirement for these mappings to function. Please install with your preferred Vim plugin manager.

Create a tmux window

In ~/.tmux.conf:

bind-key -n M-w new-window

Alt-w creates a new tmux window (aka a new tab).

Create a Vim tab page

In ~/.vimrc:

nnoremap <silent> <Leader>t :$tabnew<CR>

<Leader>t creates a new Vim tab page.

In ~/.tmux.conf:

bind-key -n M-1 select-window -t 1
bind-key -n M-2 select-window -t 2
bind-key -n M-3 select-window -t 3
bind-key -n M-4 select-window -t 4
bind-key -n M-5 select-window -t 5
bind-key -n M-6 select-window -t 6
bind-key -n M-7 select-window -t 7
bind-key -n M-8 select-window -t 8
bind-key -n M-9 select-window -t 9

tmux windows are numbered tabs which are usually navigated to by <prefix>-<number>. However, I find using a <prefix> based mapping a bit cumbersome if I want to quickly flick between windows.

Instead, I prefer to use Alt-<number> to quickly get to the window I want.

In ~/.vimrc:

Plug 'gcmt/taboo.vim'
let g:taboo_tab_format = " tab:%N%m "

nnoremap <Leader>1 1gt
nnoremap <Leader>2 2gt
nnoremap <Leader>3 3gt
nnoremap <Leader>4 4gt
nnoremap <Leader>5 5gt
nnoremap <Leader>6 6gt
nnoremap <Leader>7 7gt
nnoremap <Leader>8 8gt
nnoremap <Leader>9 9gt

These mapping provide <Leader>1/<Leader>2/... navigation to switch to a specific numbered tab. I use the vim-taboo plugin to display numbered tabs rather than use Vim’s default tab naming convention.

Fold code in Vim

set foldmethod=indent
nnoremap <Leader>z za

There are multiple choices available when choosing a Vim fold method. I like to use indent since it is simple and performant. <Leader>z toggles a fold based on the indent level of the current cursor line. Think of z as being an accordion that grows and shrinks.

Equalize Vim splits

nnoremap <Leader>= <C-w>=

The above mapping equalizes the splits of the current Vim workspace.

Maximimse a Vim split

nnoremap <silent> <Leader>m :tab split<CR>

This handy mapping maximizes a split into its own full tab page. This is useful when the current workspace has been divided multiple times making edits to any single split difficult due to a lack of space. In that case simply maximize the split.

Yank and paste helpers

" Paste from the yank register
noremap <Leader>p "0p
noremap <Leader>P "0P

The default, unnamed, register is prone to accidentally being overwrittern by subsequent operations, so when it comes time to do a paste after a yank you may sometimes find you are not pasting what you want.

However, the "0 yank register will remain intact (until the next yank operation).

<Leader>p and <Leader>P provide quick access to the yank register. If p does not paste what you want, <Leader>p and <Leader>P are available if you want to paste from the last yank.

Insert mode completion mappings

inoremap <C-]>     <C-x><C-]>
inoremap <C-Space> <C-x><C-o>
inoremap <C-b>     <C-x><C-p>
inoremap <C-d>     <C-x><C-k>
inoremap <C-f>     <C-x><C-f>
inoremap <C-l>     <C-x><C-l>

When in insert mode, Vim provides a suite of useful context-aware completions via the <Control-x> prefix. However, I find it difficult and annoying to enter these completions, the Control Control cadence feels too much like Emacs.

Instead, I prefer to use these simpler mappings:

Note, these mappings also allow easy switching from one completion kind to another even when the completion popup is visible.

Readline-style mappings for insert and command modes

nnoremap <C-a> 0
nnoremap <C-e> $
inoremap <C-a>  <C-o>0
inoremap <C-e>  <C-o>$
inoremap <A-b>  <C-Left>
inoremap <A-f>  <C-Right>
inoremap <A-BS> <C-w>
inoremap <A-d>  <C-o>dw
cnoremap <C-a>  <Home>
cnoremap <C-e>  <End>
cnoremap <A-b>  <C-Left>
cnoremap <A-f>  <C-Right>
cnoremap <A-BS> <C-w>
cnoremap <A-d>  <C-Right><C-w>

The GNU Readline library is used by the Bash shell, and certain other utilities, for line-editing and history management. If one uses the default Readline key bindings, for example with Bash, then it makes sense to use those same bindings when in certain Vim modes.

Note, the above Alt-mappings function correctly with Neovim and GUI-based versions of Vim (such gVim), but may not work with terminal Vim.