# 機能設計書 5-ドキュメントを開く (textDocument/didOpen)

## 概要

本ドキュメントは、lua-language-serverにおけるドキュメントオープン通知の処理を記載したものである。エディタでファイルを開いた際に送信される`textDocument/didOpen`通知を受信し、ファイル内容の読み込みとコンパイルを行う。

### 本機能の処理概要

**業務上の目的・背景**：LSPプロトコルでは、クライアント（エディタ）とサーバー間でファイルの同期を行う必要がある。`textDocument/didOpen`通知により、エディタでファイルが開かれたことをサーバーに通知し、サーバーはそのファイルの内容をメモリに読み込んで解析を開始する。これにより、コード補完、診断、定義へ移動などの言語機能が利用可能になる。

**機能の利用シーン**：ユーザーがエディタでLuaファイルを開いた時に自動的に呼び出される。新規ファイルの作成、既存ファイルの編集開始、プロジェクト内のファイル参照時などに発生する。

**主要な処理内容**：
1. didOpen通知の受信とパラメータ解析
2. URIの正規化（files.getRealUri）
3. ファイルテキストの設定（files.setText）とバージョン管理
4. ファイルのオープン状態への登録（files.open）
5. ワークスペースの準備完了待機（workspace.awaitReady）
6. ファイルのコンパイル（files.compileState）

**関連システム・外部連携**：LSPクライアント、ファイルシステム、パーサー（構文解析器）

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は直接的な画面表示を持たない |

## 機能種別

テキスト同期 / ドキュメント管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| textDocument | TextDocumentItem | Yes | 開かれたドキュメントの情報 | - |
| textDocument.uri | string | Yes | ドキュメントのURI | 有効なURI形式 |
| textDocument.languageId | string | Yes | 言語識別子（"lua"） | - |
| textDocument.version | integer | Yes | ドキュメントのバージョン番号 | 非負整数 |
| textDocument.text | string | Yes | ドキュメントの全テキスト内容 | - |

### 入力データソース

LSPクライアントからのJSON-RPC通知

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| - | - | 通知のため戻り値なし |

### 出力先

なし（内部状態の更新のみ）

## 処理フロー

### 処理シーケンス

```
1. didOpen通知の受信
   └─ JSON-RPCでクライアントから通知を受信

2. URIの正規化
   └─ files.getRealUri(doc.uri)でURIを正規化

3. デバッグログ出力
   └─ log.debug('didOpen', uri)

4. ファイルテキストの設定
   └─ files.setText(uri, text, true, callback)
   └─ callbackでバージョン番号を設定

5. ファイルのオープン状態登録
   └─ files.open(uri)でオープン状態に登録

6. ワークスペース準備完了待機
   └─ workspace.awaitReady(uri)で準備完了を待機

7. ファイルのコンパイル
   └─ files.compileState(uri)で構文解析を実行
```

### フローチャート

