Skip to content

[Feature] --symbol 検索オプション実装(シンボル名による関数・クラス検索) #38

@Kewton

Description

@Kewton

概要

commandindex search --symbol <name> オプションを実装し、関数名・クラス名でシンボルを検索できるようにする。

背景・動機

開発者が「この関数はどこに定義されているか」「このクラスの実装はどこか」を素早く検索するために必要。

提案する解決策

CLI オプション

commandindex search --symbol handleAuth
commandindex search --symbol UserService --format json
commandindex search --symbol "parse_*"    # ワイルドカード対応(将来)

CLI 設計変更

  • query 引数を Option<String> に変更(現在は必須 String
  • --symbol オプションを Option<String> として追加
  • clap の conflicts_with を使って --symbolquery を排他制御
  • どちらも指定されない場合はアプリケーション側でエラーメッセージを表示(「Either or --symbol is required」)

アーキテクチャ設計方針

tantivy 検索パスと SQLite シンボル検索パスを明確に分離する:

  • SearchOptions.queryString のまま維持(既存の tantivy 検索パスに影響を与えない)
  • main.rs レベルで --symbol の有無により分岐:
    • query 指定時 → 既存の cli::search::run() を呼び出し(変更なし)
    • --symbol 指定時 → 新設の cli::search::run_symbol_search() を呼び出し
  • cli/search.rsSearchError の新バリアント追加:
    • SymbolStore(SymbolStoreError) — SQLite エラー(From<SymbolStoreError> 変換と source() 更新を含む)
    • SymbolDbNotFound — symbols.db 不在

検索ロジック

  1. symbols.db の symbols テーブルから名前で検索
  2. 新規メソッド find_by_name_like(name: &str, limit: usize) を追加(既存の find_by_name() は変更しない)
  3. 部分一致(LIKE %name%、COLLATE NOCASE で大文字小文字区別なし)
  4. LIKE 特殊文字のエスケープ: 入力中の %_\%\_ にエスケープし、ESCAPE '\' 句を使用
  5. 結果に ファイルパス、行番号、シンボル種別、所属クラスを含める
  6. --limit オプションを --symbol 検索にも適用(SQL LIMIT 句で制御)

階層表示の仕様

前提: 現在の convert_symbol() (src/cli/index.rs) は parent_symbol_id を常に None で格納している。階層表示を実現するには index コマンドの変更が必要。

index コマンドの変更(本Issue スコープ内):

  • index_code_file() 内で2パス方式を実装:
    1. 全シンボルを parent_symbol_id = None で INSERT(convert_symbol() は変更不要)
    2. find_by_file() で挿入済みシンボルを取得
    3. parser の parent フィールド(クラス名文字列)をキーに、シンボル名→id のマッピングを構築
    4. 子シンボルの parent_symbol_id を UPDATE する新メソッド update_parent_symbol_id() を SymbolStore に追加
  • 制約: parent_symbol_id は同一ファイル内の親子関係のみ対象
  • idx_symbols_parent ON symbols(parent_symbol_id) インデックスを create_tables() 内に追加
  • insertupdate は同一トランザクション内で実行

incremental update への影響:

  • run_incremental()index_code_file() 経由で呼ばれるため、2パス方式が index_code_file() 内で完結していれば追加変更不要
  • delete_by_file() の CASCADE 削除は既存通り動作

検索時の階層表示:

  • クラス検索時: find_children_by_parent_id() で子シンボルも取得して階層表示
  • メソッド検索時: 親クラス名を併記
  • N+1問題の回避: find_by_file() で全シンボルを一括取得し、HashMap<Option<i64>, Vec<SymbolSearchResult>> でメモリ上にツリー構築(再帰SQLは使用しない)

出力フォーマット

SymbolSearchResult 構造体定義:

pub struct SymbolSearchResult {
    pub name: String,
    pub kind: String,          // "function", "class", "method"(小文字)
    pub file_path: String,
    pub line_start: u32,
    pub line_end: u32,
    pub parent_name: Option<String>,
    pub children: Vec<SymbolSearchResult>,
}
  • output モジュールに format_symbol_results() 系関数を追加
  • 既存の SearchResult および format_results() は変更しない

各フォーマッタに追加:

  • human.rsformat_symbol_human() — kind の小文字表示は本関数内で .to_lowercase()
  • json.rsformat_symbol_json() — kind は小文字で出力
  • path.rsformat_symbol_path()file_path:line_start 形式、重複除去

human形式:

[function] handleAuth
  src/auth/handler.ts:42-58

[class] UserService
  src/services/user.ts:10-85
    [method] getUser (line 25-40)
    [method] updateUser (line 42-60)

json形式:

{"name": "handleAuth", "kind": "function", "path": "src/auth/handler.ts", "line_start": 42, "line_end": 58}

path形式:

src/auth/handler.ts:42
src/services/user.ts:10

--symbol と query の排他性

--symbol は通常の全文検索 query とは排他。--symbol 指定時は symbols.db のみ検索。

エラーハンドリング

  • --symbolquery を同時指定: clap の排他制御でエラーメッセージ表示
  • どちらも未指定: 「Either or --symbol is required」
  • symbols.db が存在しない場合: 「Symbol database not found. Run commandindex index first.」
  • シンボルが見つからない場合: 「No symbols found matching ''」

SymbolKind 文字列正規化

  • DB格納時は SymbolKind::to_string() の出力をそのまま使用("Function", "Class", "Method")
  • 表示時は各フォーマッタ内で小文字化(.to_lowercase()
  • JSON 出力も小文字で統一

前提条件(ビルドエラー修正)

  • src/cli/index.rs:14parse_code_content import を parse_code_file に修正する(現在ビルドが通らない)

テスト計画

既存テストへの影響

  • tests/cli_args.rssearch_requires_query_argument を修正
    • 変更後の期待値: stderr に「Either or --symbol is required」を含むこと、exit code は 1
  • 既存 E2E テスト(e2e_code_index.rs, incremental_update.rs)は変更不要

新規テスト

  • CLI引数テスト: --symbol オプションのパース、排他制御、両方未指定時のエラー
  • symbol_store テスト: find_by_name_like() 部分一致検索、LIKE特殊文字エスケープ、find_children_by_parent_id()
  • 出力フォーマットテスト: SymbolSearchResult の human/json/path 出力
  • E2E テスト: --symbol 検索のエンドツーエンド動作
    • tests/common/mod.rsrun_symbol_search() ヘルパー追加
  • index コマンドテスト: parent_symbol_id が正しく設定されることの検証
  • incremental update テスト: ファイル修正→update後に parent_symbol_id が正しいことの検証

受け入れ基準

  • --symbol で関数名検索ができる
  • --symbol でクラス名検索ができる
  • 部分一致(LIKE、大文字小文字区別なし)で検索できる
  • human / json / path の各出力形式に対応
  • --symbol と query を同時指定した場合にエラーメッセージが表示される
  • どちらも未指定の場合にエラーメッセージが表示される
  • シンボルが見つからない場合に適切なメッセージが表示される
  • symbols.db が存在しない場合に適切なエラーメッセージが表示される
  • --limit オプションが --symbol 検索にも適用される
  • クラス検索時に子メソッドが階層表示される
  • parent_symbol_id がインデックス時に正しく設定される
  • 既存の全文検索機能が正常に動作する(回帰なし)
  • cargo test / clippy / fmt 全パス

依存 Issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions