Vim の :nmap と :nnoremap の違い


どちらもノーマルモードのマッピングを設定するコマンドで, 一見どちらを使っても問題ないように思えますが, :nnoremap の方を使われることを強くお勧めします.

:nmap の危険性

:nmap は再帰的にコマンドを実行し, :nnoremap は再帰的にコマンドを実行しません.

再帰的というのはどういう意味かと言いますと, 次のようなコマンドをみていただければと思います:

:nmap j gj
:nmap gj j

j はカーソルを一行下に下げるコマンドですが, 一行目で gj と設定することにより, ソフトラップされた仮想的な行も一行ずつカーソルを下げられるようにしました.

そして 2 行目で gjj とマップして, j 本来の挙動を設定しました.

つまりは jgj を入れ替えたというつもりです.

でも実際それらの 2 行を設定して jgj をノーマルモードで入力すると次のようなエラーが表示されます:

E223: recursive mapping

recursive mapping とあるように, 再帰的なマッピングのため操作が中断されてしまいました.

どうしてこのようなことになってしまったのかというのは, :namp の設定するコマンドと同名のマッピングが存在している場合, そのマッピングを実行してしまうためです.

namp の再帰的というのは, そのような意味での再帰的ということになります.

つまりは 1 行目の j にマップされている gj というコマンドは, 2 行目の gj にマップされている j が実際のところ呼び出されていたということになります.

そして jgj が呼び出され, gjj が呼び出されてと, このように無限ループに陥ってしまいます.

:nnoremap を使おう

なので, jgj を入れ替えるようにマッピングさせたい場合, 次のように :nnoremap を代わりに使う必要があります:

:nnoremap j gj
:nnoremap gj j

このようにすることによって, 意図した通りの挙動をしてくれるようになります.

:nmap というコマンド, 一見こちらの方が一般的な印象を受け, :nnoremap よりも使いたくなってしまいますが, 実際のところはそのような再帰的にコマンドを実行するという危険性をはらんでいるため, むしろ :nnoremap の方が普段のマッピングに適しています.

なので僕はノーマルモードのマッピングを設定する時は, もっぱら :nnoremap を使っています.

あとちなみになんですが, Vim ってコマンドの省力形が色々あるので, :nmap の場合, :nm[ap] となり, 最小文字数として :nm とすることもできます.

:nnoremap:nn[oremap] となるので, 最小文字数として :nn とすることもできます.

なので他の人の .vimrc (Vim の設定ファイル) を見たときに :nm:nn などのコマンドを見たときは, それぞれ :nmap:nnoremap の省略形を使っているのねと解釈する事ができます.

モード毎のマップコマンド

あと :nmap:nnoremap はノーマルモードでのマッピングを設定するためのコマンドですが, Vim はご存知の通りモードという概念がありますので, モード毎に次のようなマッピングを設定するためのコマンドがあります:

モード 再帰的 非再帰的
ノーマル, ビジュアル, セレクト, 操作待機中 :map :no[remap]
ノーマル :nm[ap] :nn[oremap]
ビジュアル, セレクト :vm[ap] :vn[oremap]
ビジュアル :xm[ap] :xn[oremap]
セレクト :smap :snor[emap]
操作待機中 :om[ap] :ono[remap]
インサート, コマンドライン :map! :no[remap]!
インサート :im[ap] :ino[remap]
インサート, コマンドライン, Lang-Arg :lm[ap] :ln[oremap]
コマンドライン :cm[ap] :cno[remap]
ターミナル :tma[p] :tno[remap]

見ていただくと分かるように, ノーマル, ビジュアル, インサートといった基本的なモードの他にもこれだけ色々と細かくあります.

なので状況に応じて, 設定していきたいですね.

まとめ

:nmap:nnoremap その両者は, ノーマルモードのマッピングを設定するということは同じですが, 再帰的にコマンドを実行するかしないかという決定的な違いがあります.

:nmap でマッピングを設定してしまうと, 自分が設定した他のマッピングや他の人のプラグインによって設定されたマッピングによって, 再帰的にコマンドが実行され意図しない操作が起きないかということを考えなくてはいけなくなります.

設定されているマッピングが自分によるものだけならまだなんとかいけそうですが, 10 や 20 ものプラグインをインストールしていて, 色々なマッピングが設定されているとなると :nmap でマッピングを設定するというのは, ほぼ無理ゲーです.

仮にインストールしているプラグインが設定しているマッピングを全て覚えて, そのマッピングと被らないように :nmap で設定するにしても, 労力に対する効果がほとんど得られません.

というか被らないように設定するとなると, 他のマッピングやプラグインを追加していくに従って, 使えるコマンドも限られていきます. (むしろマゾゲー)

なので自分のマッピングを設定するときは, ノーマルモードだったら :nmap ではなく :nnoremap, ビジュアルモードだったら :vmap ではなく :vnoremap, インサートモードだったら :imap ではなく :inoremap というように, 非再帰的なコマンドの方を使われることを強くお勧めします.

:nmap:nnoremap の違い, ひいては再帰的マップコマンドと非再帰的マップコマンドの違いをそれなりに分かりやすくお伝えする事ができましたら幸いです.

マップコマンドに関する情報は :h mapping で見れます.

どのマップコマンドはどのモードと関係しているのかという情報は :h map-modes で見れます.

再帰的なマッピングに関する情報は :h recursive_mapping で見れます.