Vim
tmux
tips

Pane (tmux) - Window (vim) の移動をシームレスにする - Ctrl-h を殺さない ver.

4年前に『【Vim × tmux】Pane(tmux) - Window(Vim)の移動をシームレスに - Qiita』という記事を書いた.
当時とモチベーションは同じ.

Vim × tmuxの画面移動

tmuxでpane分割してVimを開いて作業しているとき,Vim内でもウィンドウ分割をすると画面内の移動が大変ややこしいことになる.

vim-tmux.png

面倒なのでVimとかtmuxとか気にせずに 同じキーバインドで 画面の移動をしたい.

Vim Tmux Navigator の問題点

前述の記事では以下のようなキーバインドで pane - window 間を意識せず移動できるような設定を紹介した.

  • C-h: 左に移動
  • C-j: 下に移動
  • C-k: 上に移動
  • C-l: 右に移動

しかし,このキーバインドでは C-h(backspace) や C-l(clear-screen)など,shell 上で日常的に使用するキーを殺すことになる.この点がネックになり,自分は Vim Tmux Navigator を使わなくなっていた

Tmux の key-table

ところで,tmux の bind-keybind)には -T というオプションが存在する.

     bind-key [-nr] [-T key-table] key command [arguments]
                   (alias: bind)
             Bind key key to command.  Keys are bound in a key table.  By default (without -T), the key is bound in the prefix key table.  This table is used for
             keys pressed after the prefix key (for example, by default `c' is bound to new-window in the prefix table, so `C-b c' creates a new window).  The root
             table is used for keys pressed without the prefix key: binding `c' to new-window in the root table (not recommended) means a plain `c' will create a
             new window.  -n is an alias for -T root.  Keys may also be bound in custom key tables and the switch-client -T command used to switch to them from a
             key binding.  The -r flag indicates this key may repeat, see the repeat-time option.

             To view the default bindings and possible commands, see the list-keys command.

key-table には copy-mode, copy-mode-vi, prefix rooot が存在し,何も指定しなければ prefix が利用される.また,-n をつければ root になる.
Vim Tmux Navigator が C-h をハンドルできるのは,bind-key -n をしているから.

これをたとえば C-w を利用する keytable を作って,C-w + h で移動できるように変更できれば,Vim の window 移動のキーバインドで tmux も移動できるようになる.しかも backsapce や clear-screen も殺されずに済むはず.

navigator 用の key-table を作る

こんなかんじ

# `C-s` で NAVIGATOR table に切り替え
bind -n C-w switch-client -T NAVIGATOR

# See: https://github.com/christoomey/vim-tmux-navigator
is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
    | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'"

# `bind-key -n` のかわりに `bind -T NAVIGATOR` にする
# `send-keys h` のかわりに `send-keys C-w h` にする
bind -T NAVIGATOR h if-shell "$is_vim" "send-keys C-w h"  "select-pane -L"
bind -T NAVIGATOR j if-shell "$is_vim" "send-keys C-w j"  "select-pane -D"
bind -T NAVIGATOR k if-shell "$is_vim" "send-keys C-w k"  "select-pane -U"
bind -T NAVIGATOR l if-shell "$is_vim" "send-keys C-w l"  "select-pane -R"
bind -T NAVIGATOR \ if-shell "$is_vim" "send-keys C-w \\" "select-pane -l"

# `C-w` が Tmux に喰われてしまうので,2回打つことで Vim に `C-w` を送れるようにして救う
# 使用頻度の高い Window command がある場合は,明示的に `bind -T NAVIGATOR <key> send-keys C-w <key>` すればいい
bind -T NAVIGATOR C-w send-keys C-w

vim 側は Vim Tmux Navigator の設定をそのまま利用する.

" <C-w> 系を Vim Tmux Navigator に移譲する
nnoremap <silent> <C-w>h :TmuxNavigateLeft<cr>
nnoremap <silent> <C-w>j :TmuxNavigateDown<cr>
nnoremap <silent> <C-w>k :TmuxNavigateUp<cr>
nnoremap <silent> <C-w>l :TmuxNavigateRight<cr>
nnoremap <silent> <C-w>\\ :TmuxNavigatePrevious<cr>

これで,C-w + h j k l で pane / window を気にせず移動ができるようになる.