Vimperatorのtabmoveをcount指定で実行するユーザーコマンド


:tabmoveの問題

いつも20個前後のタブを開いていることが多いのですが、2つのページを比べたり、1つのページをちょくちょく見ながら別のページで作業する際など、Vimperatorでタブの位置を移動させるコマンド:tabmove +N|-Nをよく使います。

ただ、このコマンドはカウント指定をサポートしておらず、たくさん移動する時に不便なんです。今までは下記のようにキーマッピングしていて、タブ位置を10個分左に移動させる時は、Cmd+Shift+Hを10回叩いてました。同じキー入力を10回も繰り返すなんて、Vimp力の低さに悲しくなりますよね。

.vimperatorrc
nnoremap <M-S-h> :tabmove -1<CR>
nnoremap <M-S-l> :tabmove +1<CR>

:commandでユーザーコマンドを追加

悲しくなってるだけじゃダメなので、:commandで追加できるユーザーコマンドで、:tabmoveleft:tabmoverightを実装し、カウントを渡せるようにしてやればいいんじゃないかと考え、以下の様なユーザーコマンドを追加してみました。

.vimperatorrc
command -count tabmoveleft :tabmove -<count>
command -count tabmoveright :tabmove +<count>
nnoremap <M-S-h> :tabmoveleft<CR>
nnoremap <M-S-l> :tabmoveright<CR>

これで、タブ位置を10個分左に移動させる時は、10,Cmd+Shift+Hと叩けばよくなりました。いい感じです。

と思ったのもつかの間、これだとカウントを指定しなかった場合<count>に数字が渡らないので、:tabmoveの引数チェックにはじかれTrailing charactersというエラーになってしまうんです。1個分左に移動させるのに1,Cmd+Shift+Hと入力するのは、Vimp力の低い私でもさすがに嫌です。

そこで、引数のところを式展開してくれないかなぁと淡い希望をもって、下のように変更してみたのですが、Trailing charactersとなってダメでした。当たり前ですね。どうにかして式展開する方法があればこの方法で解決できそうですが、残念ながらそんな方法は見つかりませんでした。(-count=1とVimライクに初期値を追加する方法はサポートされていません)

.vimperatorrc
" // these command won't work
command -count tabmoveleft :tabmove -1+1*(<count> || 0)
command -count tabmoveright :tabmove +1+1*(<count> || 0)

JavaScriptでユーザーコマンドを追加

Githubなどに公開されてる.vimperatorrcなどを見て、JavaScriptでユーザーコマンドを追加できるというのは知っていたのですが、ちょっとめんどくさそうなので敬遠していました。今回は他に方法が見つからなかったので、Vimp力を上げるためにもトライしてみました。

コマンド実行時に指定した引数やカウントは、vimperatorがパースしてからユーザー定義の関数に引数として渡されるようになっており、カウントはargs.countで参照可能でした。そのため、カウントを取得して+-記号をくっつけてtabmoveに渡してあげるだけOKでした。結構簡単ですよね?

addUserCommandに渡すパラメータや、ユーザーコマンドから他のVimperatorコマンドを実行する方法は、Vimperatorのソースを見て真似しました。パラメータ値などの確認はWebコンソールを使いました。Vimperator用のJavaScriptコード内からはcontent.window.console.log()でWebコンソールに出力できます。

tabmove.js
// tabmove with [count] support

;(function() {

  liberator.modules.commands.addUserCommand( ["tabmr[ight]"], "[count]Move tab right   (same as :tabmove +N)",
    function(args) {
      let arg = "+" + (args.count || 1);
      commands.get("tabmove").execute(arg, args.bang); 
    },
    { argCount: 0, bang: true, count: true },
    true
  );

  liberator.modules.commands.addUserCommand( ["tabml[eft]"], "[count]Move tab left   (same as :tabmove -N)",
    function(args) {
      let arg = "-" + (args.count || 1);
      commands.get("tabmove").execute(arg, args.bang); 
    },
    { argCount: 0, bang: true, count: true },
    true
  );

})();

.vimperatorrcで上記のJavaScriptをロードして、お好きなキーマッピングを追加すれば完成です。タブ位置を一番最初と一番最後に移動させるコマンドと一緒に使えば便利なんじゃないかと思います。

.vimperatorrc
"tabmove with count
source ~/.vimperator/scripts/tabmove.js
nnoremap <M-S-h> :tabmleft!<CR>
nnoremap <M-S-l> :tabmright!<CR>
nnoremap <M-S-C-h> :tabmove 0<CR>
nnoremap <M-S-C-l> :tabmove<CR>

意外と簡単にJavaScriptでコマンドを拡張できるので、これを期に色々と試してみようかと思います。

参考資料

Vimperatorのソース
addUserCommand_for2.0pre - Vimperator