💗

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

2022/01/17に公開約2,600字

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は通常のマッピングコマンドに渡す引数であれば何でも受け取れます。

マッピングの定義

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

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

マッピング文字列の中に<Plug>が存在する場合はnoremapではなくmapが使われます。

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

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

Keymap no <script> <expr> s SomeCondition() ? '<Plug>(do-something)' : 's'
" same as ↓
" nmap <script> <expr> s SomeCondition() ? '<Plug>(do-something)' : 's'
" omap <script> <expr> s SomeCondition() ? '<Plug>(do-something)' : 's'

Bangの利用

前述の通り、このコマンドは引数内に<Plug>があるかどうかを自動で判別してmapを使います。
しかし、以下のように「関数を呼び出し、その返り値の中に<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()

マッピングの確認

{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を自作しました。
なお、map/noremapを自動で判断する機能はvim.keymap.set()を踏襲しています。
Vim scriptで実装しているので、Vimでも同様のコマンドを利用できるという点もポイントです。

おわりに

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

Discussion

ログインするとコメントできます