# 通知設計書 2-textDocument/didChange

## 概要

本ドキュメントは、LSP(Language Server Protocol)におけるtextDocument/didChange通知の設計仕様を記載する。クライアントからサーバーへ送信される通知であり、テキストドキュメントの内容が変更されたことをサーバーに通知する。

### 本通知の処理概要

textDocument/didChange通知は、エディタ上でClojure/ClojureScriptファイルの内容が編集された際に、クライアントからclojure-lspサーバーへ送信される通知である。この通知により、サーバーはドキュメントの最新状態を把握し、変更内容を反映した解析を非同期で実行する。

**業務上の目的・背景**：ユーザーがコードを編集する際、リアルタイムでコード補完、エラー検出、リファクタリング提案を提供するために、サーバーはドキュメントの最新状態を常に把握する必要がある。この通知により、サーバーはインクリメンタルな変更を効率的に処理し、ユーザー体験を損なうことなく解析を実行できる。

**通知の送信タイミング**：ユーザーがエディタでファイルを編集するたびに（キー入力、ペースト、削除等）、エディタ(LSPクライアント)がこの通知を送信する。変更のバッチ処理によりある程度まとめて送信されることもある。

**通知の受信者**：clojure-lspサーバープロセスがこの通知を受信する。サーバーは変更をキューに入れ、デバウンス処理（50ms）後に非同期で解析を実行する。

**通知内容の概要**：通知には、変更されたドキュメントのURI、新しいバージョン番号、および変更内容（インクリメンタルまたは全文）が含まれる。インクリメンタル変更の場合、変更範囲と新しいテキストのペアが配列で送信される。

**期待されるアクション**：サーバーは受信後、ドキュメントのテキストを更新し、変更をチャンネル経由で解析ワーカーに送信する。デバウンス処理後、clj-kondoによる静的解析が実行され、検出された問題は診断情報としてクライアントへ返送される。また、関連ファイル（参照元・参照先）の再解析も必要に応じて実行される。

## 通知種別

LSP通知 (クライアント → サーバー)

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（デバウンス処理あり、50ms） |
| 優先度 | 高（リアルタイム編集体験に直結） |
| リトライ | なし（通知のため応答なし） |

### 送信先決定ロジック

クライアントからサーバーへの一方向通知のため、送信先決定ロジックは不要。

## 通知テンプレート

### パラメータ構造

| 項目 | 内容 |
|-----|------|
| メソッド名 | textDocument/didChange |
| パラメータ型 | DidChangeTextDocumentParams |
| プロトコル | JSON-RPC 2.0 over stdio |

### パラメータ定義

```json
{
  "textDocument": {
    "uri": "file:///path/to/file.clj",
    "version": 2
  },
  "contentChanges": [
    {
      "range": {
        "start": {"line": 3, "character": 0},
        "end": {"line": 3, "character": 10}
      },
      "text": "(defn greet"
    }
  ]
}
```

### 添付ファイル

