# 画面設計書 9-ドキュメントハイライト

## 概要

本ドキュメントは、Lua Language Serverの「ドキュメントハイライト」機能の設計を記載したものである。LSP（Language Server Protocol）の `textDocument/documentHighlight` メソッドに対応し、カーソル位置のシンボルを同一ファイル内でハイライト表示する機能を提供する。

### 本画面の処理概要

**業務上の目的・背景**：
エディタでコードを読む際、同じ変数やシンボルがファイル内のどこで使用されているかを視覚的に把握できることは、コード理解の効率化に貢献する。本機能により、カーソル位置のシンボルと同じ参照箇所が自動的にハイライトされ、読み取り・書き込みの区別も視覚化される。

**画面へのアクセス方法**：
エディタ（VS Code等）上でLuaファイルを開き、変数名・関数名などのシンボル上にカーソルを移動すると自動的にハイライトが適用される。また、キーワード（if/end/while等）上にカーソルを置くと対応するキーワードがハイライトされる。

**主要な操作・処理内容**：
1. エディタからLSPサーバーへ `textDocument/documentHighlight` リクエストを送信
2. カーソル位置からソースオブジェクトを特定
3. シンボルの種類に応じた参照検索を実行
4. 同一ファイル内の参照のみをフィルタリング
5. 読み取り（Read）・書き込み（Write）・テキスト（Text）の種別を判定
6. DocumentHighlight 形式でエディタへ返却
7. エディタがハイライト表示

**画面遷移**：
- 遷移元：エディタのコード編集画面（カーソル移動時）
- 遷移先：なし（同一画面内でのハイライト表示）
- 関連遷移：参照検索（詳細な参照一覧が必要な場合）

**権限による表示制御**：
本機能に権限による表示制御はない。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | ドキュメントハイライト (textDocument/documentHighlight) | 主機能 | カーソル位置のシンボルを同一ファイル内でハイライトする主処理 |

## 画面種別

ナビゲーション

## URL/ルーティング

LSPメソッド: `textDocument/documentHighlight`

**リクエストパラメータ**:
```json
{
  "textDocument": {
    "uri": "file:///path/to/file.lua"
  },
  "position": {
    "line": 10,
    "character": 15
  }
}
```

## 入出力項目

### 入力項目

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| textDocument.uri | string | Yes | 対象ファイルのURI |
| position.line | integer | Yes | カーソル位置の行番号（0始まり） |
| position.character | integer | Yes | カーソル位置の列番号（0始まり） |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (配列) | DocumentHighlight[] | ハイライト位置のリスト |
| [].range | Range | ハイライト位置の範囲 |
| [].kind | integer | ハイライト種別（1=Text, 2=Read, 3=Write） |

## 表示項目

### ドキュメントハイライト結果

| 表示項目 | 形式 | 説明 |
|---------|------|------|
| 読み取りハイライト | 背景色変更 | シンボルを読み取っている箇所（薄い色） |
| 書き込みハイライト | 背景色変更 | シンボルに代入している箇所（濃い色） |
| テキストハイライト | 背景色変更 | リテラル値・キーワードの一致箇所 |

## イベント仕様

### 1-ドキュメントハイライトリクエスト処理

1. エディタからLSPサーバーへ `textDocument/documentHighlight` リクエストを送信
2. `provider.lua` の `textDocument/documentHighlight` ハンドラが呼び出される
3. ワークスペースの準備状態を確認
4. `core/highlight.lua` の関数を呼び出し
5. `findSource` でカーソル位置のソースオブジェクトを特定
6. シンボルの種類に応じた参照検索を実行：
   - `local`: ローカル変数の全参照をローカルに検索
   - `getlocal`/`setlocal`: 対応するlocal宣言の参照を検索
   - `field`/`method`: 親要素の参照を `vm.getRefs` で検索
   - `getindex`/`setindex`/`tableindex`: `vm.getRefs` で検索
   - `setglobal`/`getglobal`: `vm.getRefs` で検索
   - `goto`/`label`: `vm.getRefs` で検索
   - `string`（インデックスとして使用）: 親要素の参照を検索
   - リテラル値: 同一値のリテラルのみハイライト
7. 同一ファイル内の参照のみをフィルタリング
8. ハイライト種別（Read/Write/Text）を判定
9. キーワードのハイライトも検索（if/end/while等の対応）
10. リージョンコメント（region/endregion）のハイライトも検索
11. レスポンスとしてエディタへ返却

### 2-キーワードハイライト処理

キーワード上にカーソルがある場合、対応するキーワードをハイライト：

| 構文 | ハイライト対象 |
|------|--------------|
| do...end | `do`, `end` |
| function...end | `function`, `end` |
| for...do...end | `for`, `do`, `end` |
| while...do...end | `while`, `do`, `end` |
| repeat...until | `repeat`, `until` |
| if...then...elseif...else...end | `if`, `then`, `elseif`, `else`, `end` |

### 3-ハイライト種別判定

| 対象タイプ | ハイライト種別 |
|-----------|--------------|
| getfield, getmethod, getindex | Read |
| setfield, setmethod, setindex, tablefield, tableindex | Write |
| getlocal, getglobal, goto | Read |
| setlocal, local, setglobal, label | Write |
| string, boolean, number, integer, nil | Text |
| キーワード | Text |
| region/endregion | Text |

