# 機能設計書 6-ドキュメントを閉じる (textDocument/didClose)

## 概要

本ドキュメントは、lua-language-serverにおけるドキュメントクローズ通知の処理を記載したものである。エディタでファイルを閉じた際に送信される`textDocument/didClose`通知を受信し、ファイルのオープン状態を解除する。

### 本機能の処理概要

**業務上の目的・背景**：LSPプロトコルでは、クライアント（エディタ）がファイルを閉じた際に`textDocument/didClose`通知を送信する。サーバーはこの通知を受けて、ファイルのオープン状態を解除し、必要に応じてメモリからファイル情報を削除する。これにより、メモリ使用量を適切に管理し、ワークスペース外のファイルの不要なキャッシュを解放できる。

**機能の利用シーン**：ユーザーがエディタでLuaファイルを閉じた時、またはファイルタブを閉じた時に自動的に呼び出される。

**主要な処理内容**：
1. didClose通知の受信とパラメータ解析
2. URIの正規化（files.getRealUri）
3. ファイルのクローズ状態への移行（files.close）
4. 非Luaファイルの場合はファイル情報を削除

**関連システム・外部連携**：LSPクライアント

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

## 関連画面

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

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| textDocument | TextDocumentIdentifier | Yes | 閉じられたドキュメントの識別子 | - |
| textDocument.uri | string | Yes | ドキュメントのURI | 有効なURI形式 |

### 入力データソース

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

## 出力仕様

### 出力データ

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

### 出力先

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

## 処理フロー

### 処理シーケンス

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

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

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

4. ファイルのクローズ処理
   └─ files.close(uri)を実行
   └─ openMap[uri]をnilに設定
   └─ file.trustedをfalseに設定

5. 非Luaファイルの削除判定
   └─ files.isLua(uri)がfalseの場合、files.remove(uri)を実行
```

### フローチャート

```mermaid
flowchart TD
    A[didClose通知受信] --> B[URI正規化]
    B --> C[デバッグログ出力]
    C --> D[files.close実行]
    D --> E{Luaファイル?}
    E -->|Yes| F[処理完了]
    E -->|No| G[files.remove実行]
    G --> F
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | オープン状態解除 | openMap[uri]をnilに設定 | 常時 |
| BR-002 | 信頼フラグ解除 | file.trustedをfalseに設定 | 常時 |
| BR-003 | 非Luaファイル削除 | 拡張子が.luaでないファイルはメモリから削除 | 常時 |
| BR-004 | 参照カウント確認 | ref <= 0かつ非オープンの場合のみ削除 | files.close内 |

### 計算ロジック

特になし

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

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

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

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

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

| 操作 | 項目（キー/値） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | uri | nil | files.closeで削除 |

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

| 操作 | 項目（キー/値） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | file.trusted | false | files.closeで更新 |
| DELETE | uri | nil | 非Luaファイルの場合、files.removeで削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | エラーケースなし | - |

### リトライ仕様

リトライ不要

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

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

## パフォーマンス要件

即座に処理すること

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

特になし

## 備考

- 非Luaファイル（.txt、.jsonなど）を開いていた場合は、閉じた際にメモリから削除される
- Luaファイルはワークスペース管理のため、閉じてもメモリに残る場合がある

---

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

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

### 推奨読解順序

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

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

**主要処理フロー**:
1. **287-297行目**: `textDocument/didClose`ハンドラの定義
2. **289行目**: `params.textDocument`からドキュメント情報を取得
3. **290行目**: `files.getRealUri(doc.uri)`でURIを正規化
4. **291行目**: `log.debug('didClose', uri)`でログ出力
5. **292行目**: `files.close(uri)`でクローズ処理
6. **293-295行目**: 非Luaファイルの場合は`files.remove(uri)`を実行

#### Step 2: ファイルクローズ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | files.lua | `script/files.lua` | files.closeの実装 |

**主要処理フロー**:
- **127-141行目**: `m.close(uri)`関数
- **130行目**: `m.openMap[uri] = nil`でオープン状態解除
- **132-134行目**: `file.trusted = false`で信頼フラグ解除
- **135行目**: `m.onWatch('close', uri)`でイベント発火
- **136-139行目**: 参照カウントとオープン状態を確認し、条件を満たせば削除

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

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

**主要処理フロー**:
- **468-484行目**: `m.remove(uri)`関数
- **475行目**: `m.removeState(file)`で構文木を削除
- **476行目**: `m.fileMap[uri] = nil`でファイル情報を削除
- **483行目**: `m.onWatch('remove', uri)`でイベント発火

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

```
m.register 'textDocument/didClose' (provider.lua:287-297)
    |
    +-- files.getRealUri(doc.uri) (files.lua:82-116)
    |       └─ URI正規化
    |
    +-- files.close(uri) (files.lua:127-141)
    |       +-- m.openMap[uri] = nil
    |       +-- file.trusted = false
    |       +-- m.onWatch('close')
    |       +-- 条件付きでm.remove(uri)
    |
    +-- files.isLua(uri) (files.lua:817-828)
    |       └─ Luaファイル判定
    |
    +-- files.remove(uri) (files.lua:468-484) [条件付き]
            +-- m.removeState(file)
            +-- m.fileMap[uri] = nil
            +-- m.onWatch('remove')
```

### データフロー図

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

TextDocumentIdentifier ───> provider.lua                      (内部状態更新)
  - uri                      m.register 'textDocument/didClose'
                             |
                             +-- files.getRealUri()
                             |     (URI正規化)
                             |
                             +-- files.close()
                             |     +-- openMap[uri] = nil
                             |     +-- file.trusted = false
                             |
                             +-- files.isLua() ?
                             |     |
                             |     +-- No: files.remove()
                             |           +-- fileMap[uri] = nil
                             |           +-- stateMap[uri] = nil
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSP通知ハンドラの登録・実行 |
| files.lua | `script/files.lua` | ソース | ファイル管理（close、remove、isLua） |
| file-uri.lua | `script/file-uri.lua` | ソース | ファイルURIの変換処理 |
