# 機能設計書 13-定義へ移動 (textDocument/definition)

## 概要

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

### 本機能の処理概要

定義へ移動機能は、カーソル位置のシンボル（変数、関数、フィールドなど）の定義元を検索し、その位置情報を返す機能である。開発者はエディタ上でシンボルをクリックまたはショートカットキーで定義位置に即座に移動できる。

**業務上の目的・背景**：大規模なLuaプロジェクトでは、変数や関数の定義位置を手動で探すのは困難である。この機能により、コードベース内のシンボル間を素早くナビゲートでき、コード理解と開発効率が大幅に向上する。IDE/エディタの必須機能として広く期待されている。

**機能の利用シーン**：
- 変数や関数の定義箇所を確認したい場合
- require文で参照しているモジュールファイルを開きたい場合
- クラス定義やアノテーションの定義元を確認したい場合
- 外部ライブラリの関数定義を確認したい場合

**主要な処理内容**：
1. カーソル位置のソースノードを特定
2. require/dofile/loadfile呼び出しの場合はファイルURIを解決
3. `@see`アノテーションの場合はワークスペースシンボル検索
4. `vm.getDefs`で定義元を取得
5. 結果をソートして重複を除去
6. `@source`アノテーションがある場合は外部ファイルパスを解決

**関連システム・外部連携**：
- LSPプロトコルの`textDocument/definition`リクエストハンドラ
- ワークスペースのrequireパス解決機能
- ワークスペースシンボル検索機能

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

## 関連画面

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

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| 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. @seeアノテーションをチェック
   └─ ワークスペースシンボル検索で定義を検索
5. vm.getDefsで定義を取得
   └─ ソースノードの全定義元を収集
6. 各定義をフィルタリング
   └─ global型、self、doc.generic.name等を除外
   └─ リテラル値やdoc.param等を除外
7. 結果をソート
   └─ URI順、開始位置順でソート
   └─ ネストした結果は範囲が小さい方を優先
8. 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{@seeアノテーション?}
    J -->|Yes| K[ワークスペースシンボル検索]
    K --> L[結果に追加]
    J -->|No| L
    L --> M[vm.getDefs実行]
    M --> N[各定義をループ]
    N --> O{フィルタ条件}
    O -->|除外| N
    O -->|採用| P[結果に追加]
    P --> Q{全定義処理完了?}
    Q -->|No| N
    Q -->|Yes| R{結果あり?}
    R -->|No| D
    R -->|Yes| S[結果をソート]
    S --> T[jumpSource処理]
    T --> U[結果を返す]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 対象ノード型 | local, setlocal, getlocal, label, goto, field, method, setglobal, getglobal, string, boolean, number, integer, ..., doc.type.name等を対象 | ソース特定時 |
| BR-02 | global除外 | type='global'のノードは定義結果から除外 | 定義フィルタリング時 |
| BR-03 | self除外 | type='self'のノードは定義結果から除外 | 定義フィルタリング時 |
| BR-04 | リテラル除外 | function/doc.type.function/doc.type.table以外のリテラルは除外 | 定義フィルタリング時 |
| BR-05 | ネスト優先 | 同一URI内でネストした結果は範囲が小さい方を優先 | ソート処理時 |
| BR-06 | require解決 | require/dofile/loadfile呼び出しはファイルパスを解決 | require チェック時 |

### 計算ロジック

インデックスアクセスの変換:
```lua
-- string/boolean/number/integerの場合、親がsetindex/getindex/tableindexなら親を返す
if source.type == 'string' or source.type == 'boolean' or ...
    local parent = source.parent
    if parent.type == 'setindex' or parent.type == 'getindex' or parent.type == 'tableindex' then
        return parent
    end
end
```

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

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

## エラー処理

### エラーケース一覧

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

### リトライ仕様

リトライは行わない。

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

該当なし

## パフォーマンス要件

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

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

- ワークスペース外のファイルへのアクセスは制限される
- @sourceアノテーションによる外部ファイル参照は相対パスまたはURI形式で指定

## 備考

- linkSupportがある場合はLocationLink形式で返す
- ない場合はLocation形式で返す
- メタファイルの定義も対象となる

---

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

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

