Vim 8 terminal job
Vim 8 からは terminal jobというVimのバッファ上でシェルを操作できる機能があります。
こちらなんですが、実はシェルを操作できるだけじゃなく、sshやNode.jsやClojureのreplなどttyを必要とするアプリケーションを指定するとバッファ上でそのままジョブが動いたりして便利です。
この記事ではこのterminal jobを利用した環境を魔改造カスタマイズしてより便利にしていく方法を紹介します。
この記事はbashを前提としています。
Vim上のbashからVimにコマンドを送れるようにする
Vimのclientserver機能
Vimにはclientserver機能があり、こちらを使うと他プロセスから対応するサーバ名のVimに対してコマンドを送信できます。
こちらを利用するためにはclientserver機能を有効にしたVimが必要です。
まずVim上で以下を実行します。
:call remote_startserver('mainecoon')
そして、ターミナル上(Vim上のものでも、他のものでも構いません)で、以下を実行します。
$ vim --servername mainecoon --remote-send '<C-w>:echo "hoge"<CR>'
すると、Vimには --remote-send
で指定した <C-w>:echo "hoge"<CR>
というキー入力が送信され、これにより :echo "hoge"
が実行されます。
更に、clientserver機能を実行した状態で起動したアプリケーションの環境変数には VIM_SERVERNAME
という値が追加されます。
よって、Vim上のbashでVimにキー入力を送信したい場合bashで以下のようなコマンドを実行すれば良いことがわかります。
$ vim --servername $VIM_SERVERNAME --remote-send '<C-w>:echo "hoge"<CR>'
ここまでのことを踏まえると以下のように .bashrc
に追記することで、簡単なコマンドでVimにキー入力ができることが分かります。
if [ -n $VIM_SERVERNAME ]; then
function v() {
vim --servername $VIM_SERVERNAME --remote-send "<C-W>$*<CR>"
}
fi
追記し、新たにVim上でbashを開くと
$ v :new hoge.md
などとすることでVim上のbashから新たなバッファを開き、そこで指定したファイルの編集を開始できます。
しかし
$ cd ./hoge ; v :new fuga.md
のような相対パスでのファイル指定は上手く実行できません。これはterminal jobのバッファのカレントディレクトリが起動時のカレントディレクトリのままになっているからです。
というわけで、指定コマンドの実行前に :lcd $PWD
してあげることで解決します。
最終的に v
という関数は以下のようになります。
if [ -n $VIM_SERVERNAME ]; then
function v() {
vim --servername $VIM_SERVERNAME --remote-send "<C-W>:lcd ${PWD}<CR>"
vim --servername $VIM_SERVERNAME --remote-send "<C-W>$*<CR>"
}
fi
毎起動時にclientserverを起動する場合は以下を .vimrc
に記述してください(サーバ名は自由にしてください)
if has('clientserver') && len(v:servername) <= 0
let server_list=split(serverlist(), '\n')
let server_count=len(server_list)
if match(server_list, v:servername) <= 0
call remote_startserver('siberian' . server_count)
endif
endif
注意事項
terminal jobが生きている状態では :e hoge.md
はできないので、 :e! hoge.md
しないといけません。我慢してください。
独自のエイリアスを定義し、Vimで対応するコマンドを実行する
人の欲は止まるところを知らず、Vim上で開いたbashから任意のVimコマンドにエイリアスをつけたくなります。
そこで、.bashrcに最高の関数を定義することで、無理矢理これを実現します。
エイリアス設定の形式
今回は
alies commands args...
のような形式でエイリアス設定を記述することにしました。
よって、Vim上のbashでvn
というエイリアスで :new
したい場合は
vn :new
という形になります。今回はこの設定ファイルを ~/.vimfuncrc
に配置することにします(rcではないが書いた当初の気持ちでこのような命名になっています)
エイリアス設定からVimコマンド実行用のbash関数を生成する関数を .bashrc
に書く
解説が大変なので先にスクリプトを貼ります。
if [ -n $VIM_SERVERNAME ]; then
function v() {
vim --servername $VIM_SERVERNAME --remote-send "<C-W>:lcd ${PWD}<CR>"
vim --servername $VIM_SERVERNAME --remote-send "<C-W>$*<CR>"
}
function vFuncDeclare() {
eval "function $1() {
v `echo "$*" | cut -d' ' -f2-` "\$*"
}"
}
VFUNCPROP=`sed -e 's/^/vFuncDeclare /g' ~/.vimfuncrc`
VFUNCCOUNT=`wc -l <<EOF
$VFUNCPROP
EOF`
for i in `seq 1 $VFUNCCOUNT`; do
`head -$i <<EOF | tail -1
$VFUNCPROP
EOF`
done
fi
重要なのは vFuncDeclare
という関数で、これは
funcname :vim-func arg1 arg2 ...
の形式から
function funcname() {
v :vim-func arg1 arg2 ... "$*"
}
という関数を定義する関数です。さらに、ここで定義された関数には追加の引数を与えることができます。
たとえば
vn :new
という定義をこの関数に渡すと
function vn() {
v :new "$*"
}
という関数が定義されるので、Vim内のbashから
$ vn ./filename
とすると新しいバッファで ./filename
を編集できます。
この vFuncDeclare
の後のあれこれは .vimfuncrc
の中身をなんとかして vFuncDeclare
に渡すための記述です。
実際の設定例
vn :new
vt :tabnew
gt :tabnext
gT :tabprevious
wt T'<C-W>':Nop
vterm :term
Vterm :term $* '<CR><C-W>T<C-W>':Nop
vssh :term ssh
Vssh :term ssh $* '<CR><C-W>T<C-W>':Nop
Vemacs :term emacs -nw $* '<CR><C-W>T<C-W>':Nop
vquit :qa!
Vterm
やVssh
などがなにやら激しいことになっています。
Vterm
を例に見るとこれは実際に定義される関数が
function Vterm() {
v :term $* '<CR><C-W>T<C-W>':Nop
}
となり、実際に実行される際はv
関数内で
vim --servername $VIM_SERVERNAME \
--remote-send "<C-W>:term {{Vtermの引数全て}}<CR><C-W>T<CR><C-W>:Nop{{Vtermの引数全て}}<CR>"
のように実行されます。これによって
-
:term {{引数}}
を実行 -
<C-W>T
で開いたバッファを新たなタブへ移動 -
:Nop {{引数}}
を実行
の順で処理を実行させることが可能です。
:Nop
は余計なキー入力を抑制するためのVimコマンドで.vimrcなどで
function! Noperation()
endfunction
command! -nargs=* Nop call Noperation()
を定義しておいてください。
最後に
無理くり改造するのが楽しかっただけで他にもっと良いカスタマイズやプラグインなどがあるかもしれません。
みなさまも是非環境を魔改造し快適なCUIライフを