# 機能設計書 14-型定義へ移動 (textDocument/typeDefinition)

## 概要

本ドキュメントは、Lua Language Serverにおける「型定義へ移動」機能の設計を記述する。この機能は、LSPの`textDocument/typeDefinition`リクエストに対応し、変数の型定義位置へジャンプする機能を提供する。

### 本機能の処理概要

型定義へ移動機能は、カーソル位置のシンボルの「型」の定義元を検索し、その位置情報を返す機能である。通常の「定義へ移動」とは異なり、変数自体の定義ではなく、その変数の型（クラス、エイリアス、関数型など）の定義位置を返す。

**業務上の目的・背景**：Luaは動的型付け言語であるが、LuaDocアノテーションにより型情報を付与できる。開発者は変数がどのような型であるかを確認したい場合がある。この機能により、変数の型定義（@class、@alias、@enum等）に直接ジャンプでき、型システムの理解と活用が促進される。

**機能の利用シーン**：
- 変数に割り当てられているクラスの定義を確認したい場合
- 型エイリアスの実際の定義を確認したい場合
- 関数の戻り値型の定義を確認したい場合
- 列挙型（enum）の定義を確認したい場合

**主要な処理内容**：
1. カーソル位置のソースノードを特定
2. require/dofile/loadfile呼び出しの場合はファイルURIを解決
3. `vm.getDefs`で定義を取得
4. 型定義（doc.class.name、doc.alias.name、doc.type.function等）のみをフィルタ
5. 結果をソートして重複を除去
6. `@source`アノテーションがある場合は外部ファイルパスを解決

**関連システム・外部連携**：
- LSPプロトコルの`textDocument/typeDefinition`リクエストハンドラ
- ワークスペースのrequireパス解決機能
- 型推論システム（vm）

**権限による制御**：特になし

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | 型定義へ移動 | 主機能 | 変数の型定義位置へジャンプする主処理 |

## 機能種別

コードナビゲーション / 検索処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | uri | Yes | 対象ファイルのURI | 有効なファイルURI |
| offset | integer | Yes | カーソルのオフセット位置 | 正の整数 |

### 入力データソース

- 対象ファイルのAST状態（files.getState）
- ワークスペース内のファイル情報
- requireパス設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| target | parser.object | 型定義のソースノード |
| uri | uri | 型定義のファイルURI |
| source | parser.object | 参照元のソースノード |

### 出力先

LSPレスポンスとしてクライアントに返される

## 処理フロー

### 処理シーケンス

