Emacs のディレクトリ内検索・置換
背景
- 時代の潮流にのり、少し前から
vscode にhelm から vertico + consult に移行していた -
helm-ag の "Save results in buffer" (検索結果をバッファに出力する) と "Edit search results" (検索結果を 1つのバッファで一括編集する) というアクションがとても便利だったのだが、上記だけの構成では同等の機能がなかった。のでやり方を調べた。
- 検索結果を行って戻ってしたい場合、検索結果をバッファに保持しておいたが手間が少ない
- 検索結果について一個一個確認しながら置換するような場面では、再検索(リフレッシュ)ができると便利
- 確認不要で一括置換できるような場面では、検索結果のバッファ上で一括編集したい
目的
- 関連機能や関連パッケージについて整理する
- 上記の2機能を使えるようにする
検索関連
grep
- emacs のビルトイン機能
-
M-x grep
でgrep
コマンド(細かいパラメータは省略)を組み立て、結果を*grep*
バッファに出力する - grep なので再帰的なディレクトリの検索はできない
grep バッファ
-
*grep*
バッファでは以下の操作が行える-
n
次のマッチ行にすすむ -
p
前のマッチにもどる -
g
再検索(リフレッシュ) -
C-u g
コマンドを編集して再検索
-
grep-find (find-grep)
- emacs のビルトイン機能
-
M-x grep-find
でミニバッファ上でfind . -exec grep
コマンド(細かいパラメータは省略)を組み立て、結果を*grep*
バッファに出力する - 再帰的な検索ができる
rgrep
- recursive grep (?)
- emacs のビルトイン機能
-
M-x rgrep
で対象の単語・拡張子・ディレクトリを対話的に入力する- ( grep-find の人間にやさしい版
lgrep
- limited grep (?)
- emacs のビルトイン機能
-
M-x lgrep
で単語・拡張子・ディレクトリを対話的に入力する- ( grep の人間にやさしい版
ripgrep
-
ripgrep は ag (The Silver Searcher)と同じような高速で便利な検索ツール
- .gitignore / .ignore を参照してファイル・ディレクトリを除外できる
-
ripgrep の emacs バインディングは以下
-
- wgrep (後述) が依存パッケージとして指定されており、
*rg*
バッファ上で e をタイプすると wgrep モードに入る
- wgrep (後述) が依存パッケージとして指定されており、
検索結果の編集・一括置換
occur
- emacs のビルトイン機能
-
M-x occur
カレントバッファに対して入力された単語を検索し、結果を*Occur*
バッファに表示する -
M-x multi-occur
対象のバッファを選択し、空 [RET]
で選択終了。その後はoccur
と同様 -
*Occur*
バッファで e をタイプすると Occur Edit モードになり、一括編集が可能になる。C-c C-c
で編集を終了する。
wgrep
-
*grep*
バッファを ( occur のように ) 編集可能にする -
*grep*
バッファでC-c C-p
をタイプすると wgrep モードに入る-
C-c C-c
orC-x C-s
で変更を適用して wgrep モードを終了 -
C-c C-k
変更を破棄して wgrep モードを終了
-
dired-do-query-replace-regexp
- emacs のビルトイン機能
- Dired モードでは
Q
が dired-do-query-replace-regexp にバインドされている - Dired でマークした(もしくはカーソル上の)ファイルまたはディレクトリに対して再帰的に query-replace-regexp を行う
- 置換実行後はバッファの更新は保存されていないのでそれぞれ保存する (
ibuffer
を使うと編集中のバッファを一括保存できる) - 内部で xref を利用している
補完UI関連
vertico
- vertico.el
- vertico.el - VERTical Interactive COmpletion とあるように、保管候補を縦にインタラクティブに表示するUI
consult
-
Emacs の completing-read (ミニバッファで入力を受け取って補完候補を提供する) を使った便利コマンド集 [1]
-
例えば以下のコマンドがあり、それぞれの補完候補を提供する
-
consult-grep
grep の結果 -
consult-ripgrep
ripgrep の結果 -
consult-buffer
現在開いているバッファの一覧や recentf のファイル -
consult-recentf-file
recentf -
consult-yank-from-killring
killring の履歴
-
-
consult 単体では標準の
*Completion*
バッファを表示に用いる- (この場合、インクリメンタルな補完でなく TAB を押して補完候補を取得する
embark
-
embark-act
: コンテキストに応じたアクション(サブコマンド)を提供する- 右クリックメニューのようなイメージ
-
補完候補を対象としたアクションもある
- なにかしらキーを
embark-act
にバインドし、minibuffer で補完候補の表示中に実行する
- なにかしらキーを
-
embark-select
: (*Embark Actions*
表示中に SPC ) 候補を選択(マーク)して embark-act-all で一括操作を行う -
embark-collect
: (*Embark Actions*
表示中に S ) 補完候補をリストにして*Embark Collect:*
バッファーを生成する。 -
embark-export
: (*Embark Actions*
表示中に E ) 補完候補の種類に応じたメジャーモードバッファを生成する。-
ファイルなら Dired バッファ、バッファーなら iBuffer など
-
embark-consult を使うと以下の exporter が追加される
- consult-line, consult-outline, consult-mark > occur-mode バッファ
- consult-grep, consult-git-grep, consult-ripgrep の検索結果 > grep-mode バッファ
-
なにを使うか
とりあえず基本的には .gitignore で除外しているファイルは検索対象外にするだろうということで ripgrep を使う
- 元々は helm-ag を使っていたので consult-ag も検討したが、consult-ripgrep は consult に元から入っているので
となると選択肢は二つ
- consult-ripgrep した検索結果を embark (consult-embark) で export (+ wgrep)
- consult-ripgrep と rg.el を併用(リストが欲しいときや一括編集したい場合は rg.el + wgrep)
で、両方試してみた結果、1 だと export 結果で g を押したときにリフレッシュでなくリストを閉じて minibuffer にもどる挙動をしていたので、2 でやってみる。 [2]
あとわりと副次的ではあるが、 rg.el で wgrep したあと g でリフレッシュすると編集中のファイルを保存するか聞かれるので保存も楽。
感想
分からんことだらけだったが割と整理できた。(予備知識的なものも書いているので記事としては入ってき辛いかもしれない)
昔のメモを掘り返していたら grep-find を使っていた痕跡があったので、多分 helm-ag を長く使っている間に grep-find の存在を忘れたものと思われる。(ありがとう helm 、ありがとう helm-ag )
参考
- Emacsの次世代ミニバッファ補完UI | 日々、とんは語る。
- 2021年の補完UI事情とEmacs28に標準添付されるfido-vertical-modeについて #Emacs - Qiita
- Emacsで拡張を使わず複数ファイルの複数行に渡る置換を行う #Emacs - Qiita
- Why use Vertico/Consult if i can just use fido-vertical-mode? : r/emacs
- Vimmer から見た Emacs ファジーファインダーの歴史について
Discussion