```mermaid
flowchart TD
    A[didOpen通知受信] --> B[URI正規化]
    B --> C[デバッグログ出力]
    C --> D[files.setText実行]
    D --> E[バージョン設定]
    E --> F[files.open実行]
    F --> G[workspace.awaitReady]
    G --> H[files.compileState]
    H --> I[処理完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 信頼フラグ | didOpenで受信したテキストはtrusted=trueとして扱う | 常時 |
| BR-002 | バージョン管理 | ファイルごとにバージョン番号を管理し、変更追跡に使用 | 常時 |
| BR-003 | オープン状態管理 | files.openMap[uri]でオープン状態を管理 | 常時 |
| BR-004 | 非同期処理 | workspace.awaitReadyとcompileStateは非同期実行 | 常時 |

### 計算ロジック

特になし

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

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

本機能はデータベースを使用しない（インメモリ処理のみ）

### テーブル別操作詳細

#### fileMap（インメモリ）

| 操作 | 項目（キー/値） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | uri -> file | ファイル情報オブジェクト | files.setTextで設定 |

#### openMap（インメモリ）

| 操作 | 項目（キー/値） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | uri -> {cache: {}} | オープン状態情報 | files.openで設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 構文エラー | Luaコードに構文エラーがある場合 | 診断情報としてクライアントに通知 |
| - | エンコーディングエラー | ファイルエンコーディングが不正 | デフォルトエンコーディングで処理 |
| - | 巨大ファイル | 10MBを超えるファイル | 警告を表示し、処理をスキップ |

### リトライ仕様

エラー時のリトライは行わない

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

トランザクション管理なし（インメモリ操作のみ）

## パフォーマンス要件

- ファイルオープン処理は即座に応答すること
- コンパイル処理は非同期で実行し、UIをブロックしないこと

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

- ファイルサイズ制限（10MB）による過大なメモリ使用の防止
- URIの正規化によるパス操作攻撃の防止

## 備考

- 非同期処理（async）として実装されている
- ワークスペース準備完了後にコンパイルを実行

---

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

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

### 推奨読解順序

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

ファイル管理のデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | files.lua | `script/files.lua` | fileクラスの定義（20-35行目）とfileMap、openMapの構造 |

**読解のコツ**: `---@class file`のアノテーションでファイルオブジェクトの構造を確認。uri、text、version、state等のフィールドが重要。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | provider.lua | `script/provider/provider.lua` | `m.register 'textDocument/didOpen'`でハンドラを登録 |

**主要処理フロー**:
1. **271-285行目**: `textDocument/didOpen`ハンドラの定義
2. **274行目**: `params.textDocument`からドキュメント情報を取得
3. **275行目**: `files.getRealUri(doc.uri)`でURIを正規化
4. **277-280行目**: `files.setText()`でテキスト設定とバージョン設定
5. **281行目**: `files.open(uri)`でオープン状態に登録
6. **282行目**: `workspace.awaitReady(uri)`で準備完了待機
7. **283行目**: `files.compileState(uri)`でコンパイル

#### Step 3: ファイル管理処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | files.lua | `script/files.lua` | files.setText、files.open、files.compileStateの実装 |

**主要処理フロー**:
- **118-125行目**: `m.open(uri)`関数 - オープン状態の登録とイベント発火
- **229-303行目**: `m.setText(uri, text, isTrust, callback)`関数 - テキスト設定処理
- **673-725行目**: `m.compileState(uri)`関数 - 構文解析処理

#### Step 4: コンパイル処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | parser/compile.lua | `script/parser/compile.lua` | Lua構文解析の実装 |

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

```
m.register 'textDocument/didOpen' (provider.lua:271-285)
    |
    +-- files.getRealUri(doc.uri) (files.lua:82-116)
    |       └─ URI正規化（Windows対応含む）
    |
    +-- files.setText(uri, text, true, callback) (files.lua:229-303)
    |       +-- pluginOnSetText() - プラグインによるテキスト変換
    |       +-- file.version = doc.version - バージョン設定
    |       +-- m.onWatch('create'/'update') - イベント発火
    |
    +-- files.open(uri) (files.lua:118-125)
    |       +-- m.openMap[uri] = {cache: {}}
    |       +-- m.onWatch('open')
    |
    +-- workspace.awaitReady(uri) (workspace.lua)
    |       └─ ワークスペース準備完了待機
    |
    +-- files.compileState(uri) (files.lua:673-725)
            +-- parser.compile() - 構文解析
            +-- parser.luadoc() - LuaDoc解析
            +-- m.onWatch('compile')
```

### データフロー図

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

TextDocumentItem ──────> provider.lua                      (内部状態更新)
  - uri                    m.register 'textDocument/didOpen'
  - languageId             |
  - version        ───────>+-- files.getRealUri()
  - text                   |     (URI正規化)
                           |
                           +-- files.setText()
                           |     +-- fileMap[uri] = file
                           |     +-- file.text = text
                           |     +-- file.version = version
                           |
                           +-- files.open()
                           |     +-- openMap[uri] = {...}
                           |
                           +-- workspace.awaitReady()
                           |     (準備完了待機)
                           |
                           +-- files.compileState()
                                 +-- parser.compile()
                                 +-- stateMap[uri] = state
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSP通知ハンドラの登録・実行 |
| files.lua | `script/files.lua` | ソース | ファイル管理（setText、open、compileState） |
| workspace.lua | `script/workspace/workspace.lua` | ソース | ワークスペース管理 |
| compile.lua | `script/parser/compile.lua` | ソース | Lua構文解析 |
| luadoc.lua | `script/parser/luadoc.lua` | ソース | LuaDocアノテーション解析 |
| file-uri.lua | `script/file-uri.lua` | ソース | ファイルURIの変換処理 |
| plugin.lua | `script/plugin.lua` | ソース | プラグインによるテキスト変換 |