### 推奨読解順序

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

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

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

**読解のコツ**: `accept`テーブルはハッシュセットとして機能し、対象ノード型をtrueで定義している。

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

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

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

**主要処理フロー**:
1. **131-134行目**: AST取得、存在しなければnil
2. **136-139行目**: ソースノード特定、存在しなければnil
3. **141-155行目**: require/dofile/loadfile呼び出しチェック
4. **157行目**: @seeアノテーションチェック
5. **159行目**: vm.getDefsで定義取得
6. **161-228行目**: 各定義のフィルタリング
7. **230-235行目**: 結果のソートとjumpSource処理

#### Step 3: 補助関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | definition.lua | `script/core/definition.lua` | sortResults関数（10-34行目） |
| 3-2 | definition.lua | `script/core/definition.lua` | checkRequire関数（62-87行目） |
| 3-3 | definition.lua | `script/core/definition.lua` | convertIndex関数（89-108行目） |
| 3-4 | definition.lua | `script/core/definition.lua` | checkSee関数（110-127行目） |

**主要処理フロー**:
- **sortResults**: URI順、開始位置順でソート後、ネスト重複を除去
- **checkRequire**: 文字列がrequire/dofile/loadfileの引数かチェック
- **convertIndex**: リテラル値の場合、親のインデックスアクセスを返す
- **checkSee**: @seeアノテーションの場合、ワークスペースシンボル検索

#### Step 4: jumpSource処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | jump-source.lua | `script/core/jump-source.lua` | 全体（63行） - @sourceアノテーション解決 |

**主要処理フロー**:
- **28-58行目**: bindDocsに@sourceがあれば外部ファイルパスを解決

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

```
provider.lua ('textDocument/definition' ハンドラ)
    │
    └─ definition.lua (メイン関数)
           │
           ├─ files.getState() [AST取得]
           ├─ findSource() [ソースノード特定]
           ├─ convertIndex() [インデックス変換]
           ├─ checkRequire() [require解決]
           │      └─ rpath.findUrisByRequireName()
           │      └─ workspace.findUrisByFilePath()
           ├─ checkSee() [@see解決]
           │      └─ wssymbol() [ワークスペースシンボル検索]
           ├─ vm.getDefs() [定義取得]
           ├─ sortResults() [ソート処理]
           └─ jumpSource() [@source解決]
                  └─ furi.encode() [ファイルURI変換]
```

### データフロー図

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

URI ─────────────────────┐
                         │
オフセット位置 ───────────┼──▶ definition() ──▶ results[]
                         │         │
                         │         ▼
                         │   ┌────────────────┐
                         │   │ ソースノード特定 │
                         │   └────────────────┘
                         │         │
                         │    ┌────┴────┐
                         │    ▼         ▼
                         │ [require]  [通常]
                         │    │         │
                         │    ▼         ▼
                         │ [ファイル  [vm.getDefs]
                         │  URI解決]     │
                         │    │         ▼
                         │    │    [フィルタ]
                         │    │         │
                         │    ▼         ▼
                         │   ┌────────────────┐
                         │   │   結果マージ    │
                         │   └────────────────┘
                         │         │
                         │         ▼
                         │   ┌────────────────┐
                         │   │    ソート       │
                         │   └────────────────┘
                         │         │
                         │         ▼
                         └─ [{ target, uri, source }]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| definition.lua | `script/core/definition.lua` | ソース | 定義へ移動のメイン実装 |
| provider.lua | `script/provider/provider.lua` | ソース | LSPハンドラ、定義リクエストを処理 |
| find-source.lua | `script/core/find-source.lua` | ソース | カーソル位置のソースノード特定 |
| jump-source.lua | `script/core/jump-source.lua` | ソース | @sourceアノテーション解決 |
| workspace-symbol.lua | `script/core/workspace-symbol.lua` | ソース | ワークスペースシンボル検索 |
| require-path.lua | `script/workspace/require-path.lua` | ソース | requireパス解決 |
| vm/init.lua | `script/vm/init.lua` | ソース | getDefs関数を提供 |
| files.lua | `script/files.lua` | ソース | ファイル状態管理 |
| converter.lua | `script/proto/converter.lua` | ソース | LSP位置変換 |
