# 機能設計書 82-プロトコル定義

## 概要

本ドキュメントは、lua-language-serverにおけるLSPプロトコル定義機能について記載する。この機能は、Language Server Protocol (LSP) で使用される各種定数、型定義、メッセージ処理、およびプロトコル通信の管理を担当する。

### 本機能の処理概要

**業務上の目的・背景**：Language Server Protocol (LSP) では、クライアントとサーバー間の通信で使用される多数の定数（診断レベル、シンボル種別、補完アイテム種別など）やエラーコードが規定されている。本機能は、これらの定義を一元管理し、LSPサーバー全体で統一的に利用できるようにする。また、プロトコルメッセージの送受信、リクエスト・レスポンスの管理、および位置情報の変換処理を提供する。

**機能の利用シーン**：LSPサーバーのすべての機能で利用される。コード補完時のCompletionItemKind、診断時のDiagnosticSeverity、シンボル検索時のSymbolKind、セマンティックトークン時のTokenTypesなど、LSPメッセージの作成・解析時に参照される。

**主要な処理内容**：
1. LSP定数の定義（DiagnosticSeverity, CompletionItemKind, SymbolKind等）
2. エラーコードの定義（JSON-RPC標準エラーコード、LSP固有エラーコード）
3. 診断タイプとデフォルト設定の管理
4. プロトコルメッセージの送受信処理
5. 位置・範囲情報の変換（内部表現 ⇔ LSP表現）
6. リクエスト・レスポンスの対応付け管理

**関連システム・外部連携**：JSON-RPC通信モジュール（jsonrpc.lua）を使用してクライアントと通信する。診断モジュール（diagnostic.lua）と連携して診断設定を管理する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面を持たず、LSP通信の基盤として動作 |

## 機能種別

データ定義 / 通信プロトコル管理

## 入力仕様

### 入力パラメータ

#### proto.on関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| method | string | Yes | 登録するLSPメソッド名 | 文字列であること |
| callback | function | Yes | メソッドに対応するコールバック関数 | 関数であること |

#### proto.notify関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | 通知メソッド名 | 文字列であること |
| params | table | No | 通知パラメータ | - |

#### proto.awaitRequest関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | リクエストメソッド名 | 文字列であること |
| params | table | No | リクエストパラメータ | - |

#### converter.packPosition関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| state | parser.state | Yes | パーサー状態 | - |
| pos | integer | Yes | 内部位置表現 | 整数であること |

### 入力データソース

- LSPクライアントからのJSON-RPCメッセージ
- 内部モジュールからの位置情報

## 出力仕様

### 出力データ

#### 定数定義（define.lua）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| DiagnosticSeverity | table | 診断レベル定義（Error=1, Warning=2, Information=3, Hint=4） |
| CompletionItemKind | table | 補完アイテム種別（Text=1, Method=2, Function=3, ...） |
| SymbolKind | table | シンボル種別（File=1, Module=2, Namespace=3, ...） |
| ErrorCodes | table | エラーコード定義（ParseError=-32700, MethodNotFound=-32601, ...） |
| TokenTypes | table | セマンティックトークンタイプ |
| TokenModifiers | table | セマンティックトークン修飾子 |

#### 位置変換（converter.lua）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| position | table | LSP位置形式 {line, character} |
| range | table | LSP範囲形式 {start, end} |
| location | table | LSPロケーション形式 {uri, range} |

### 出力先

- LSPクライアントへのJSON-RPCメッセージ
- 内部モジュールへの定数値・変換結果

## 処理フロー

### 処理シーケンス

```
プロトコル通信処理:
1. メソッドハンドラーの登録
   └─ proto.on(method, callback)
2. メッセージ受信・ディスパッチ
   └─ proto.doMethod(proto)
3. メソッドキューの処理
   └─ proto.applyMethodQueue()
4. キャンセル済みリクエストの除外
   └─ $/cancelRequestをチェック
5. 対応するハンドラーの呼び出し
   └─ await.call()で非同期実行
6. レスポンス送信
   └─ proto.response(id, res)

位置変換処理:
1. 内部位置からLSP位置への変換
   └─ converter.packPosition(state, pos)
2. UTF-16エンコーディング対応
   └─ encoder.len()でオフセット計算
3. Diffed位置の対応
   └─ files.diffedOffsetBack()で元位置を取得
```

