概要
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 を使って --symbol と query を排他制御
- どちらも指定されない場合はアプリケーション側でエラーメッセージを表示(「Either or --symbol is required」)
アーキテクチャ設計方針
tantivy 検索パスと SQLite シンボル検索パスを明確に分離する:
SearchOptions.query は String のまま維持(既存の tantivy 検索パスに影響を与えない)
main.rs レベルで --symbol の有無により分岐:
query 指定時 → 既存の cli::search::run() を呼び出し(変更なし)
--symbol 指定時 → 新設の cli::search::run_symbol_search() を呼び出し
cli/search.rs に SearchError の新バリアント追加:
SymbolStore(SymbolStoreError) — SQLite エラー(From<SymbolStoreError> 変換と source() 更新を含む)
SymbolDbNotFound — symbols.db 不在
検索ロジック
- symbols.db の symbols テーブルから名前で検索
- 新規メソッド
find_by_name_like(name: &str, limit: usize) を追加(既存の find_by_name() は変更しない)
- 部分一致(LIKE
%name%、COLLATE NOCASE で大文字小文字区別なし)
- LIKE 特殊文字のエスケープ: 入力中の
% と _ を \%、\_ にエスケープし、ESCAPE '\' 句を使用
- 結果に ファイルパス、行番号、シンボル種別、所属クラスを含める
--limit オプションを --symbol 検索にも適用(SQL LIMIT 句で制御)
階層表示の仕様
前提: 現在の convert_symbol() (src/cli/index.rs) は parent_symbol_id を常に None で格納している。階層表示を実現するには index コマンドの変更が必要。
index コマンドの変更(本Issue スコープ内):
index_code_file() 内で2パス方式を実装:
- 全シンボルを
parent_symbol_id = None で INSERT(convert_symbol() は変更不要)
find_by_file() で挿入済みシンボルを取得
- parser の
parent フィールド(クラス名文字列)をキーに、シンボル名→id のマッピングを構築
- 子シンボルの
parent_symbol_id を UPDATE する新メソッド update_parent_symbol_id() を SymbolStore に追加
- 制約:
parent_symbol_id は同一ファイル内の親子関係のみ対象
idx_symbols_parent ON symbols(parent_symbol_id) インデックスを create_tables() 内に追加
insert と update は同一トランザクション内で実行
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.rs → format_symbol_human() — kind の小文字表示は本関数内で .to_lowercase()
json.rs → format_symbol_json() — kind は小文字で出力
path.rs → format_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 のみ検索。
エラーハンドリング
--symbol と query を同時指定: 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:14 の parse_code_content import を parse_code_file に修正する(現在ビルドが通らない)
テスト計画
既存テストへの影響
tests/cli_args.rs の search_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.rs に run_symbol_search() ヘルパー追加
- index コマンドテスト: parent_symbol_id が正しく設定されることの検証
- incremental update テスト: ファイル修正→update後に parent_symbol_id が正しいことの検証
受け入れ基準
依存 Issue
概要
commandindex search --symbol <name>オプションを実装し、関数名・クラス名でシンボルを検索できるようにする。背景・動機
開発者が「この関数はどこに定義されているか」「このクラスの実装はどこか」を素早く検索するために必要。
提案する解決策
CLI オプション
CLI 設計変更
query引数をOption<String>に変更(現在は必須String)--symbolオプションをOption<String>として追加conflicts_withを使って--symbolとqueryを排他制御アーキテクチャ設計方針
tantivy 検索パスと SQLite シンボル検索パスを明確に分離する:
SearchOptions.queryはStringのまま維持(既存の tantivy 検索パスに影響を与えない)main.rsレベルで--symbolの有無により分岐:query指定時 → 既存のcli::search::run()を呼び出し(変更なし)--symbol指定時 → 新設のcli::search::run_symbol_search()を呼び出しcli/search.rsにSearchErrorの新バリアント追加:SymbolStore(SymbolStoreError)— SQLite エラー(From<SymbolStoreError>変換とsource()更新を含む)SymbolDbNotFound— symbols.db 不在検索ロジック
find_by_name_like(name: &str, limit: usize)を追加(既存のfind_by_name()は変更しない)%name%、COLLATE NOCASE で大文字小文字区別なし)%と_を\%、\_にエスケープし、ESCAPE '\'句を使用--limitオプションを --symbol 検索にも適用(SQL LIMIT 句で制御)階層表示の仕様
前提: 現在の
convert_symbol()(src/cli/index.rs) はparent_symbol_idを常にNoneで格納している。階層表示を実現するには index コマンドの変更が必要。index コマンドの変更(本Issue スコープ内):
index_code_file()内で2パス方式を実装:parent_symbol_id = Noneで INSERT(convert_symbol()は変更不要)find_by_file()で挿入済みシンボルを取得parentフィールド(クラス名文字列)をキーに、シンボル名→id のマッピングを構築parent_symbol_idを UPDATE する新メソッドupdate_parent_symbol_id()を SymbolStore に追加parent_symbol_idは同一ファイル内の親子関係のみ対象idx_symbols_parent ON symbols(parent_symbol_id)インデックスをcreate_tables()内に追加insertとupdateは同一トランザクション内で実行incremental update への影響:
run_incremental()はindex_code_file()経由で呼ばれるため、2パス方式がindex_code_file()内で完結していれば追加変更不要delete_by_file()の CASCADE 削除は既存通り動作検索時の階層表示:
find_children_by_parent_id()で子シンボルも取得して階層表示find_by_file()で全シンボルを一括取得し、HashMap<Option<i64>, Vec<SymbolSearchResult>>でメモリ上にツリー構築(再帰SQLは使用しない)出力フォーマット
SymbolSearchResult 構造体定義:
format_symbol_results()系関数を追加SearchResultおよびformat_results()は変更しない各フォーマッタに追加:
human.rs→format_symbol_human()— kind の小文字表示は本関数内で.to_lowercase()json.rs→format_symbol_json()— kind は小文字で出力path.rs→format_symbol_path()—file_path:line_start形式、重複除去human形式:
json形式:
{"name": "handleAuth", "kind": "function", "path": "src/auth/handler.ts", "line_start": 42, "line_end": 58}path形式:
--symbol と query の排他性
--symbolは通常の全文検索 query とは排他。--symbol指定時は symbols.db のみ検索。エラーハンドリング
--symbolとqueryを同時指定: clap の排他制御でエラーメッセージ表示commandindex indexfirst.」SymbolKind 文字列正規化
SymbolKind::to_string()の出力をそのまま使用("Function", "Class", "Method").to_lowercase())前提条件(ビルドエラー修正)
src/cli/index.rs:14のparse_code_contentimport をparse_code_fileに修正する(現在ビルドが通らない)テスト計画
既存テストへの影響
tests/cli_args.rsのsearch_requires_query_argumentを修正新規テスト
find_by_name_like()部分一致検索、LIKE特殊文字エスケープ、find_children_by_parent_id()tests/common/mod.rsにrun_symbol_search()ヘルパー追加受け入れ基準
--symbolで関数名検索ができる--symbolでクラス名検索ができる--symbolと query を同時指定した場合にエラーメッセージが表示される--limitオプションが --symbol 検索にも適用される依存 Issue