# 機能設計書 45-インレイヒント (textDocument/inlayHint)

## 概要

本ドキュメントは、Lua Language Serverにおけるインレイヒント機能（textDocument/inlayHint）の設計仕様を記述する。この機能は、コード中に型情報、パラメータ名、配列インデックスなどのヒントをインラインで表示する。

### 本機能の処理概要

インレイヒント機能は、エディタのコード内に追加情報（型、パラメータ名等）を半透明のテキストとして表示する。これにより、コードを読む際に変数の型やパラメータの意味をその場で確認できる。

**業務上の目的・背景**：Luaは動的型付け言語であり、コードを読むだけでは変数の型やパラメータの意味が分かりにくい場合がある。インレイヒントは、型推論結果やパラメータ名をコード内にインライン表示することで、コードの可読性と理解度を向上させる。特に、リテラル引数（数値や文字列）を渡す際にパラメータ名が表示されると、何を渡しているかが一目で分かる。

**機能の利用シーン**：
- 変数宣言時に型情報を確認したい場合
- 関数呼び出し時にパラメータ名を確認したい場合
- 配列リテラル内の要素インデックスを確認したい場合
- async関数呼び出しを視覚的に確認したい場合
- セミコロンの挿入位置を確認したい場合

**主要な処理内容**：
1. クライアントからinlayHintリクエストを受信
2. 指定範囲を内部位置に変換
3. 型ヒント（typeHint）の生成
4. パラメータ名ヒント（paramName）の生成
5. 配列インデックスヒント（arrayIndex）の生成
6. awaitヒントの生成
7. セミコロンヒントの生成
8. ヒント情報をLSP形式に変換して返却

**関連システム・外部連携**：
- 型推論エンジン（script/vm）からの型情報
- LSPクライアントとのJSON-RPC通信

**権限による制御**：設定（Lua.hint.enable, Lua.hint.paramType, Lua.hint.setType, Lua.hint.paramName, Lua.hint.arrayIndex, Lua.hint.await, Lua.hint.semicolon）により各ヒントの有効/無効を個別制御可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | インレイヒント | 主機能 | コード内に型・パラメータ名などのヒントを表示する主処理 |

## 機能種別

コードインテリジェンス / エディタ補助機能

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| textDocument.uri | string | Yes | 対象ドキュメントのURI | 有効なファイルURIであること |
| range | Range | Yes | ヒント取得対象範囲 | start/endを含む有効な範囲 |

### 入力データソース

- LSPクライアントからのJSON-RPCリクエスト
- ファイルシステム上のLuaソースコード（AST経由）
- 型推論結果

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| InlayHint[] | array | インレイヒントの配列 |
| InlayHint.label | array | ラベル部品の配列 |
| InlayHint.position | Position | 表示位置 |
| InlayHint.kind | InlayHintKind | ヒント種別（Type/Parameter/Other） |
| InlayHint.paddingLeft | boolean | 左パディングの有無 |
| InlayHint.paddingRight | boolean | 右パディングの有無 |

### 出力先

- LSPクライアントへのJSON-RPCレスポンス

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ textDocument/inlayHintメッセージを受信

2. 設定確認
   └─ Lua.hint.enableがfalseならnull返却

3. ドキュメント状態取得
   └─ files.getState(uri)

4. 範囲変換
   └─ converter.unpackRange()

5. ヒント生成
   └─ core/hint.lua呼び出し
      ├─ typeHint(): 型ヒント
      ├─ paramName(): パラメータ名ヒント
      ├─ awaitHint(): awaitヒント
      ├─ arrayIndex(): 配列インデックスヒント
      └─ semicolonHint(): セミコロンヒント

6. LSP形式変換
   └─ 各ヒントをInlayHint形式に変換