該当なし（LSP通知にファイル添付機能なし）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| textDocument.uri | ドキュメントのURI | クライアント（エディタ） | Yes |
| textDocument.version | 新しいバージョン番号 | クライアント（インクリメント） | Yes |
| contentChanges | 変更内容の配列 | クライアント（編集操作） | Yes |
| contentChanges[].range | 変更範囲（インクリメンタル時） | クライアント | No |
| contentChanges[].text | 新しいテキスト | クライアント | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | テキスト入力 | ドキュメントがオープン中 | ユーザーがキー入力した時 |
| 画面操作 | テキスト削除 | ドキュメントがオープン中 | ユーザーがBackspace/Delete押下時 |
| 画面操作 | ペースト | ドキュメントがオープン中 | ユーザーがテキストを貼り付けた時 |
| 画面操作 | Undo/Redo | ドキュメントがオープン中 | 元に戻す/やり直し操作時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ドキュメント未オープン | didOpenが送信されていない場合 |
| サーバー未接続 | LSPサーバーが起動していない/接続されていない場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ドキュメント編集] --> B[textDocument/didChange通知送信]
    B --> C[サーバーで受信]
    C --> D[URIを正規化]
    D --> E[テキスト変更を適用]
    E --> F[バージョン更新]
    F --> G[変更をチャンネルに送信]
    G --> H{デバウンス 50ms}
    H --> I[clj-kondo解析実行]
    I --> J[clj-depend解析実行]
    J --> K[組み込み診断解析]
    K --> L[カスタム診断解析]
    L --> M[診断情報公開]
    M --> N[参照ファイル通知]
    N --> O[テストツリー更新]
    O --> P[終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| db/documents | 既存ドキュメント情報の参照 | 現在のテキスト取得 |
| db/analysis | 解析結果の参照 | 差分検出用 |
| db/settings | 設定情報の参照 | notify-references-on-file-change等 |

### テーブル別参照項目詳細

#### db/documents

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| [uri :text] | 現在のテキスト | キー: uri |
| [uri :v] | 現在のバージョン | キー: uri |
| [uri :analyzed-version] | 解析済みバージョン | キー: uri |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| db/documents | UPDATE | テキストとバージョンの更新 |
| db/analysis | UPDATE | 解析結果の更新 |
| db/diagnostics | UPDATE | 診断情報の更新 |

#### db/documents更新詳細

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| ASSOC | [:documents uri :v] | version | 新バージョン番号 |
| ASSOC | [:documents uri :text] | final-text | 変更適用後のテキスト |
| ASSOC | [:documents uri :analyzed-version] | version | 解析完了後に更新 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| バージョン競合 | 古いバージョンの変更を受信 | 無視して処理をスキップ |
| テキスト適用エラー | 範囲指定が不正 | エラーログ出力 |
| 解析エラー | clj-kondoが失敗 | 診断情報として公開 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（通知のためリトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| デバウンス間隔 | 50ms |
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし（常時処理可能）

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

- ファイルパス(URI)はユーザーのローカルファイルシステム上のパスを含む
- ドキュメント内容はメモリ上に保持され、ログには記録されない
- 変更内容は一時的にチャンネルバッファに保持される

## 備考

- LSP仕様: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didChange
- この通知は応答を返さない(notification)
- デバウンス処理により、高速な連続編集時のパフォーマンスを最適化

---

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

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

### 推奨読解順序

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

まず、プログラム間で受け渡されるデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | server.clj | `cli/src/clojure_lsp/server.clj` | change-debounce-ms定義（44行目） |
| 1-2 | coercer.clj | `lib/src/lsp4clj/coercer.clj` | DidChangeTextDocumentParams構造 |

**読解のコツ**: インクリメンタル変更(range指定)と全文置換(range省略)の両方のパターンを理解すること。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | server.clj | `cli/src/clojure_lsp/server.clj` | receive-notification multimethod定義 |

**主要処理フロー**:
1. **257-258行目**: `textDocument/didChange`メソッドのdefmethod定義
2. **258行目**: `handler/did-change`への委譲、URI正規化処理

#### Step 3: ハンドラー層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | handlers.clj | `lib/src/clojure_lsp/handlers.clj` | did-change関数の定義 |

**主要処理フロー**:
- **243-244行目**: did-change関数本体
- **244行目**: f.file-management/did-changeへの委譲

#### Step 4: ファイル管理機能層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | did-change関数の実装 |
| 4-2 | file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | replace-text関数 |

**主要処理フロー**:
- **283-291行目**: did-change関数本体
- **284行目**: 現在のテキスト取得
- **285行目**: 変更をreduceで適用
- **286-288行目**: バージョンとテキストの更新
- **289-291行目**: 変更をチャンネルに送信

#### Step 5: 非同期解析処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | server.clj | `cli/src/clojure_lsp/server.clj` | spawn-async-tasks!関数 |
| 5-2 | file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | analyze-changes関数 |

**主要処理フロー**:
- **597-622行目**: spawn-async-tasks!で非同期ループを起動
- **601行目**: debounce-byでデバウンス処理
- **612-616行目**: 変更の非同期解析
- **255-281行目**: analyze-changes関数本体

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

```
jsonrpc.server/receive-notification "textDocument/didChange"
    |
    +-- server.clj (257-258行目)
           |
           +-- handler/did-change (handlers.clj 243-244行目)
                  |
                  +-- f.file-management/did-change (file_management.clj 283-291行目)
                         |
                         +-- handle-change (232-244行目) [reduce適用]
                         +-- swap! db* (286-288行目)
                         +-- async/>!! current-changes-chan (289-291行目)
                                |
                                v
                         [非同期処理 - spawn-async-tasks!]
                                |
                                +-- debounce-by (50ms)
                                +-- f.file-management/analyze-changes (255-281行目)
                                       |
                                       +-- lsp.kondo/run-kondo-on-text!
                                       +-- lsp.depend/analyze-uri!
                                       +-- f.diagnostic/publish-diagnostics!
                                       +-- notify-references
                                       +-- producer/refresh-test-tree
```

### データフロー図

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

DidChangeTextDocumentParams
    |
    +-- uri --------+
    |               |
    +-- version ----+---> normalize-doc-uri
    |               |            |
    +-- changes ----+            v
                    |     handle-change (reduce)
                    |            |
                    |            v
                    +---> swap! db* [:documents uri :v, :text]
                    |            |
                    |            v
                    +---> async/>!! current-changes-chan
                                 |
                                 v (50ms debounce)
                    +---> analyze-changes
                                 |
                                 +---> run-kondo-on-text! -> db* [:analysis uri]
                                 |
                                 +---> publish-diagnostics! -> textDocument/publishDiagnostics
                                 |
                                 +---> notify-references -> 関連ファイル再解析
                                 |
                                 +---> refresh-test-tree -> clojure/textDocument/testTree
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| server.clj | `cli/src/clojure_lsp/server.clj` | ソース | LSPサーバーのエントリーポイント、通知受信、非同期タスク管理 |
| handlers.clj | `lib/src/clojure_lsp/handlers.clj` | ソース | 各種LSPメッセージのハンドラー |
| file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | ソース | ファイル管理機能の実装、テキスト変更処理 |
| shared.clj | `lib/src/clojure_lsp/shared.clj` | ソース | debounce-by関数の定義 |
| kondo.clj | `lib/src/clojure_lsp/kondo.clj` | ソース | clj-kondo連携 |
| diagnostics.clj | `lib/src/clojure_lsp/feature/diagnostics.clj` | ソース | 診断情報の公開 |
| fixture.clj | `cli/integration-test/integration/fixture.clj` | テスト | did-change-notification定義 |
