Visual Studio 全文検索拡張

はじめに

数週間でとりあえず検索できるだけのVisual Studio用の全文検索拡張ができました. Entrian Source SearchFastFindといった既存の拡張があるのは知っています. 早期リリースを目標に不具合検証やユーザビリティは後回しです.

  • 高速な全文検索をVisual Studio上で行いたい
  • ソリューションの情報からインデックスを作るファイルを決定してほしい
    • 上に挙げた拡張を使用したことがありませんが, インデックスをつけるディレクトリを指定するのでしょうか

Visual Studio Extensibility

昔と比べればずいぶんとドキュメントが充実しました, Visual Studio Extensibility. ただそれでもドキュメント化されていない仕様に悩まされます, とくにソリューションのアイテムについては扱いが全くわかりません.

たとえばProjectItemクラスのタイプについては, EnvDTE.Constantsで種類を判別できますが, vsProjectItemKindSolutionItemsが実際はどれに対応してどういう動作をするのかわかりません. ProjectItemクラスはなんでもクラスなので, サポートしていない機能にアクセスすると例外が送出されるかnullが返ります. わたしはCollectionメンバは子オブジェクトを所有していると勘違いしましたが, これはProjectItem自身も含みます.

Projectについても情報がないので, 有志の情報をもとにProjectTypes.csを作りました.

Lucene.Net

研究段階ではgroongaも試してみたのですが, ドキュメントが皆無に等しいのとVisual Studio拡張にC/C++のDLLを組み込むのは少し手間なので断念しました.

サブDB選定

ファイルの更新時刻とインデックスの更新時刻の比較に当初Luceneを利用していましたが, あまりにも遅いため, 別 DBに保存することにしました.

C/C++のDLLが面倒くさい問題のため, LevelDB, RocketDBは候補から外しました. Microsoft FARSTERがよさそうですが, Lucene.Netと依存ライブラリで問題が出ました, これで一日消費しました. 単純なkey-valueでいいのですが, 仕方がないのでLiteDBを選択しました.

索引付け

索引付けは次のようにしました.

  1. 検索対象をソリューションファイルから収集
    • ソリューションのアイテムをロックできないので, 処理中に変更操作があると何が起きるかわかりません
  2. 最後に調べたファイルの更新時刻と比較して, 索引付け候補を絞り込み
    • 別にDBを用意しました
  3. Luceneの索引を行ごとに更新

未解決の問題はプロジェクトが削除された場合にどうするかです.

パフォーマンス

環境は次のとおり,

CPU メモリ
Core i7-8700 32 GB

全てデバッグビルドでVisual Studioのデバッガに繋いだ状態です.

インデックス作成

手元にあった, おそらくローカルひとつで, これより巨大なプロジェクトはなかなかない, Unreal Engine 4.27.2で試しました.

処理 時間(ミリ秒) 備考
インデックス作成候補のファイル収集 380 34133ファイル
更新日時をチェックしてカリング 1812
インデックス作成 422517 34120ファイル, 5373439行

完全に空のファイルはインデックス作成に含まれないので, 少なくなっています.

ファイル サイズ
Luceneのインデックスファイル群 約343MB
LiteDBのDB 約11MB

Unreal Engineが無駄に消費するストレージサイズに比べればゴミのようなサイズです.

検索

こちらEntrian Source Searchで, r.invalidateCachedShaderが約3秒で検索できるということです.

r.invalidateCachedShaderで検索するとrにヒットしたり, invalidateCachedShaderだとヒットしなかったり, インデックスの付け方と検索クエリが悪いのですが, それでも最大1000件の検索で300ミリ秒をきるぐらいなので, ここから使い勝手を磨いていきたいところです.

まとめ

とりあえず動いて早期リリース, というより, 事情により使いにくくても自分のために実践投入したいので, 不具合は後回しです.

初回起動時は完全に固まってしまうのは, なんなのですかね. awaitの仕様を勘違いしてるのかな.