# プログラム設計書 29-codeLensResolve

## 1. 概要

### このプログラムの目的

このプログラムは、コードレンズの詳細情報を遅延取得するためのものです。初期のコードレンズリクエストでは軽量な位置情報のみを返し、ユーザーがコードレンズにホバーまたはクリックした際に、この resolve 処理で参照数やコマンド情報を取得します。

### このプログラムの役割

- **呼び出し元**: LSPプロトコルの `codeLens/resolve` リクエストハンドラ
- **主な処理**: 指定されたコードレンズIDの詳細情報（参照数、クリック時のコマンド）を取得
- **出力**: 完全なコードレンズ情報（タイトル、コマンド）

## 2. ファイル基本情報

| 項目 | 内容 |
|-----|------|
| ファイルパス | `script/core/code-lens.lua` |
| ファイル名 | code-lens.lua |
| 種別 | ソースコード（code-lens.lua の一部機能） |
| 使用言語 | Lua |
| 該当行 | 55-64行目（mt:resolve）、89-121行目（mt:resolveReference）、150-161行目（resolve関数） |
| 作成目的 | コードレンズの遅延解決機能を提供する |

## 3. 用語解説

このプログラムを理解するために必要な専門用語を解説します。

| 用語 | 読み方 | 意味・説明 |
|-----|--------|----------|
| Resolve | リゾルブ | 解決。軽量な初期情報から詳細情報を取得すること |
| Lazy Loading | レイジーローディング | 遅延読み込み。必要になった時点で情報を取得する手法 |
| Command | コマンド | LSPでクリック時に実行されるアクション |
| Location | ロケーション | ファイルと位置を示す情報 |

### 言語固有の概念

No.28（code-lens）と同じファイルのため、用語解説はNo.28を参照してください。

## 4. 依存関係

### このプログラムが使用している外部ファイル

No.28（code-lens）と同じファイルのため、依存関係はNo.28を参照してください。

## 5. データ構造詳細

### 主要な変数・データ項目

#### resolve 関数の戻り値（proto.command 型）

| 項目名 | 型 | 用途・説明 |
|-------|--------|----------|
| title | string | コードレンズに表示されるテキスト（例: "3 references"） |
| command | string | クリック時に実行されるコマンド名 |
| arguments | array | コマンドに渡される引数 |

**解説**: コードレンズの表示内容と、クリック時の動作を定義するオブジェクトです。

## 6. 処理詳細解説

このプログラムの処理内容をステップバイステップで説明します。

### 6.1 処理一覧

| No | 処理名 | 行番号 | 概要 |
|----|-------|--------|------|
| 1 | resolve（エントリポイント） | 150-161 | 解決処理のメインエントリ |
| 2 | mt:resolve | 55-64 | ID から解決情報を取得 |
| 3 | mt:resolveReference | 89-121 | 参照数を取得してコマンドを生成 |

### 6.2 resolve（エントリポイント）の詳細（150-161行目）

**この処理は何をするものか**: codeLens/resolve リクエストのエントリポイントとして、保存されたコードレンズオブジェクトから詳細情報を取得します。

**処理の流れ**:

```
ステップ1: パーサー状態を取得
    │
    ↓
ステップ2: 保存されたcodeLensオブジェクトを取得
    │
    ↓
ステップ3: IDを使って解決処理を実行
    │
    ↓
ステップ4: コマンド情報を返す
```

**ステップバイステップ解説**:

1. **ステップ1: パーサー状態の取得**（151-154行目）

   ```lua
   local state = files.getState(uri)
   if not state then
       return nil
   end
   ```

   ファイルのパーサー状態を取得します。ファイルが閉じられた場合などは `nil` を返します。

2. **ステップ2: codeLensオブジェクトの取得**（155-158行目）

   ```lua
   local codeLens = state._codeLens
   if not codeLens then
       return nil
   end
   ```

   `getCodeLens` 時に保存されたオブジェクトを取得します。これにより、初期リクエスト時の状態を再利用できます。