7. レスポンス返却
   └─ InlayHint配列を返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{Lua.hint.enable?}
    B -->|false| C[null返却]
    B -->|true| D[workspace.awaitReady]
    D --> E{ドキュメント状態取得}
    E -->|失敗| C
    E -->|成功| F[converter.unpackRange]
    F --> G[core/hint.lua呼び出し]
    G --> H[typeHint]
    H --> I[paramName]
    I --> J[awaitHint]
    J --> K[arrayIndex]
    K --> L[semicolonHint]
    L --> M[InlayHint配列生成]
    M --> N[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-45-01 | ヒント無効化 | Lua.hint.enable=falseで全ヒント無効 | 設定値 |
| BR-45-02 | アンダースコアスキップ | 変数名が'_'の場合は型ヒントをスキップ | 型ヒント |
| BR-45-03 | リテラル値スキップ | 値がリテラルの場合は型ヒントをスキップ | 型ヒント |
| BR-45-04 | パラメータヒントモード | Disable/Literal/Allの3モード | パラメータ名ヒント |
| BR-45-05 | 同名スキップ | パラメータ名と引数名が同じ場合はスキップ | パラメータ名ヒント |
| BR-45-06 | 配列インデックスモード | Disable/Auto/Enableの3モード | 配列インデックス |
| BR-45-07 | セミコロンモード | Disable/SameLine/Allの3モード | セミコロンヒント |
| BR-45-08 | async呼び出し表示 | vm.isAsyncCall()=trueの場合に'await 'を表示 | awaitヒント |

### 計算ロジック

型ヒント表示位置:
```lua
results[#results+1] = {
    text   = ': ' .. view,
    offset = src.finish,  -- 変数名の直後
    kind   = define.InlayHintKind.Type,
    where  = 'right',
}
```

パラメータ名ヒント表示位置:
```lua
results[#results+1] = {
    text    = param[1] .. ':',
    offset  = arg.start,  -- 引数の直前
    kind    = define.InlayHintKind.Parameter,
    where   = 'left',
}
```

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

### 操作別データベース影響一覧

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | null返却 | Lua.hint.enable=false | クライアントはヒントを表示しない |
| - | null返却 | ドキュメント状態が取得できない | クライアントはヒントを表示しない |

### リトライ仕様

リトライは行わない。

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

該当なし（メモリ内処理のみ）

## パフォーマンス要件

- await.delay()による非同期処理で応答性を確保
- 範囲指定により処理対象を限定

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

- 特になし

## 備考

- InlayHintKindは0:Other, 1:Type, 2:Parameterの3種類
- resolveProviderはtrueだが、現在resolveは元のヒントをそのまま返却

---

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

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

### 推奨読解順序

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

InlayHintKindの定義を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | define.lua | `script/proto/define.lua` | InlayHintKind定義（191-195行目） |

**読解のコツ**: Other=0, Type=1, Parameter=2の3種類。

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

LSPリクエストハンドラを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | provider.lua | `script/provider/provider.lua` | inlayHintの登録（1407-1454行目） |

**主要処理フロー**:
1. **1408-1411行目**: capability定義（resolveProvider: true）
2. **1415-1418行目**: Lua.hint.enable確認
3. **1419-1426行目**: 範囲変換とcore/hint.lua呼び出し
4. **1428-1451行目**: InlayHint配列への変換

#### Step 3: コアロジックを理解する

各種ヒント生成ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | hint.lua | `script/core/hint.lua` | 各ヒント生成関数を理解 |

**主要処理フロー**:
- **11-69行目**: typeHint() - 型ヒント生成
- **71-86行目**: getParams() - 関数パラメータ取得
- **88-98行目**: hasLiteralArgInCall() - リテラル引数チェック
- **101-160行目**: paramName() - パラメータ名ヒント生成
- **163-231行目**: arrayIndex() - 配列インデックスヒント生成
- **234-260行目**: awaitHint() - awaitヒント生成
- **262-329行目**: semicolonHint() - セミコロンヒント生成

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

```
provider.lua (m.register 'textDocument/inlayHint')
    │
    ├─ config.get(uri, 'Lua.hint.enable')
    │      └─ 有効/無効チェック
    │
    ├─ workspace.awaitReady(uri)
    │
    ├─ files.getState(uri)
    │
    ├─ converter.unpackRange(state, params.range)
    │
    └─ core/hint.lua(uri, start, finish)
           │
           ├─ typeHint(uri, results, start, finish)
           │      ├─ guide.eachSourceBetween()
           │      ├─ vm.getInfer(source):view(uri)
           │      └─ config.get(uri, 'Lua.hint.paramType/setType')
           │
           ├─ paramName(uri, results, start, finish)
           │      ├─ guide.eachSourceBetween() type='call'
           │      ├─ vm.getDefs(source.node) → 関数定義取得
           │      ├─ getParams(def) → パラメータリスト取得
           │      └─ config.get(uri, 'Lua.hint.paramName')
           │
           ├─ awaitHint(uri, results, start, finish)
           │      ├─ guide.eachSourceBetween() type='call'
           │      ├─ vm.isAsyncCall(source)
           │      └─ config.get(uri, 'Lua.hint.await')
           │
           ├─ arrayIndex(uri, results, start, finish)
           │      ├─ guide.eachSourceType() type='table'
           │      └─ config.get(uri, 'Lua.hint.arrayIndex')
           │
           └─ semicolonHint(uri, results, start, finish)
                  ├─ guide.eachSourceTypes() blockTypes
                  └─ config.get(uri, 'Lua.hint.semicolon')
```

### データフロー図

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

LSPリクエスト             provider.lua
  ├─ uri                  ├─ config.get('Lua.hint.enable')
  └─ range        ───▶    ├─ files.getState()
                          └─ converter.unpackRange()
                                    │
                                    ▼
                            hint.lua
                    ┌───────┼───────┬───────┬───────┐
                    ▼       ▼       ▼       ▼       ▼
               typeHint paramName await  array   semicolon
                    │       │       │       │       │
                    └───────┴───────┴───────┴───────┘
                                    │
                                    ▼
                          results[] 内部形式
                                    │
                                    ▼
                          provider.lua
                          InlayHint[]変換
                                    │
                                    ▼                  InlayHint[]
                          LSPレスポンス ────────────▶  ├─ label
                                                       ├─ position
                                                       ├─ kind
                                                       └─ padding
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSPリクエストハンドラの登録・実行 |
| hint.lua | `script/core/hint.lua` | ソース | 各種ヒント生成のコアロジック |
| define.lua | `script/proto/define.lua` | ソース | InlayHintKindの定義 |
| converter.lua | `script/proto/converter.lua` | ソース | 位置・範囲変換 |
| files.lua | `script/files.lua` | ソース | ファイル管理・状態取得 |
| guide.lua | `script/parser/guide.lua` | ソース | AST走査ユーティリティ |
| vm/infer.lua | `script/vm/infer.lua` | ソース | 型推論（view取得） |
| vm.lua | `script/vm.lua` | ソース | isAsyncCall判定 |
| substring.lua | `script/core/substring.lua` | ソース | セミコロンヒント用テキスト取得 |
