📝

ast-grep VSCode: 構造検索と置換の強力なツール

2024/03/03に公開3

こんにちは、 ast-grepの作者Herringtonです。

正規表現でコードを検索したことがある方なら、複数行のマッチングや入れ子構造の処理、コメントの無視などに苦労したことがあるかもしれません。

そこで、ast-grep VSCodeという新しい拡張を紹介します。これは、構造的検索と置換(SSR)という技術を利用して、より正確で効率的な検索と置換を実現するツールです。

構造検索は?

テキスト検索と置換の限界

例えば、JavaScriptコードをリファクタリングして、lodash_.filter 関数をネイティブの Array.prototype.filter メソッド に置き換えたいとします。単純なテキスト検索と置換は次のようになります:

// 検索
_.filter\((\.+), (.+)\)
// 置換
$1.filter($2)

これは一部のケースではうまくいくかもしれませんが、いくつかの問題があります。

  • 一行の式しかマッチングできません。コードが複数行にまたがっている場合、マッチング漏れや不正確な置換が発生します。
  • 括弧や角括弧、波括弧などの入れ子構造を扱えません。コードに複雑な式が含まれている場合、不正確なマッチングや置換が発生します。
  • コメントの部分を無視できません。コードに検索パターンを含むコメントがある場合、望ましくないマッチングや置換が発生します。
  • 関数の引数を保持するために、一致グループや名前付きキャプチャグループを使う必要があります。これは、引数が多かったり、関数が入れ子になっていたりする場合、面倒でエラーが起きやすいです。

構造検索と置換が助けに来る

検索アルゴリズムをもっと賢くし、コードをもっと理解しやすくし、使いやすくすることはできないでしょうか?はい!構造的検索と置換(SSR)が素晴らしい解決策となります!

構造検索と置換 (Structural Search/Replace) は、コードパターンをその構文と意味に基づいて検索および変更する技術です。単なるテキストとしてではなく、SSRは、タイプ、プロパティ、および関係を持つノードのコレクションとしてコードを扱います。

ast-grep は、SSRを実装したコマンドラインツールです。上記のlodashの例のクエリは非常にシンプルです。

// 検索パターン:
_.filter($ARR, $FUNC)
// 書き換え:
$ARR.filter($FUNC)

このパターンは、ast-grepの基本的な使い方を示しています。$ARR$FUNCメタ変数です。メタ変数は、正規表現のドット.と似ていますが、文字ではなくASTノードにマッチします。また、置換パターンでキャプチャされたメタ変数を使うこともできます。

メタ変数以外は、パターンと置換が自明であるとおもいます。

このクエリは、テキストベースの検索と置換に比べていくつかの利点があります。

  • 構文的に有効であれば、複数行にまたがる式にもマッチングできます。
  • 括弧や角括弧、波括弧などの入れ子構造も扱えます。それらはASTの一部だからです。
  • コメントやコードと関係のない部分を無視できます。それらはASTの一部ではないからです。
  • マッチンググループや名前付きキャプチャグループは必要ありません。メタ変数は、検索パターンで名前でアクセスできるからです。

ast-grepのユニークな機能

ast-grepは、多くのプログラミング言語に対応した高速で堅牢なパーサーであるtree-sitterライブラリを使用しています。ast-grepでは、コードに似たシンプルなパターン構文を使ってクエリを書き、コードのファイルやディレクトリに適用することができます。ast-grepのユニークな機能の一部は以下の通りです。

  • JavaScript, TypeScript, Python, Ruby, Java, C#, Go, Rustなど、多くの言語をサポートしています。また、設定でtree-sitter文法を登録することで、新しい言語のサポートを追加することができます。
  • Rustで書かれているため、非常に高速でメモリ効率が良いです。大規模なコードベースを数秒で処理することができます。
  • ドライラン、インタラクティブモード、カラーアウトプットなど、豊富なオプションとフラグを持っています。SSRの体験を自分の好みに合わせてカスタマイズすることができます。

しかし、ast-grepは今のところコマンドラインインターフェースしかありません。ターミナルとエディターを切り替えるのではなく、手元でその力を使えるようになることはできないでしょうか?

ast-grep VSCode: CLIとエディターの橋渡し

ast-grep VSCodeは、ast-grepをVisual Studio Codeという人気の高いコードエディターと統合する新しい拡張機能です。ast-grep VSCodeを使えば、エディター内でSSRを使うことができ、ワークフローから離れることなくコードを改善できます。

構造的検索、置換、その他

ast-grep VSCodeの機能の一部は以下の通りです。

  • SSRクエリを書いたり実行したりするためのユーザーインターフェースを提供し、サイドバーでクエリの結果をプレビューや差分とともに見ることができます。
  • リンティングやコード修正もサポートしています。ast-grepプロジェクトを設定して、自分のニーズに合わせたカスタムルールを書くことができます。
  • VSCodeのネイティブUIのように感じられるように、シームレスな統合と一貫したデザインを備えています。