## データベース更新仕様

本機能はデータベース操作を行わない（読み取り専用）。

## メッセージ仕様

本機能にユーザー向けメッセージはない（非同期で即座に結果を返却）。

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| ファイルが存在しない | `nil` を返却 |
| カーソル位置にシンボルがない | キーワード/リージョンのみ検索 |
| ハイライト対象がない | `nil` を返却 |

## 備考

- ローカル変数は高速なローカル検索を使用（`vm.getRefs` を使わない）
- グローバル変数とローカル変数の区別を正しく判定
- リテラル値は同じ値のリテラルのみをハイライト
- キーワードのハイライトはLua構文の対応関係を考慮
- リージョンコメント（`--region`/`--endregion`）のペアリングにも対応
- `参照検索` と異なり、同一ファイル内のみを対象とする

---

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

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

### 推奨読解順序

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

ドキュメントハイライトで使用されるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | proto/define.lua | `script/proto/define.lua` | `DocumentHighlightKind` の定義（Read=2, Write=3, Text=1） |
| 1-2 | parser/guide.lua | `script/parser/guide.lua` | `isGlobal`, `isLiteral` 関数の定義 |

**読解のコツ**: ハイライト種別の数値の意味を把握する。

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

処理の起点となるプロバイダーファイルを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | provider.lua | `script/provider/provider.lua` | LSPメソッドの登録とリクエスト処理 |

**主要処理フロー**:
1. **530-557行目**: `textDocument/documentHighlight` ハンドラの定義
2. **538行目**: `workspace.awaitReady` でワークスペース準備待ち
3. **544行目**: `core.highlight` の呼び出し
4. **550-553行目**: 結果をLSP形式に変換

#### Step 3: コアハイライトロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | highlight.lua | `script/core/highlight.lua` | ハイライト検索のメインロジック |

**主要処理フロー（highlight.lua）**:
- **9-16行目**: `eachRef` - `vm.getRefs` を使った参照検索
- **18-25行目**: `eachLocal` - ローカル変数の高速検索
- **28-58行目**: `find` - シンボル種類に応じた参照検索
- **60-131行目**: `findKeyWord` - キーワードハイライト
- **133-213行目**: `checkRegion` - リージョンコメントのハイライト
- **215-231行目**: `accept` テーブル - 処理対象ノード種類
- **244-368行目**: メイン関数 - ハイライト検索のエントリーポイント
- **274-336行目**: ハイライト種別の判定ロジック

#### Step 4: 補助モジュールを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | find-source.lua | `script/core/find-source.lua` | ソースオブジェクト検索 |
| 4-2 | vm/ref.lua | `script/vm/ref.lua` | 参照検索エンジンの詳細 |

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

```
textDocument/documentHighlight (provider.lua:530)
    │
    ├─ workspace.awaitReady(uri)
    │
    ├─ files.getState(uri)
    │
    ├─ converter.unpackPosition()
    │
    └─ core.highlight(uri, offset)
           │
           ├─ findSource(state, offset, accept)
           │
           ├─ find(source, callback)
           │      ├─ eachLocal()  [for local/getlocal/setlocal]
           │      │
           │      └─ eachRef()    [for field/method/global/etc.]
           │             └─ vm.getRefs()
           │
           ├─ ハイライト種別判定
           │      ├─ DocumentHighlightKind.Read
           │      ├─ DocumentHighlightKind.Write
           │      └─ DocumentHighlightKind.Text
           │
           ├─ findKeyWord(state, text, position, callback)
           │      └─ guide.eachSourceContain()
           │
           └─ checkRegion(ast, text, offset, callback)
```

### データフロー図

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

textDocument.uri    ───▶ files.getState(uri)     ───▶  AST State
position            ───▶ converter.unpackPosition ───▶  内部位置
                         │
                         ▼
                    findSource()                  ───▶  Source Object
                         │
                    ┌────┴────┐
                    ▼         ▼
              eachLocal()  eachRef()             ───▶  References
                    │         │
                    └────┬────┘
                         ▼
                    同一ファイルフィルタ          ───▶  Filtered
                         │
                         ▼
                    ハイライト種別判定            ───▶  Kind (Read/Write/Text)
                         │
                    ┌────┼────┐
                    ▼    ▼    ▼
              シンボル  キーワード  リージョン
                    │    │         │
                    └────┴─────────┘
                         │
                         ▼
                    結果配列構築                  ───▶  DocumentHighlight[]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSPプロバイダー・エントリーポイント |
| highlight.lua | `script/core/highlight.lua` | ソース | ハイライト検索のメインロジック |
| find-source.lua | `script/core/find-source.lua` | ソース | ソースオブジェクト検索 |
| define.lua | `script/proto/define.lua` | ソース | DocumentHighlightKind定義 |
| guide.lua | `script/parser/guide.lua` | ソース | isGlobal, isLiteral判定 |
| vm/init.lua | `script/vm/init.lua` | ソース | 参照検索エンジン |
| vm/ref.lua | `script/vm/ref.lua` | ソース | getRefs の実装 |
| converter.lua | `script/proto/converter.lua` | ソース | LSP位置変換 |