3. **ステップ3-4: 解決処理の実行**（159-160行目）

   ```lua
   local command = codeLens:resolve(id)
   return command
   ```

   ID を指定して `resolve` メソッドを呼び出し、詳細情報を取得します。

**この処理のポイント**:
- `state._codeLens` から前回のオブジェクトを再利用することで、情報の一貫性を保証
- ファイルが再解析されると `_codeLens` も更新されるため、古い情報は使用されない

### 6.3 mt:resolve の詳細（55-64行目）

**この処理は何をするものか**: 指定されたIDの解決情報を取得し、適切な解決処理を呼び出します。

**ステップバイステップ解説**:

```lua
function mt:resolve(id)
    local resolving = self.resolving[id]
    if not resolving then
        return nil
    end
    if resolving.mode == 'reference' then
        return self:resolveReference(resolving.source)
    end
end
```

1. **IDから解決情報を取得**（57行目）

   `self.resolving` テーブルから、IDに対応する解決情報を取得します。

2. **解決情報の確認**（58-60行目）

   解決情報が存在しない場合は `nil` を返します。

3. **モードによる分岐**（61-63行目）

   現在は `'reference'` モードのみ対応しています。将来的に他のモード（テスト実行など）を追加できる設計になっています。

**この処理のポイント**:
- `resolving` テーブルで ID と解決情報を紐付け
- モードによる分岐で拡張性を確保

### 6.4 mt:resolveReference の詳細（89-121行目）

**この処理は何をするものか**: 関数の参照数を取得し、表示用のコマンドオブジェクトを生成します。

**処理の流れ**:

```
ステップ1: 参照を検索
    │
    ↓
ステップ2: クライアントオプションを確認
    │
    ↓（codeLensViewReferences が有効）
ステップ3: 参照位置のリストを作成
    │
    ↓
ステップ4: showReferences コマンドを生成
    │
    ↓（codeLensViewReferences が無効）
ステップ5: カウントのみのコマンドを生成
```

**ステップバイステップ解説**:

1. **ステップ1: 参照の検索**（90-91行目）

   ```lua
   local refs = getRef(self.uri, source.finish, false)
   local count = refs and #refs or 0
   ```

   `core.reference` モジュールを使用して参照を検索し、カウントを取得します。

2. **ステップ2-4: 参照表示が有効な場合**（92-112行目）

   ```lua
   if client.getOption('codeLensViewReferences') then
       local locations = {}
       for _, ref in ipairs(refs or {}) do
           local state = files.getState(ref.uri)
           if state then
               locations[#locations+1] = conv.location(
                   ref.uri,
                   conv.packRange(state, ref.target.start, ref.target.finish)
               )
           end
       end
       local command = conv.command(
           lang.script('COMMAND_REFERENCE_COUNT', count),
           'lua.showReferences',
           {
               self.uri,
               conv.packPosition(self.state, source.start),
               locations,
           }
       )
       return command
   end
   ```

   - 全ての参照位置を LSP Location 形式に変換
   - `lua.showReferences` コマンドを生成（クリック時に参照一覧を表示）

3. **ステップ5: 参照表示が無効な場合**（113-120行目）

   ```lua
   else
       local command = conv.command(
           lang.script('COMMAND_REFERENCE_COUNT', count),
           '',
           {}
       )
       return command
   end
   ```

   - 空のコマンドを生成（クリックしても何も起きない）
   - 参照数のテキストのみ表示

**この処理のポイント**:
- `lang.script('COMMAND_REFERENCE_COUNT', count)` で多言語対応のテキストを生成
- クライアントオプションによってクリック動作を切り替え

## 7. 処理フロー図

プログラム全体の処理の流れを図で示します。

