telescope.nvimでlive_grepした結果をファイル名で絞り込む

by
カテゴリ:

Vim駅伝8/2の記事です。

telescope.nvimはNeovim向けのファジーファインダーと類されるプラグインです。
便利な機能の一つに:Telescope live_grepがあり、プロジェクト内のファイルを正規表現で検索できます。

しかし、検索結果が多いときに、ファイル名で絞り込みたいことがあります。
たとえば、特定のディレクトリだけの結果が必要とか、テスト関係のファイルを除外したいとかいった状況があります。

telescope.nvimには検索結果をクイックフィックスリストに送り込むアクションがあるので、これと:Telescope quickfixを組み合わせると、近しいことが簡単に実現できます。

require("telescope.builtin").live_grep({
  attach_mappings = function(prompt_bufnr, map)
    map("i", "<C-G><C-G>", function()
      require("telescope.actions").send_to_qflist(prompt_bufnr)
      require("telescope.builtin").quickfix()
    end)
    return true
  end,
})

しかし、この方法では検索対象にファイル名だけでなく、grepにマッチした行も含まれてしまいます。
たとえば、以下のようなクイックフィックスリストが作成された場合、ファイル名にqfscopeが含まれる行だけを絞り込みたいとします。
実際には、README.mdの1行目にqfscopeが含まれているため、README.mdもマッチしてしまいます。

README.md|1 col 12| # qfscope.nvim
doc/qfscope.txt|1 col 37| *qfscope.txt*     Refine telescope.nvim search results by using quickfix list
doc/qfscope.txt|2 col 11| *qfscope.nvim*
doc/qfscope.txt|6 col 47| Repository: https://github.com/atusy/qfscope.nvim

これを防ぐには、telescope.nvimsorterを弄る必要があります。
sorterは、プロンプト文字列に対して検索結果のマッチ度(スコア)を計算するscoring_functionというメソッドを持ちます。
このメソッドを良い感じにしてあげれば、ファイル名だけを検索対象にできます。

scoring_functionは通常では、function(self, prompt, line, entry): numberをいったシグネチャを持ます。
このline引数が検索対象に相当します。
先程のクイックフィックスリストの例で言えばREADME.md|1 col 12| # qfscope.nvimですね。
ファイル名を検索対象にするには、lien引数の変わりにentry引数のfilenameフィールドに注目させてあげればOKです。
ということで↓のように上書きする関数を用意しましょう。

local function filename_sorter()
  local sorter = require("telescope.config").values.generic_sorter()
  local score = sorter.scoring_function
  sorter.scoring_function = function(self, prompt, _, entry)
    return score(self, prompt, entry.filename, entry)
  end
  return sorter
end

あとは、:Telescope quickfixを起動する時に、自前のsorterを渡してあげれば、live_grepした結果をファイル名で絞り込めるようになります。
telescope.nvimsetup時に、検索アルゴリズムとしてtelescope-fzf-native.nvimを指定していれば、!_test.goなどとして、テスト関連のファイルを除外するといった使い方もできます。

require("telescope.builtin").live_grep({
  attach_mappings = function(prompt_bufnr, map)
    map("i", "<C-G><C-G>", function()
      actions.send_to_qflist(prompt_bufnr)
      require("telescope.builtin").quickfix({
        sorter = filename_sorter(),
      })
    end)
    return true
  end,
})

ちなみに今日紹介した内容をより一般化したプラグインとして、atusy/qfscope.nvimを作成しました。

次回の記事あたりでデモも含めて紹介できたらなと思います。

ENJOY!!