# Vimをtmuxの代わりに使う
# はじめに
ぼくは普段開発する時、必ずtmuxとVimを併用しています。tmuxは本当に便利で、画面分割したり、セッションを繰り替えたりながら作業をするのに必須と言ってよいほどです。
しかしVim使いのぼくはやはりVimだけで生活したいので、tmuxをやめてVimだけでtmuxの機能を一部実現してみました。 意外となんとかなったので、そのやり方を解説していきます。
# 仕組みの概要
tmuxの画面分割してターミナルを開くのは、Vimの画面分割とターミナルを組み合わせて実現できます。
例えば:vert terminal ++close bash
で縦二分割してターミナルを開くことができます。
tmuxのセッションに関しては、Vimのセッション機能を使用します。 セッションについてはこちらの記事 (opens new window)を参照してください。
ただ、Vimではターミナルウィンドウの位置、サイズを復元できますが、状態までは復元できません。 仕様上どうしてもここは完全代用が難しいので、ここは無理せず使い方でカバーすればよいです。
# 画面分割
tmux使用していた時はCTRL-S + \
で縦、CTRL-S + -
で横で画面分割、CTRL-S + c
でタブを開くようにしていました。
VimではOpenTermianl
コマンドを定義して、そのコマンド内でバッファを作成して、バッファでターミナルを開くようにしています。
コマンドをキーマッピングすれば、tmuxと同じことができます。
ソースは次になります。
" ターミナルを開く
" a:1 new or vnew or tabnew(default is new)
" a:2 path (default is current)
" a:3 shell (default is &shell)
function! s:open_terminal(...) abort
let open_type = 'new'
let shell = &shell
let path = getcwd()
if a:0 > 0 && a:0 !=# ''
let open_type = a:1
endif
if a:0 > 1 && a:2 !=# ''
let path = a:2
endif
if a:0 > 2 && a:3 !=# ''
let shell = a:3
endif
if open_type ==# 'new'
let open_type = 'bo ' .. open_type
endif
exe printf('%s | lcd %s', open_type, path)
exe printf('term ++curwin ++close %s', shell)
exe 'call term_setrestore("%", printf("++close bash -c \"cd %s && bash\"", getcwd()))'
endfunction
command! -nargs=* OpenTerminal call s:open_terminal(<f-args>)
" ターミナルを開く
noremap <silent> <C-s>\ :OpenTerminal vnew<CR>
noremap <silent> <C-s>- :OpenTerminal<CR>
noremap <silent> <C-s>^ :OpenTerminal tabnew<CR>
tnoremap <silent> <C-s>\ <C-w>:OpenTerminal vnew<CR>
tnoremap <silent> <C-s>- <C-w>:OpenTerminal<CR>
tnoremap <silent> <C-s>^ <C-w>:OpenTerminal tabnew<CR>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
noremap
はノーマルモード、tnoremap
はターミナルで動作するマッピングです。
ターミナルのオプションですが、++close
はターミナルを終了する時にバッファを閉じる、++curwin
は現在のバッファでターミナルを開くという動きになります。
ターミナルを開いた後に実行しているterm_setrestore
はかなり重要で、これはセッションでターミナルを復元する時に実行するコマンドです。
デフォルト、ターミナルを復元する時はカレントディレクトリになってしまいます。 なので、セッション保存時のディレクトリで復元するように設定する必要があります。
上記のコードをvimrc
に追記すれば、そのまま動くはずです。
# セッション保存/復元
セッション機能を使用することで、画面の状態をそのまま保持できます。 なので、基本的セッションを保存、起動時or起動後にセッションを読み込むことで復元できます。
しかし、セッションを使う上でいくつか注意する必要があります。
なるべくプレーンの状態でセッションを復元する
大体復元するタイミングはVimを再起動した後だと思うので、それほど意識する必要は無いですが、 例えばターミナルを開いた状態でセッションを復元しようとするとエラーが出ます。 なので、基本的に起動後にセッションを読み込むようにします。セッションを保存する時のオプションに
globals
を外す
セッションの保存対象を設定するsessionoptions
というオプションがありますが、 デフォルではフローバル変数を復元します。それだとvimrcの一部の設定などが復元してしまいます。 保存時のvimrcの状態を戻すよりも、最新vimrcを適用したいケースが多いので、ここではオプションから外します。
ちなみに、ぼくの設定は次になっています。
set sessionoptions=blank,buffers,curdir,folds,help,tabpages,winsize,terminal
# タブページの移動
Vimのタブをプロジェクトごとで分けて使うため、タブ移動=プロジェクト移動にしています。
デフォルトgt
とgT
でタブを切り替えられますが、個人的に使いづらいので、次の様にしています。
このキーマップはtmuxの設定と同じにしています。
nnoremap <C-s>n gt
nnoremap <C-s>p gT
tnoremap <C-s>p <C-w>:tabprevious<CR>
tnoremap <C-s>n <C-w>:tabnext<CR>
2
3
4
# ghqとfzf.vimとと組み合わせる
# リポジトリを開く
基本的にVimを開くときは何かしらプロジェクトに移動して作業する時なので、
ghq (opens new window)とfzf.vim (opens new window)を組み合わせてプロジェクトに簡単に移動した後にターミナルを開くRepo
コマンドを用意しました。
function! s:cd_repo(shell, repo) abort
exe 'lcd' trim(system('ghq root')) .. '/' .. a:repo
call s:open_terminal('new', '', a:shell)
exe 'wincmd k'
pwd
endfunction
function! s:repo(multi, cb) abort
if executable('ghq') && exists('*fzf#run()') && executable('fzf')
call fzf#run({
\ 'source': systemlist('ghq list'),
\ 'sink': a:cb,
\ 'options': a:multi,
\ 'down': '40%'},
\ )
else
echo "doesn't installed ghq or fzf.vim(require fzf)"
endif
endfunction
command! Repo call s:repo('+m', function('s:cd_repo', [&shell]))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ghq list
でリポジトリ一覧を取得して、fzf.vim
であいまい検索できるようにしています。
# リポジトリを複数開く
ときに複数のプロジェクトを同時に開きたいことがあります。
何度も:Repo
を実行しても良いですが、面倒です。
なので、NewTab
コマンドというのを用意しました。
function! s:open_tabs(shell, repo) abort
exe printf('tabnew | lcd %s/%s', trim(system('ghq root')), a:repo)
exe printf('bo term ++rows=20 ++close %s', a:shell)
exe 'call term_setrestore("%", printf("++close bash -c \"cd %s && bash\"", getcwd()))'
exe 'wincmd k'
endfunction
command! NewTab call s:repo('-m', function('s:open_tabs', [&shell]))
2
3
4
5
6
7
8
fzf.vim
は-m
オプション使うと複数の項目を選択することができるので、それを使用します。
ここで1点注意ですがfzf.vim
のcallbackでは&shell
がsh
になってしまうので、呼び出し元のshell
を渡す必要があります。
# 残作業
# タブのラベル
タブのラベルを自由に決めれるようにしたいのです、ラベルの仕組みが思ったよりもややこしく途中で諦めました。 ラベルを簡単に変えれれば一番良いですが、なくてもそこまで問題ないです。
# 画面最大化のトグル
tmuxではprefix+z
で画面の最大化のトグルができます。Vimでもそれをやろうと思えば実現はできると思いますが、まだ試せていないです。
ターミナルがある状態で<C-w>o
でターミナルが残るので、その問題が解決できればという感じです。
# まとめ
ターミナルとセッションの機能で、なんとかtmuxっぽいことはできました。 今の所それほど困ってはいないのですが、やはりtmuxはすごいなと改めて思いました。
ただ、Vimだけでもここまでできるこを知れたので大きいですね。 興味ある方はぜひvimrcに追加して試してみてください。