機能 スクリーンショット
検索パターン search pattern
フォルダ内検索 search in folder
置換プレビュー replace preview
置換コミット commit replace
コードリンティング code lint

他のSSRツールとの比較

他にもSSRツールや拡張機能はあります。しかし、ast-grep VSCodeがVSCodeの中で最高のSSR拡張機能の一つだと思っています。なぜなら:

  • ネイティブ言語で書かれたマルチスレッドCLIによる高性能を持っています。
  • コミュニティによって広く使われているtree-sitter文法を利用して、複数の言語をサポートしています。
  • VSCodeのビルトイン機能のように感じられる、ユーザーフレンドリーで直感的なインターフェースを持っています。youtube動画

最後に、ast-grep VSCodeで使われているReactのテクニックのいくつかを紹介します。これらはast-grep VSCodeを高速でレスポンシブにするのに役立っています。

  • useSyncExternalStoreは、ast-grep CLIからのストリーミング結果を管理するのに使われました。このフックは、コンポーネントが外部の可変なデータソースにサブスクライブして、レンダリングを適切に更新することを可能にします。このようにして、拡張機能は結果をすぐに表示することができます。
  • useDeferredValueは、結果リストのレンダリングを遅延させました。このフックは、派生した状態の更新を次の並行レンダーまで遅らせることで、ユーザー入力をブロックすることを避け、UIのパフォーマンスを向上させることができます。
  • JavaScriptの負荷を減らすために、memoやCSSのテクニックを丁寧に多用しています。

まとめ

この記事がSSRのメリットやast-grep VSCodeの機能について理解するのに役立つと思います。もし試してみたいと思ったら、VSCode Marketplaceからインストールすることができます。ast-grep VSCodeはまだ開発中で、改善の余地がたくさんあります。フィードバックや提案を聞きたいので、GitHubでイシューやプルリクエストを気軽に開いてください。また、SoonIterさん、Steven Loveさん、Nyakku Shigureさん、ssr.nvimにも、貢献やインスピレーションをありがとうと言いたいです!

楽しく grep しましょう!

Discussion

ykyk

私も似たような拡張機能を作っているものです。(HTML Likeなファイルをターゲットにしてます。)

Typescriptで同じようなことがやりたかったのでぜひ今後使わせていただきます。
また、アプローチ自体は知っていたのですが、構造的検索と置換(SSR)という名前があることを知ることができ大変勉強になりました。

最後にいくつか共有させてください。

・私の環境(Windows10)では導入する際、npm i @ast-grep/cli -gした後、(もしかするとvscode再起動も?)preference>Ast Grep: Server Pathをデフォルトからast-grep.cmdに変更する必要がありました。
ちなみに、cargoからのインストールは私の環境にgccがインストールされておらず失敗しました。
(tree-sitterのビルドで失敗してるようでした。)

・検索パターンには、通常の$METHOD($ARGS)パターンとyamlで記載するパターンがあるようですが、拡張機能ではyamlサポートされていないのでしょうか。

・リポジトリを確認したのですが、l10n(ローカライズ)の対応はサポートされるのでしょうか。
先日Webviewへのl10nファイルの受け渡しを行うことができたので、もしよければ参考にしてください。
https://github.com/microsoft/vscode/issues/206547

・置換において、ネストされたパターンを置き換える際、親のみ置換、親と影響しない子を置換する方法があると考えているのですが、ast-grepではどのように考えていますでしょうか。
ちなみに私のほうは全件置き換える場合、親のみ置換する方向で進めていますが、将来的には後者の方法に移行したいと考えています。

長くなりましたが、とても良い拡張機能だと思います。

Herrington DarkholmeHerrington Darkholme

コメント頂きありがとうございます!とても思いやりのある詳しい返信で、感謝します🫰。

  • Windows10 のご報告ありがとう。Windows環境のテストはまだありませんのせいと思います。Windowsのテストやドキュメントを検討します。
  • 今yamlサポートされていないですが、ロードマップで計画を立てた。https://github.com/ast-grep/ast-grep-vscode/issues/167
  • l10nは力不足でおそらくサポートされないが、Webview利用方法は参考になります!
  • 実装の容易さから、ast-grepは親のみ置換します。「親と影響しない子を置換」を実装したいが、recursive replacementは止めないの場合があるですから、いい方法を見つかったらせひ教えて下さい〜

海外の開発者なので日本語が下手し、多数のmachine翻訳を利用し、言い間違いがあったら気にしないでください🙏

ありがとうございます!

Herrington DarkholmeHerrington Darkholme

Windowsのデフォルトコマンドはver0.1.1でast-grep.exeに変更しました。たぶん設定しないも動けるとおもいます。
ご報告ありがとう!