💗

Vimで複数のモードのマッピングを一括で設定する

2022/01/17に公開

https://twitter.com/KawarimiDoll/status/1482878223000403973

Vimのキーマッピングは、対象のモードにより、nmapxmapなど複数のコマンドが存在します(:h map-commands)。

しかし、「ノーマルモードでもビジュアルモードでも同じマッピングを設定したい」と思うことがあります。
通常は、このためにほぼ同じ(最初の1文字だけ違う)コマンドを何回も使用することになり、冗長な記述になってしまいます。

nnoremap s <Cmd>DoSomething<CR>
xnoremap s <Cmd>DoSomething<CR>

今回は、この複数モードでのマッピングを一括で指定するコマンドを作ったのでご紹介します。

導入

以下のコードを.vimrcinit.vimなどの設定ファイル内に記載してください。

つかいかた

:Keymap {modes} {arguments-of-map-commands}で実行できます。
modesは必須引数です。対象のモードを文字列で渡します。ノーマルモードだけのマッピングならn、ノーマルモードとビジュアルモードならnxとします。
arguments-of-map-commandsは通常のマッピングコマンドに渡す引数であれば何でも受け取れます。

マッピングの定義

基本的には、モード指定とマッピングを渡すだけです。
渡されたモード全てに対して同じマッピングを定義します。

Keymap nx s <Cmd>DoSomething<CR>
" same as ↓
" nnoremap s <Cmd>DoSomething<CR>
" xnoremap s <Cmd>DoSomething<CR>

引数arguments-of-map-commandsはマッピングコマンドに透過的に渡されるので、<expr>などのオプションもそのまま使用できます。

Keymap no <script> <expr> s SomeCondition() ? '<Plug>(do-something)' : 's'
" same as ↓
" nnoremap <script> <expr> s SomeCondition() ? '<Plug>(do-something)' : 's'
" onoremap <script> <expr> s SomeCondition() ? '<Plug>(do-something)' : 's'
旧バージョンの解説

以前この記事で紹介していたものでは、マッピング文字列の中に<Plug>が存在する場合はnoremapではなくmapを使う形にしていました。

Keymap nx s <Plug>(do-something)
" same as ↓
" nmap s <Plug>(do-something)
" xmap s <Plug>(do-something)

また、以下のように「関数を呼び出し、その返り値の中に<Plug>が含まれる」ような場合は正しく判定できず、noremapが使われてしまうため…

function! s:some_function() abort
  return "\<Plug>(do-something)"
endfunction
Keymap n <expr> s <SID>some_function()
" same as ↓
" nnoremap <expr> s <SID>some_function()

Keymap!を使うことで、引数に関わらずmapを呼び出せるようにしていました。

Keymap! n <expr> s <SID>some_function()
" same as ↓
" nmap <expr> s <SID>some_function()

現在は、VimもNeovimも<Plug>noremapすることが可能になっているため、こういった工夫は不要になっています。

Vim: https://github.com/vim/vim/commit/1fc34225acbee5ddca2b9ec3f82b3014d385b7f8
Neovim: https://github.com/neovim/neovim/pull/17591

マッピングの確認

{arguments-of-map-commands}を省略すると、nmapなどを無引数で使った場合のように、定義済みのマッピングを表示します。

Keymap nx
" same as ↓
" nmap
" xmap

引数としてマッピングを表示したいキーを渡すことも可能です。このあたりはマッピングコマンドと同じです。

Keymap nx s
" same as ↓
" nmap s
" xmap s

なぜつくったか

実は、Neovimには既にnvim_set_keymap()およびvim.keymap.set()というAPIが存在します。
しかし、これらの「モードを配列で渡す」「<expr>などのオプションを辞書として渡す」といった点が 個人的な好みに合わなかった 改善できると感じたため、今回のKeymapを自作しました。

Vim scriptで実装しているので、Vimでも同様のコマンドを利用できるという点もポイントです。

おわりに

今回のコマンドを使用し、重複した記述を減らせたことで、設定ファイルの見通しが良くなりました。
よろしければご利用ください。

Discussion