反省はしても後悔はしない

Vim とか備忘録とか。それと関数型言語勉強中

Vim のコマンドラインモードを使いこなそう

[Vim]Vim のコマンドラインモードを使いこなそう

この記事は Vim Advent Calendar 2012 の 180 日目の記事です。

昨日は @raa0121 さんの Sapporo.vim(仮称)発足(仮) のお知らせでした。

はじめに

この記事は Vim のカーソル移動などの基本操作を覚えて、自分の vimrc に自分用のマッピングも少しずつ設定できるようになってきたくらいの方向けの記事です。

コマンドラインモードとはなんでしょう?
そう! : (コロン) を押した時のあの下に行くモードです。

だれでもまず最初は :q で Vim の終了や :w で保存をしますよね。

この記事ではそんなコマンドラインモードをもっと便利に使うための Tips なんかを紹介します。

コマンドラインモードでも補完をする

コマンドラインモードでもコマンド名やファイル名などの補完ができます。

ある程度文字を入力してから <C-d> を押します。

その後 <C-l> を押すと、共通部分までは自動で入力してくれます。
このようになんの設定もしなくてもこれくらいの補完はできます。

設定をいじってもっとコマンドラインモードの補完を使いやすくする

前述のとおり、コマンドラインモードでも補完はできるのですが、デフォルトのままだとやはり微妙に使いづらいです。そこで設定をいろいろいじってもっと使いやすい補完にしましょう。

以下の設定を vimrc に追加します。

set wildmenu

これで、文字を入力した後に <Tab>*1 を打つだけで良い感じに補完することができます

さらに、さきほどの wildmenu での補完の挙動は wildmode オプションでいろいろ設定を変えることができます。
私は下のように設定しています。

set wildmode=longest:full,full

これで最初の1回目の <Tab> では共通部分までの補完をしつつ補完候補を出し、次回以降はこの補完候補を順に選択するような動作になります。

ほかにどういう機能や設定があるかは :help cmdline-completion してみてください。

コマンドラインモードでのマッピングを設定する

ノーマルモードでのマッピングは nmap、ビジュアルモードでのマッピングは xmap*2 で設定します。

これらと同様、コマンドラインモードでも cmap というコマンドでマッピングを設定することができます。

といっても何を設定すればいいのかわかりませんね。なので、いろいろ設定例を紹介します。


以下はコマンドラインモードで Emacs 風のキー操作を提供するものです。

cnoremap <C-a> <Home>
" 一文字戻る
cnoremap <C-b> <Left>
" カーソルの下の文字を削除
cnoremap <C-d> <Del>
" 行末へ移動
cnoremap <C-e> <End>
" 一文字進む
cnoremap <C-f> <Right>
" コマンドライン履歴を一つ進む
cnoremap <C-n> <Down>
" コマンドライン履歴を一つ戻る
cnoremap <C-p> <Up>
" 前の単語へ移動
cnoremap <M-b> <S-Left>
" 次の単語へ移動
cnoremap <M-f> <S-Right>

コマンドラインモードでも方向キーなどホームポジションから離れてしまうキーをおそうとすると生産性が低下してしまいます。コマンドラインモードでもなるべくホームポジションにとどまれるようにしましょう。*3


以下は検索時の / のエスケープを簡単に入力できるようにするものです。

cnoremap <expr> / (getcmdtype() == '/') '\/' : '/'

hoge/fuga を検索するときには Vim では hoge\/fuga と入力しないといけませんが、それを簡単に入力できるようになります。ただし、パスの区切りを入力するなど検索以外の時にマッピングされると困るので、getcmdtype を使って / による検索のときだけ \/ というマッピングが働くようにしています。
この設定は vimrc 読書会で誰かから教えて頂きました。*4

追記

? の検索の時は / のエスケープはいらないです。逆に ? のエスケープが必要です。

以下の設定は /? で検索時に現在の検索する語に単語境界を付与するものです。

cnoremap <C-o> <C-\>e(getcmdtype() == '/' <Bar><Bar> getcmdtype() == '?') ? '\<' . getcmdline() . '\>' : getcmdline()<CR>

どういうことかというと、たとえば /hoge と入力した後に <C-o> を押すと /\<hoge\> というように変わります。最初の /hoge では ahoge や hogera など単語の一部に hoge が含まれていても検索に引っかかりますが /\<hoge\> だと hoge という単語しか検索できないようにすることができます。

この設定だと一方的に単語境界を設定するだけですが、VimScript でちょっとした関数を作れば単語境界をトグルさせるようなことも可能です。(私はそのように設定しています)

smartinput プラグインを使ってさらに複雑なマッピングを設定する

上級編です。
smartinput という入力補助系プラグインがあります。これはもともと開き括弧を入力したら自動的に閉じ括弧を入力するというようなプラグインですが、非常に柔軟なカスタマイズができるためいろいろなことに使えます。

今回はこれをコマンドラインモードでのマッピングに応用します。

call smartinput#map_to_trigger('c', 'D', 'D', 'D')
call smartinput#map_to_trigger('c', 'I', 'I', 'I')
call smartinput#map_to_trigger('c', 'C', 'C', 'C')
call smartinput#define_rule({'at': 'Unite \%#', 'char': 'D', 'input': '<C-u>UniteWithInputDirectory<Space>', 'mode': ':'})
call smartinput#define_rule({'at': 'Unite \%#', 'char': 'I', 'input': '<C-u>UniteWithInput<Space>', 'mode': ':'})
call smartinput#define_rule({'at': 'Unite \%#', 'char': 'C', 'input': '<C-u>UniteWithCursorWord<Space>', 'mode': ':'})

これは Unite 系のコマンドを簡単に入力するためのものです。:Unite<Space> まで入力した時点で I を押すと、:UniteWithInput<Space> に変えることができます。Unite の source 名はだいたい小文字で始まるため大文字をマッピングしても問題ないことを利用しています。
:Unite<Space> を入力するマッピングを他に用意しておくと大変便利です。

このように smartinput を使うと、コマンドラインモードで文脈に応じた柔軟なマッピングを設定することができます。

まとめ

ノーマルモードだけではなく、コマンドラインモードも使いこなしてこそ真の Vimmer

最後に

vimrc 読書会に参加すると以下略

追記

最初コマンドモードと書いていましたが、コマンドモードは vi におけるノーマルモードのことで、Vim ではコマンドラインモードというそうです。id:thinca さん教えてくださりありがとうございました。

次回は @ujihisa さんです。

*1:このキーは wildchar オプションで変更できます

*2:vmap はセレクトモードでもマッピングしてしまいます。意味がわからなければビジュアルモードのマッピングは xmap と覚えた方が良いです

*3:気づいた人は気づいたかもしれませんが、実はこれは :help emacs-kes からのコピペです

*4:誰だったかは忘れた