# 機能設計書 20-ドキュメントシンボル (textDocument/documentSymbol)

## 概要

本ドキュメントは、serve-d LSPサーバーにおける「ドキュメントシンボル」機能の設計を記述する。LSPの `textDocument/documentSymbol` リクエストを処理し、現在のドキュメント内の全シンボル（関数・クラス・構造体等）を階層的またはフラットに返却する機能である。

### 本機能の処理概要

DScanner（D言語静的解析ツール）を使用してドキュメント内のシンボル（関数、クラス、構造体、列挙型、変数等）を解析し、アウトラインビュー表示用のシンボル情報を返却する。クライアントの能力に応じて、階層的なDocumentSymbol形式またはフラットなSymbolInformation形式で返却する。

**業務上の目的・背景**：ファイル内のシンボル一覧（アウトラインビュー）は、ファイル構造の把握とナビゲーションに不可欠な機能である。特にD言語のようにクラス・構造体・テンプレート・ミックスインテンプレート等の多様な構文要素を持つ言語では、アウトラインビューによる構造把握が開発効率に大きく貢献する。

**機能の利用シーン**：エディタのアウトラインビュー（サイドバー）の表示時、ブレッドクラムナビゲーション、Go to Symbolコマンドの実行時にトリガーされる。ファイルを開いた時点で自動的にリクエストされることが多い。

**主要な処理内容**：
1. クライアントの hierarchicalDocumentSymbolSupport 能力判定
2. DScanner.listDefinitions でドキュメント内の定義一覧を取得
3. 各定義をSymbolInformationEx/DocumentSymbolInfo形式に変換
4. 階層モード: 親子関係に基づくネスト構造の構築
5. フラットモード: SymbolInformation配列としてそのまま返却
6. キャッシュによる重複計算の回避

**関連システム・外部連携**：DscannerComponent

**権限による制御**：DscannerComponentが利用可能である必要がある。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | ドキュメントシンボル（textDocument/documentSymbol） | 主機能 | 現在のドキュメント内の全シンボルを階層的またはフラットに返却する（アウトライン表示用） |

## 機能種別

データ取得・変換処理 / コード解析

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| params | DocumentSymbolParams | Yes | ドキュメントシンボル要求 | - |
| params.textDocument.uri | DocumentUri | Yes | 対象ドキュメントのURI | 有効なURI文字列 |

### 入力データソース

- LSPクライアントからの `textDocument/documentSymbol` リクエスト
- DscannerComponentからの定義一覧

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (階層モード) | DocumentSymbol[] | 階層的なシンボル情報（子シンボル含む） |
| (フラットモード) | SymbolInformation[] | フラットなシンボル情報 |
| name | string | シンボル名 |
| kind | SymbolKind | シンボル種別（class, function, variable等） |
| location/range | TextRange | シンボルの位置範囲 |
| containerName | string | 親シンボル名（フラットモード） |
| children | DocumentSymbol[] | 子シンボル（階層モード） |
| tags | SymbolTag[] | タグ（deprecatedなど） |
| detail | string | シグネチャ等の詳細情報 |

### 出力先

LSPクライアントへのレスポンスメッセージ（JsonValue形式）

## 処理フロー

### 処理シーケンス

```
1. DocumentSymbolParams を受信
2. クライアント能力判定
   ├─ hierarchicalDocumentSymbolSupport == true:
   │   └─ provideDocumentSymbolsHierarchical を呼び出し
   └─ false:
       └─ provideDocumentSymbolsOld を呼び出し（verbose=true）
3. フラット形式シンボル取得 (provideDocumentSymbolsOld):
   3-1. キャッシュ確認（documentSymbolsCacheOld）
   3-2. DscannerComponent が利用可能か確認
   3-3. 言語ID が "d" か確認
   3-4. DscannerComponent.listDefinitions で定義一覧取得
   3-5. 各定義を SymbolInformationEx に変換
        ├─ name, location, kind, extendedType, containerName, tags, detail を設定
        └─ verbose/non-verbose の2種類で保存
   3-6. キャッシュに保存
4. 階層形式シンボル取得 (provideDocumentSymbolsHierarchical):
   4-1. キャッシュ確認（documentSymbolsCacheHierarchical）
   4-2. provideDocumentSymbolsOld（verbose=true）で全シンボル取得
   4-3. SymbolInformationEx → DocumentSymbolInfo に変換
   4-4. containerNameに基づく親子関係構築
   4-5. ルートシンボル（親なし）のみフィルタして返却
   4-6. キャッシュに保存
```

### フローチャート

```mermaid
flowchart TD
    A[textDocument/documentSymbol 受信] --> B{hierarchicalSupport?}
    B -->|Yes| C[provideDocumentSymbolsHierarchical]
    B -->|No| D[provideDocumentSymbolsOld]
    C --> E[キャッシュ確認]
    E -->|ヒット| F[キャッシュ返却]
    E -->|ミス| G[provideDocumentSymbolsOld verbose=true]
    G --> H[SymbolInformationEx → DocumentSymbolInfo変換]
    H --> I[containerNameで親子関係構築]
    I --> J[ルートシンボルフィルタ]
    J --> K[キャッシュ保存・返却]
    D --> L[キャッシュ確認]
    L -->|ヒット| M[キャッシュ返却]
    L -->|ミス| N[DscannerComponent.listDefinitions]
    N --> O[SymbolInformationEx変換]
    O --> P[キャッシュ保存・返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-20-01 | 能力ベース形式切替 | クライアントのhierarchicalDocumentSymbolSupport能力に応じてDocumentSymbolまたはSymbolInformation形式を返却 | 全リクエスト |
| BR-20-02 | D言語限定 | D言語以外のファイルではnullを返却 | provideDocumentSymbolsOld |
| BR-20-03 | verbose/non-verboseフィルタ | isVerboseType の定義（unittest, debug, version等）はnon-verboseでは除外 | フラットモード |
| BR-20-04 | キャッシュ戦略 | PerDocumentCacheでドキュメント変更時にキャッシュ無効化 | 全リクエスト |
| BR-20-05 | 親子関係構築 | containerName と name の一致でDocumentSymbolの子に追加 | 階層モード |
| BR-20-06 | deprecatedタグ | 定義の attributes に "deprecation" が含まれる場合はSymbolTag.deprecated_を付与 | 全定義 |
| BR-20-07 | DScanner型変換 | DScannerのタイプ文字をLSP SymbolKindに変換（convertFromDscannerType） | 全定義 |

### 計算ロジック

DScanner型変換（convertFromDscannerType）:
- c=class, s=struct, i=interface, T=property(template)
- f/U/Q/W/P/N=function（"this"の場合はconstructor）
- C/S=constructor, g=enum, u=struct(union)
- D/V/e=constant, v=variable, a=field, :=module

## データベース操作仕様

本機能ではデータベース操作は行わない。インメモリキャッシュ（PerDocumentCache）のみ使用。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | コンポーネント不在 | DscannerComponentが利用不可 | null返却 |
| - | 言語ID不一致 | D言語以外のファイル | null返却 |

### リトライ仕様

リトライなし。キャッシュが利用可能であればキャッシュから返却。

## トランザクション仕様

トランザクション管理なし。読み取り専用操作。

## パフォーマンス要件

アウトラインビューの表示に使用されるため、低遅延が求められる。PerDocumentCacheによりドキュメント未変更時はキャッシュから即座に返却。DScanner解析はgetYieldで非同期結果を取得。

## セキュリティ考慮事項

特になし。

## 備考

- `SymbolInformationEx` は標準の `SymbolInformation` を拡張した型で、`extendedType`（SymbolKindEx: test, debugSpec, versionSpec等）を含む
- 階層モードでは `containerName` ベースの簡易的な親子関係構築を行っている（同名の場合は最初に見つかったシンボルの子になる）
- `provideDocumentSymbols` はJsonValueを返却し、内部でtoJsonValueにより適切な形式に変換する

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | symbol_search.d | `source/served/commands/symbol_search.d` | `DocumentSymbolInfo` 構造体（130-136行目）: DocumentSymbolにparent文字列を追加したラッパー |
| 1-2 | complete.d | `source/served/commands/complete.d` | `convertFromDscannerType` 関数（163-205行目）: DScannerタイプ文字→SymbolKind変換 |

**読解のコツ**: `@serdeProxy!DocumentSymbol` アトリビュートにより、DocumentSymbolInfoはシリアライズ時にDocumentSymbol型として扱われる。`alias symbol this;` によりDocumentSymbolのメンバーに直接アクセス可能。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | symbol_search.d | `source/served/commands/symbol_search.d` | `provideDocumentSymbols` 関数（51-61行目）がLSPメソッドのエントリーポイント |

**主要処理フロー**:
1. **51行目**: `@protocolMethod("textDocument/documentSymbol")` でLSPメソッド登録
2. **54-57行目**: `hierarchicalDocumentSymbolSupport` 能力判定
3. **58行目**: 階層モードの場合 `provideDocumentSymbolsHierarchical` 呼び出し
4. **60行目**: フラットモードの場合 `provideDocumentSymbolsOld` 呼び出し（verbose=true、downcast）

#### Step 3: フラット形式シンボル取得

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | symbol_search.d | `source/served/commands/symbol_search.d` | `provideDocumentSymbolsOld` 関数（70-106行目）のDScanner連携と変換処理 |

**主要処理フロー**:
- **75-77行目**: キャッシュ確認とヒット時の早期返却
- **78-80行目**: ドキュメント取得と言語ID確認
- **82-84行目**: `DscannerComponent.listDefinitions` で定義一覧取得
- **91-102行目**: 各定義のSymbolInformationEx変換（バイトオフセット→Position変換含む）
- **98行目**: `makeSymbolInfoEx` で変換
- **103行目**: キャッシュ保存

#### Step 4: 階層形式シンボル取得

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | symbol_search.d | `source/served/commands/symbol_search.d` | `provideDocumentSymbolsHierarchical` 関数（139-178行目）の親子関係構築ロジック |

**主要処理フロー**:
- **141-143行目**: キャッシュ確認
- **145行目**: `provideDocumentSymbolsOld(verbose=true)` で全シンボル取得
- **146-158行目**: SymbolInformationEx → DocumentSymbolInfo 変換（range/selectionRange設定）
- **160-173行目**: containerNameベースの親子関係構築（ネスト構造化）
- **175行目**: ルートシンボル（parent.lengthが0）のフィルタ
- **176行目**: キャッシュ保存

#### Step 5: シンボル変換ユーティリティ

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | symbol_search.d | `source/served/commands/symbol_search.d` | `makeSymbolInfoEx` 関数（108-128行目）のDScanner定義→SymbolInformationEx変換 |

**主要処理フロー**:
- **111行目**: name設定
- **112-113行目**: location設定（uri + range）
- **114行目**: `convertFromDscannerType` でkind設定
- **115行目**: `convertExtendedFromDscannerType` でextendedType設定
- **118-120行目**: containerName設定（struct/class/enum/union）
- **121-122行目**: deprecatedタグ設定
- **123-126行目**: signature/detail設定

### プログラム呼び出し階層図

```
provideDocumentSymbols (symbol_search.d:52)
    │
    ├─ [階層モード] provideDocumentSymbolsHierarchical (symbol_search.d:139)
    │      ├─ provideDocumentSymbolsOld (verbose=true)
    │      └─ 親子関係構築（containerNameベース）
    │
    └─ [フラットモード] provideDocumentSymbolsOld (symbol_search.d:70)
           ├─ documentSymbolsCacheOld（キャッシュ）
           ├─ DscannerComponent.listDefinitions
           └─ makeSymbolInfoEx (symbol_search.d:108)
                  ├─ convertFromDscannerType (complete.d:163)
                  └─ convertExtendedFromDscannerType (complete.d:207)
```

### データフロー図

```
[入力]                     [処理]                            [出力]

DocumentSymbolParams ──▶ provideDocumentSymbols ──────▶ JsonValue
                              │                          (DocumentSymbol[] or
                              │                           SymbolInformation[])
                              ├─ DscannerComponent
                              │    └─ listDefinitions
                              │         └─ DefinitionElement[]
                              ├─ makeSymbolInfoEx
                              │    └─ SymbolInformationEx
                              └─ 階層構築（オプション）
                                   └─ DocumentSymbolInfo[]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| symbol_search.d | `source/served/commands/symbol_search.d` | ソース | provideDocumentSymbols, provideDocumentSymbolsOld, provideDocumentSymbolsHierarchical, makeSymbolInfoEx |
| complete.d | `source/served/commands/complete.d` | ソース | convertFromDscannerType, convertExtendedFromDscannerType |
| extension.d | `source/served/extension.d` | ソース | コマンド登録 |
| types.d | `source/served/types.d` | ソース | LSP型定義、PerDocumentCache |