### フローチャート

```mermaid
flowchart TD
    subgraph receive["メッセージ受信"]
        R1[JSON-RPCデコード] --> R2[doMethod呼び出し]
        R2 --> R3[キューに追加]
        R3 --> R4[applyMethodQueue]
    end

    subgraph dispatch["メソッドディスパッチ"]
        R4 --> D1{キャンセル済み?}
        D1 -->|Yes| D2[スキップ]
        D1 -->|No| D3[abilityからハンドラー取得]
        D3 --> D4{ハンドラーあり?}
        D4 -->|No| D5[MethodNotFoundエラー]
        D4 -->|Yes| D6[await.callで実行]
        D6 --> D7{成功?}
        D7 -->|Yes| D8[response送信]
        D7 -->|No| D9[responseErr送信]
    end

    subgraph convert["位置変換"]
        C1[内部位置] --> C2{diffed?}
        C2 -->|Yes| C3[diffedPackPosition]
        C2 -->|No| C4[rawPackPosition]
        C3 --> C5[行・列計算]
        C4 --> C5
        C5 --> C6[UTF-16オフセット変換]
        C6 --> C7[LSP position返却]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-82-01 | $/メソッドはオプショナル | $/で始まるメソッドは未対応でもエラーにしない | メソッド名が$/で始まる場合 |
| BR-82-02 | メソッドキュー処理 | メッセージはキューに入れ、まとめて処理する | 全ての受信メッセージ |
| BR-82-03 | キャンセル処理 | $/cancelRequest受信後の対象リクエストはスキップ | キャンセル対象のリクエスト |
| BR-82-04 | UTF-16位置変換 | 位置情報はUTF-16オフセットで計算する | 位置変換処理 |
| BR-82-05 | Diffed対応 | 差分適用済みファイルは元位置に変換する | diffedフラグがtrue |

### 計算ロジック

位置変換の計算:
- 内部位置（1-indexed, バイトオフセット）→ LSP位置（0-indexed, UTF-16オフセット）
- `row, col = guide.rowColOf(pos)`
- `col = encoder.len(offsetEncoding, text, lineOffset, lineOffset + col - 1)`

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| -32700 | ParseError | JSON解析エラー | 不正なメッセージとして処理 |
| -32600 | InvalidRequest | 不正なリクエスト形式 | エラーレスポンスを返却 |
| -32601 | MethodNotFound | 未定義のメソッド | エラーレスポンスを返却 |
| -32602 | InvalidParams | 不正なパラメータ | エラーレスポンスを返却 |
| -32603 | InternalError | 内部エラー | エラーレスポンスを返却 |
| -32800 | RequestCancelled | リクエストがキャンセルされた | 処理を中断 |
| -32801 | ContentModified | コンテンツが変更された | 処理を中断 |

### リトライ仕様

リトライは行わない。エラー発生時はエラーレスポンスを返却する。

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

本機能はトランザクションを使用しない。

## パフォーマンス要件

- メッセージ処理は0.5秒以内に完了することが望ましい
- 0.5秒を超える処理はログに警告を出力
- 位置変換は高頻度で呼び出されるため、計算コストを最小化

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

- テキストドキュメントの内容はログに記録しない（secretOptionで"***"に置換）
- 信頼されたクライアントからの接続を前提とする

## 備考

本機能は複数のサブモジュールで構成される：
- `proto/proto.lua`: プロトコル通信管理
- `proto/define.lua`: LSP定数定義
- `proto/converter.lua`: 位置・範囲変換
- `proto/diagnostic.lua`: 診断設定管理

---

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

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

### 推奨読解順序

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

LSPで使用される定数定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | define.lua | `script/proto/define.lua` | LSP定数の定義全体 |

**読解のコツ**: LSP仕様書と対照しながら読むと、各定数の意味が理解できる。

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

プロトコル管理モジュールの主要関数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | proto.lua | `script/proto/proto.lua` | 主要関数とモジュール構造 |

**主要処理フロー**:
1. **47-49行目**: on関数 - メソッドハンドラーの登録
2. **51-59行目**: send関数 - メッセージ送信
3. **96-101行目**: notify関数 - 通知送信
4. **104-123行目**: awaitRequest関数 - リクエスト送信と応答待機
5. **161-205行目**: applyMethod関数 - メソッド実行

#### Step 3: メッセージ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | proto.lua | `script/proto/proto.lua` | applyMethod, applyMethodQueue |

**主要処理フロー**:
- **207-221行目**: applyMethodQueue - キューからメッセージを取り出し処理
- **223-229行目**: doMethod - メッセージをキューに追加
- **241-255行目**: doResponse - レスポンス処理

#### Step 4: 位置変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | converter.lua | `script/proto/converter.lua` | 位置・範囲変換処理 |

**主要処理フロー**:
- **25-46行目**: rawPackPosition - 通常の位置変換
- **51-68行目**: diffedPackPosition - Diffed位置の変換
- **73-79行目**: packPosition - 振り分け処理
- **84-98行目**: rawUnpackPosition - LSP位置から内部位置へ
- **103-119行目**: diffedUnpackPosition - Diffed対応の逆変換

#### Step 5: 診断設定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | diagnostic.lua | `script/proto/diagnostic.lua` | 診断タイプの登録と設定 |

**主要処理フロー**:
- **25-39行目**: register関数 - 診断タイプの登録
- **41-228行目**: 各種診断タイプの定義（グループ、重要度、ステータス）
- **231-246行目**: getDefaultSeverity/getDefaultStatus - デフォルト設定取得

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

```
provider.lua (LSPハンドラー)
    │
    ├─ proto.on(method, callback)
    │      └─ ability[method] = callback
    │
    ├─ proto.doMethod(proto)
    │      └─ methodQueue追加
    │           └─ timer.wait(0, applyMethodQueue)
    │
    └─ proto.applyMethod(proto)
           ├─ getMethodName(proto)
           ├─ ability[method](params, id)
           │      └─ await.call()
           ├─ proto.response(id, res)
           │      └─ jsonrpc.encode()
           └─ proto.responseErr(id, code, message)

converter.packPosition(state, pos)
    ├─ guide.rowColOf(pos)
    ├─ encoder.len(offsetEncoding, text, start, finish)
    └─ diffedPackPosition() [if diffed]
           └─ files.diffedOffsetBack()
```

### データフロー図

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

クライアントメッセージ ───▶ jsonrpc.decode ───▶ proto.doMethod ───▶ キュー
                                                       │
キュー ───▶ applyMethodQueue ───▶ applyMethod ───▶ ハンドラー実行
                                      │
                              ┌───────┴───────┐
                              ▼               ▼
                      proto.response    proto.responseErr
                              │               │
                              └───────┬───────┘
                                      ▼
                              jsonrpc.encode ───▶ クライアント

内部位置 ───▶ packPosition ───▶ {line, character} ───▶ LSPメッセージ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| proto.lua | `script/proto/proto.lua` | ソース | プロトコル通信管理 |
| define.lua | `script/proto/define.lua` | ソース | LSP定数定義 |
| converter.lua | `script/proto/converter.lua` | ソース | 位置・範囲変換 |
| diagnostic.lua | `script/proto/diagnostic.lua` | ソース | 診断設定管理 |
| init.lua | `script/proto/init.lua` | ソース | モジュールエントリーポイント |
| jsonrpc.lua | `script/jsonrpc.lua` | ソース | JSON-RPCエンコード・デコード |
| await.lua | `script/await.lua` | ソース | 非同期処理 |