```mermaid
flowchart TD
    A[codeLens/resolve リクエスト] --> B[パーサー状態取得]
    B --> C{状態存在?}
    C -->|No| Z[nullを返す]
    C -->|Yes| D[保存されたcodeLensオブジェクト取得]
    D --> E{オブジェクト存在?}
    E -->|No| Z
    E -->|Yes| F[IDで解決情報を検索]
    F --> G{解決情報存在?}
    G -->|No| Z
    G -->|Yes| H{モード判定}
    H -->|reference| I[参照を検索]
    I --> J[参照数をカウント]
    J --> K{viewReferences有効?}
    K -->|Yes| L[位置リスト作成]
    L --> M[showReferencesコマンド生成]
    K -->|No| N[カウントのみコマンド生成]
    M --> O[コマンドを返す]
    N --> O
```

**図の説明**:
- **codeLens/resolve リクエスト**: ユーザーがコードレンズにホバー/クリック時
- **保存されたcodeLensオブジェクト取得**: getCodeLens 時に保存されたオブジェクトを再利用
- **showReferencesコマンド生成**: クリック時に参照一覧を表示するコマンド

## 8. 外部連携

### 呼び出し関係図

```
[呼び出し元]                    [このプログラム]                    [呼び出し先]
                                     │
provider.lua ──────────────▶ code-lens.lua ────────────────▶ reference.lua
  (codeLens/resolve)                 │  (resolve)                (参照検索)
                                     │
                                     ├──────────────▶ client.getOption()
                                     │                   (オプション確認)
                                     │
                                     └──────────────▶ conv.command()
                                                        (コマンド生成)
```

### 呼び出し元（このプログラムを呼び出すもの）

| プログラム名 | 呼び出し契機 | 渡されるデータ |
|------------|------------|--------------|
| provider.lua | コードレンズにホバー/クリック時 | URI, ID |

### 呼び出し先（このプログラムが呼び出すもの）

| プログラム名 | 呼び出し目的 | 渡すデータ | 受け取るデータ |
|------------|------------|----------|--------------|
| files.getState | パーサー状態取得 | URI | 状態オブジェクト |
| reference.lua | 参照検索 | URI, 位置 | 参照リスト |
| client.getOption | オプション確認 | オプション名 | 値 |
| conv.command | コマンド生成 | タイトル、コマンド名、引数 | コマンドオブジェクト |
| conv.location | 位置情報変換 | URI, 範囲 | Location オブジェクト |

## 9. エラー処理

このプログラムで発生する可能性があるエラーと、その対処方法を説明します。

### エラー一覧

| エラー種別 | 発生条件 | 対処内容 | 該当行 |
|----------|---------|---------|--------|
| 状態なし | ファイルが閉じられた | null を返す | 151-154 |
| オブジェクトなし | codeLens が保存されていない | null を返す | 155-158 |
| 解決情報なし | ID に対応する情報がない | null を返す | 58-60 |

### エラー発生時の動作

`nil` が返された場合、コードレンズは表示されないか、または詳細情報なしで表示されます。

## 10. このプログラムを読むときのポイント

このプログラムを理解するためのアドバイスです。

### 推奨する読み方

1. **まず resolve エントリポイントを見る**: 全体の流れを把握
2. **次に mt:resolve を見る**: ID による振り分けを理解
3. **最後に mt:resolveReference を見る**: 具体的な解決処理

### 注意すべきポイント

- `state._codeLens` は getCodeLens 時に設定され、resolve 時に再利用されます
- ファイルが再解析されると `_codeLens` も更新されます
- `conv.command` は LSP の Command オブジェクトを生成するヘルパー関数です

### 関連して読むべきファイル

| ファイル | パス | 関連性 |
|---------|------|--------|
| reference.lua | `script/core/reference.lua` | 参照検索の実装 |
| converter.lua | `script/proto/converter.lua` | LSP 形式変換の詳細 |

## 11. 備考

- この遅延解決パターンにより、初期のコードレンズ表示が高速化されます
- 参照検索は重い処理なので、必要な時にのみ実行されます
- `codeLensViewReferences` オプションはクライアント（エディタ）で設定可能です
- 将来的に他のモード（テスト実行、デバッグなど）を追加できる設計になっています
