Vimの検索まとめ 1文字・単語・ファイル内・複数ファイル
Vimの検索は/だけではありません。同じ行の中で1文字先へ飛ぶのも、開いているファイル全体を探すのも、プロジェクトの何百ファイルを横断するのも、それぞれ別の道具が用意されています。どれを使うかは「何を探すか」ではなくどこを探すかで決まります。この記事は範囲の狭いほうから順に並べた見取り図で、それぞれの詳しい使い方は担当の記事に渡します。
検証環境:Vim 9.1.1752とnightlyビルドのNVIM v0.13.0-dev-1389+gd3b4f562a6(macOS)。大文字小文字の表は、4行のバッファに対して実際に検索してからsearchcount()が返した件数です。数え間違いを避けるため、目視ではなくこの関数の値をそのまま載せています。
目次(9項目)
| 探す範囲 | 使うもの |
|---|---|
| 同じ行の中の1文字 | f t |
| いま開いているファイル | / ? |
| カーソルの下の単語 | * # |
| 開いている全バッファ | :bufdo + :vimgrep |
| プロジェクトのファイル群 | :vimgrep :grep |
| ファイルの名前そのもの | :find |
探す範囲で道具が変わる
最初に全体を並べます。上から順に範囲が広がっていきます。狭いほうで足りるなら狭いほうが速いので、いま探したいものがどの行にあるかを思い浮かべてから選ぶと迷いません。
| 探す範囲 | 使うもの | 詳しい使い方 |
|---|---|---|
| 同じ行の中の1文字 | f F t T | 1文字検索して移動する |
| いま開いているファイル | / ? n N | 文字列検索の基本 |
| カーソルの下の単語 | * # | カーソル下の単語を検索する |
| 開いている全バッファ | :bufdo + :vimgrep | 全バッファを検索する |
| プロジェクトのファイル群 | :vimgrep :grep | 複数ファイルを横断検索する |
| ファイルの名前そのもの | :find | ファイル名からファイルを探す |
いま開いているファイルの中を探す
いちばん使うのが/です。打ち込んでEnterを押すと、カーソルより下の最初の一致へ飛びます。nで次、Nで前に戻ります。上の方向へ探したいときは?で、このときはnとNの向きも反転します。
/error " 下方向に error を探す
?error " 上方向に探す
探す語に記号が混ざると正規表現として解釈されるので、そこだけ最初につまずきます。文字列検索の基本と正規表現の入り口に、行頭行末の指定やOR検索など最初に覚える形をまとめてあります。手を動かして覚えたい場合は正規表現検索の練習ドリルのほうが早いかもしれません。
検索し終わったあとに色が残って邪魔になることがあります。これは検索ハイライトを消す設定で片付きます。長い変数名を打ち直したくないときは検索コマンドラインでの単語補完が使えます。
カーソルの下の単語をそのまま探す
いま目の前にある変数名がこのファイルに何回出てくるかを知りたい、という場面では打ち込む必要すらありません。単語の上で*を押すと、その単語を検索語にして下方向へ飛びます。#なら上方向です。
このとき検索語には単語の境界(\<と\>)が自動で付きます。errorで探してもerrorCountは引っかからない、ということです。境界を外して部分一致で探したいときはg*を使います。この違いはカーソル下の単語を検索する記事に書いてあります。
複数のファイルを横断して探す
1つのファイルに収まらない範囲は:vimgrepの担当です。結果はquickfixリストに集まり、:cwindowで一覧を開いて上から順に飛べます。
:vimgrep /oldName/gj **/*.js
:cwindow
似た名前の:grepもあり、こちらは外部のgrepコマンドを呼びます。速さと正規表現の書き方が変わるので、使い分けはvimgrepとgrepの違いにまとめました。ファイルではなく開いているバッファだけを対象にする方法もあります。
ファイルの名前で探す
ここまでは中身を探す話でしたが、:findだけは毛色が違います。探すのはファイルの名前で、pathオプションに登録した場所から拾います。プラグインを入れずにファイルを開きたいときの答えがこれです。詳しくはファイル名からファイルを探すにあります。
大文字小文字がどう決まるか
ここがこの記事でいちばん誤解の多いところです。ignorecaseとsmartcaseを組にする設定は定番ですが、この組み合わせは*には効きません。次の4行のバッファに対して、設定を変えながら検索してsearchcount()の件数を記録しました。
error one
Error two
ERROR three
error four
| 設定 | /error | /Error | Errorの上で* |
|---|---|---|---|
既定(noignorecase) | 2件 | 1件 | 1件 |
ignorecase | 4件 | 4件 | 4件 |
ignorecase + smartcase | 4件 | 1件 | 4件 |
最後の行を見てください。同じErrorという語を探しているのに、自分で打った/Errorは1件しか拾わず、単語の上で*を押すと4件すべてを拾います。smartcaseが働くのは「自分で打った検索語」に限られ、*が組み立てた検索語は対象外だからです。Vim 9.1とNeovim 0.13の両方で同じ結果になりました。
設定に関係なくその場で決めたいときは、検索語に\c(区別しない)か\C(区別する)を混ぜます。こちらは設定を上書きし、パターンの先頭でも末尾でも同じように効きます。
| 打ち方 | 既定のとき | smartcaseのとき |
|---|---|---|
/Error\c | 4件 | 4件 |
/error\C | 2件 | 2件 |
普段はignorecaseとsmartcaseを入れておき、*で拾いすぎたと感じたときだけ/で打ち直す、という運用が現実的です。厳密に区別したい検索を頻繁にするなら、\Cを付ける癖をつけるほうが確実です。
探したあとに編集へつなぐ
検索は移動のためだけのものではありません。検索した語をそのまま書き換えるcgnによる変更を使うと、1か所直して.を押すだけで次々に置き換えられます。検索そのものをオペレータの範囲に使うオペレータ+検索モーションなら、行数を数えずに「あの語の手前まで削除」ができます。
一括で置き換えるなら検索ではなく置換の担当で、やり方が4通りあります。置換の4つの方法と使い分けから入って、範囲の広いものは:substituteコマンド活用集に進むと迷いません。飛んだ先で止まる位置を細かく決めたいときは検索オフセット、変数の宣言まで飛びたいときはgd と gDがあります。
この記事を書くまで、smartcaseを入れておけば*も賢くなると思い込んでいました。実際に数えてみて逆だと分かったときは、これまで*で「なんだか多く引っかかるな」と感じていた理由がやっと腑に落ちました。件数を目で数えると必ず間違えるので、searchcount()に出させたのも今回の収穫です。範囲の順に並べ直してみると、自分が普段/と*しか使っておらず、:findをほとんど忘れていたことにも気づきました。
まとめ
Vimの検索は探す範囲で道具が変わります。同じ行の中ならf、いま開いているファイルなら/、カーソルの下の単語なら*、複数ファイルなら:vimgrep、ファイルの名前なら:findです。狭いほうで足りるなら狭いほうが速いので、上から順に当てはめると選び間違えません。大文字小文字はignorecaseとsmartcaseで決まりますが、実測すると*にはsmartcaseが効かず、同じ語を探しているのに件数が変わります。その場で決めたいときは\cと\Cを混ぜれば設定を上書きできます。