```
1. ASTを取得
   └─ files.getState(uri)でファイル状態を取得
2. カーソル位置のソースを特定
   └─ findSource(ast, offset, accept)で該当ノードを検索
   └─ convertIndex()でインデックスアクセスを親に変換
3. require/dofile/loadfile呼び出しをチェック
   └─ 文字列リテラルの場合、モジュールファイルを解決
   └─ 解決されたURIを結果に追加
4. vm.getDefsで定義を取得
   └─ ソースノードの全定義元を収集
5. 型定義のみをフィルタリング
   └─ doc.class.name、doc.alias.name を採用
   └─ doc.type.function、doc.type.array、doc.type.table を採用
   └─ function を採用
6. 結果をソート
   └─ URI順、開始位置順でソート
   └─ ネストした結果は範囲が小さい方を優先
7. jumpSourceで@source解決
   └─ 外部ファイルパスがある場合はそちらを使用
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[AST取得]
    B --> C{AST存在?}
    C -->|No| D[nil を返す]
    C -->|Yes| E[ソースノード特定]
    E --> F{ソース存在?}
    F -->|No| D
    F -->|Yes| G{require呼び出し?}
    G -->|Yes| H[モジュールファイル解決]
    H --> I[結果に追加]
    G -->|No| I
    I --> J[vm.getDefs実行]
    J --> K[各定義をループ]
    K --> L{型定義か?}
    L -->|No| K
    L -->|Yes| M[結果に追加]
    M --> N{全定義処理完了?}
    N -->|No| K
    N -->|Yes| O{結果あり?}
    O -->|No| D
    O -->|Yes| P[結果をソート]
    P --> Q[jumpSource処理]
    Q --> R[結果を返す]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 対象ノード型 | local, setlocal, getlocal等の通常シンボル + doc.type.name, doc.class.name等のアノテーション | ソース特定時 |
| BR-02 | 型定義のみ採用 | doc.class.name、doc.alias.name、doc.type.function、doc.type.array、doc.type.table、function のみ | 定義フィルタリング時 |
| BR-03 | doc.class変換 | doc.classはdoc.class.nameに変換 | フィルタリング時 |
| BR-04 | doc.alias変換 | doc.aliasはdoc.alias.nameに変換 | フィルタリング時 |
| BR-05 | doc.enum変換 | doc.enumはdoc.enum.nameに変換 | フィルタリング時 |
| BR-06 | ネスト優先 | 同一URI内でネストした結果は範囲が小さい方を優先 | ソート処理時 |

### 計算ロジック

型定義フィルタリング:
```lua
-- 型定義として採用する条件
if src.type == 'doc.class.name'
or src.type == 'doc.alias.name'
or src.type == 'doc.type.function'
or src.type == 'doc.type.array'
or src.type == 'doc.type.table'
or src.type == 'function' then
    results[#results+1] = { target = src, uri = root.uri, source = source }
end
```

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

この機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無視 | ASTが取得できない | nilを返す |
| - | 無視 | ソースノードが特定できない | nilを返す |
| - | 無視 | 型定義が見つからない | nilを返す |
| - | 無視 | 定義のルートが取得できない | その定義をスキップ |

### リトライ仕様

リトライは行わない。

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

該当なし

## パフォーマンス要件

- `vm.getDefs`は非同期処理で実行されない（typeDefinitionは同期）
- 結果のソートはO(n log n)
- ネスト除去は線形時間O(n)
- LSPの応答性を維持するためprogress表示（0.5秒以上の処理時）

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

- ワークスペース外のファイルへのアクセスは制限される

## 備考

- 「定義へ移動」とは異なり、変数の値の定義ではなく型の定義を返す
- LuaDocアノテーションがない場合は結果が空になる可能性がある
- functionは関数型の定義として扱われる

---

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

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

### 推奨読解順序

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

型定義検索の結果構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | type-definition.lua | `script/core/type-definition.lua` | 結果オブジェクト構造：{ target, uri, source } |
| 1-2 | type-definition.lua | `script/core/type-definition.lua` | accept テーブル（35-57行目）で対象ノード型を定義 |

**読解のコツ**: `accept`テーブルは定義へ移動と類似しているが、型関連のdoc.系ノードも含む。

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

メイン関数の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | type-definition.lua | `script/core/type-definition.lua` | メイン関数（108-176行目）がモジュールの戻り値 |
| 2-2 | provider.lua | `script/provider/provider.lua` | textDocument/typeDefinition ハンドラ（445-468行目） |

**主要処理フロー**:
1. **109-112行目**: AST取得、存在しなければnil
2. **114-117行目**: ソースノード特定、存在しなければnil
3. **119-133行目**: require/dofile/loadfile呼び出しチェック
4. **135行目**: vm.getDefsで定義取得
5. **137-165行目**: 型定義のフィルタリング
6. **167-173行目**: 結果のソートとjumpSource処理

#### Step 3: 定義へ移動との違いを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | type-definition.lua | `script/core/type-definition.lua` | フィルタリング条件（153-163行目） |
| 3-2 | definition.lua | `script/core/definition.lua` | 比較: 定義へ移動のフィルタリング（161-228行目） |

**主要な違い**:
- 定義へ移動: 変数/関数の定義（代入、宣言）を返す
- 型定義へ移動: 型の定義（@class、@alias等）のみを返す

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

```
provider.lua ('textDocument/typeDefinition' ハンドラ)
    │
    └─ type-definition.lua (メイン関数)
           │
           ├─ files.getState() [AST取得]
           ├─ findSource() [ソースノード特定]
           ├─ convertIndex() [インデックス変換]
           ├─ checkRequire() [require解決]
           │      └─ rpath.findUrisByRequireName()
           │      └─ workspace.findUrisByFilePath()
           ├─ vm.getDefs() [定義取得]
           ├─ sortResults() [ソート処理]
           └─ jumpSource() [@source解決]
```

### データフロー図

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

URI ─────────────────────┐
                         │
オフセット位置 ───────────┼──▶ type-definition() ──▶ results[]
                         │         │
                         │         ▼
                         │   ┌────────────────┐
                         │   │ ソースノード特定 │
                         │   └────────────────┘
                         │         │
                         │         ▼
                         │   ┌────────────────┐
                         │   │  vm.getDefs    │
                         │   └────────────────┘
                         │         │
                         │         ▼
                         │   ┌────────────────┐
                         │   │ 型定義フィルタ   │
                         │   │ (doc.class.name │
                         │   │  doc.alias.name │
                         │   │  doc.type.func  │
                         │   │  function等)    │
                         │   └────────────────┘
                         │         │
                         │         ▼
                         └─ [{ target, uri, source }]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| type-definition.lua | `script/core/type-definition.lua` | ソース | 型定義へ移動のメイン実装 |
| provider.lua | `script/provider/provider.lua` | ソース | LSPハンドラ、型定義リクエストを処理 |
| definition.lua | `script/core/definition.lua` | ソース | 参考：通常の定義へ移動 |
| find-source.lua | `script/core/find-source.lua` | ソース | カーソル位置のソースノード特定 |
| jump-source.lua | `script/core/jump-source.lua` | ソース | @sourceアノテーション解決 |
| require-path.lua | `script/workspace/require-path.lua` | ソース | requireパス解決 |
| vm/init.lua | `script/vm/init.lua` | ソース | getDefs関数を提供 |
