# 通知設計書 6-workspace/didChangeWatchedFiles

## 概要

本ドキュメントは、LSP(Language Server Protocol)におけるworkspace/didChangeWatchedFiles通知の設計仕様を記載する。クライアントからサーバーへ送信される通知であり、監視対象ファイルが変更されたことをサーバーに通知する。

### 本通知の処理概要

workspace/didChangeWatchedFiles通知は、ファイルシステム上で監視対象ファイルが作成・変更・削除された際に、クライアントからclojure-lspサーバーへ送信される通知である。この通知により、サーバーはエディタで開いていないファイルの変更も検知し、プロジェクト全体の解析状態を最新に保つことができる。

**業務上の目的・背景**：Clojureプロジェクトでは、依存関係のあるファイルがエディタで開かれていなくても変更されることがある（gitブランチの切り替え、外部ツールによる生成、依存ライブラリの更新など）。この通知により、サーバーはそれらの変更を検知し、コード補完、定義ジャンプ、診断情報を常に最新の状態に保つことができる。

**通知の送信タイミング**：クライアントがファイルシステムの変更を検知した時（ファイルウォッチャーによる監視）。サーバーの初期化時に登録されたglobパターン（`**/*.{clj,cljs,cljc,cljd,edn,bb,clj_kondo}`）に一致するファイルが対象となる。

**通知の受信者**：clojure-lspサーバープロセスがこの通知を受信する。サーバーはデバウンス処理（1000ms）後に非同期でファイルを再解析する。

**通知内容の概要**：通知には、変更されたファイルのURIと変更種別（作成:1、変更:2、削除:3）のリストが含まれる。

**期待されるアクション**：サーバーは受信後、変更種別に応じた処理を行う。作成・変更されたファイルはチャンネル経由で非同期解析ワーカーに送信され、デバウンス処理後にclj-kondoによる再解析が実行される。削除されたファイルは即座にDBから削除され、空の診断情報がクライアントに送信される。

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（デバウンス処理あり、1000ms） |
| 優先度 | 中（バックグラウンド同期） |
| リトライ | なし（通知のため応答なし） |

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

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

## 通知テンプレート

### パラメータ構造

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

### パラメータ定義

```json
{
  "changes": [
    {
      "uri": "file:///path/to/new_file.clj",
      "type": 1
    },
    {
      "uri": "file:///path/to/modified_file.clj",
      "type": 2
    },
    {
      "uri": "file:///path/to/deleted_file.clj",
      "type": 3
    }
  ]
}
```

### 添付ファイル

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

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| changes | 変更ファイルリスト | クライアント（ファイルウォッチャー） | Yes |
| changes[].uri | ファイルのURI | クライアント | Yes |
| changes[].type | 変更種別（1:作成, 2:変更, 3:削除） | クライアント | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ファイル操作 | ファイル作成 | globパターン一致 | 新しいClojureファイルが作成された時 |
| ファイル操作 | ファイル変更 | globパターン一致 | Clojureファイルが外部で変更された時 |
| ファイル操作 | ファイル削除 | globパターン一致 | Clojureファイルが削除された時 |
| VCS操作 | ブランチ切り替え | 複数ファイル変更 | gitブランチ切り替え時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| サーバー未接続 | LSPサーバーが起動していない/接続されていない場合 |
| 監視未登録 | サーバーがファイル監視を要求していない場合 |
| パターン不一致 | globパターンに一致しないファイル |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ファイル変更検知] --> B[workspace/didChangeWatchedFiles通知送信]
    B --> C[サーバーで受信]
    C --> D[URIを正規化]
    D --> E[パラメータをcoerce]
    E --> F[handler/did-change-watched-filesを呼び出し]
    F --> G{変更種別ごとに分類}
    G -->|作成/変更| H[URIをチャンネルに送信]
    G -->|削除| I[files-deleted処理]
    H --> J{デバウンス 1000ms}
    J --> K[analyze-watched-files!実行]
    K --> L[clj-kondo解析]
    L --> M[診断情報公開]
    M --> N[テストツリー更新]
    N --> O[関連ファイル通知]
    O --> P[終了]
    I --> Q[DBから削除]
    Q --> R[空の診断情報公開]
    R --> S[関連ファイル通知]
    S --> P
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| db/settings | source-paths-ignore-regex参照 | 無視パターンの判定 |
| db/project-root-uri | プロジェクトルート参照 | 相対パス計算 |
| db/documents | オープンドキュメントの確認 | テキスト更新判定 |

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

#### db/settings

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| [:settings :source-paths-ignore-regex] | 無視パターン | - |
| [:settings :compute-external-file-changes] | 外部ファイル変更の計算 | - |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| db/analysis | UPDATE | 解析結果の更新 |
| db/diagnostics | UPDATE | 診断情報の更新 |
| db/documents | UPDATE/DELETE | ドキュメント情報の更新/削除 |
| db/dep-graph | UPDATE | 依存グラフの更新 |

#### 作成/変更時の更新

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| UPDATE | [:analysis uri] | 新しい解析結果 | clj-kondoの結果 |
| UPDATE | [:documents uri :text] | ファイルテキスト | オープン中の場合 |

#### 削除時の更新

| 操作 | 項目（カラム名） | 備考 |
|-----|-----------------|------|
| DISSOC | [:documents uri] | ドキュメント情報削除 |
| DISSOC | [:analysis uri] | 解析結果削除 |
| DISSOC | [:diagnostics :clj-kondo uri] | 診断情報削除 |
| UPDATE | dep-graph | 依存グラフから削除 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ファイル読み込みエラー | ファイルが存在しない | ログ出力してスキップ |
| 解析エラー | clj-kondoが失敗 | 診断情報として公開 |
| URI解決エラー | 無効なファイルパス | ログ出力してスキップ |

### リトライ仕様

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

## 配信設定

### レート制限

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

### 配信時間帯

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

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

- ファイルパス(URI)はユーザーのローカルファイルシステム上のパスを含む
- ファイル内容の読み込みが発生する
- source-paths-ignore-regexで特定パスを除外可能

## 備考

- LSP仕様: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeWatchedFiles
- この通知は応答を返さない(notification)
- ファイル監視のglobパターンは`initialized`通知時にサーバーからクライアントに登録要求される
- デバウンス処理により、大量のファイル変更（ブランチ切り替え等）時のパフォーマンスを最適化

---

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

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

### 推奨読解順序

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

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

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

**読解のコツ**: 変更種別（1:created, 2:changed, 3:deleted）とその処理の違いを理解すること。

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

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

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

**主要処理フロー**:
1. **458-465行目**: `workspace/didChangeWatchedFiles`メソッドのdefmethod定義
2. **459-464行目**: URI正規化とcoerce処理
3. **465行目**: handler/did-change-watched-filesへの委譲

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

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

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | did-change-watched-files関数 |
| 4-2 | file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | analyze-watched-files!関数 |

**主要処理フロー**:
- **342-358行目**: did-change-watched-files関数本体
- **347-352行目**: 変更種別ごとの分類
- **353-354行目**: 作成/変更ファイルをチャンネルに送信
- **355-358行目**: 削除ファイルの処理
- **293-310行目**: analyze-watched-files!関数

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

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

**主要処理フロー**:
- **597-622行目**: spawn-async-tasks!で非同期ループを起動
- **602行目**: debounce-allでデバウンス処理（1000ms）
- **617-622行目**: 監視ファイル変更の非同期解析

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

```
jsonrpc.server/receive-notification "workspace/didChangeWatchedFiles"
    |
    +-- server.clj (458-465行目)
           |
           +-- conform-or-log (パラメータcoerce)
           +-- handler/did-change-watched-files (handlers.clj 251-252行目)
                  |
                  +-- f.file-management/did-change-watched-files (file_management.clj 342-358行目)
                         |
                         +-- dir-or-file-uri->analyzable-uris (325-340行目)
                         +-- [作成/変更] async/>!! watched-files-chan (354行目)
                         |        |
                         |        v (1000ms debounce)
                         |   analyze-watched-files! (293-310行目)
                         |        |
                         |        +-- lsp.kondo/run-kondo-on-paths!
                         |        +-- f.diagnostic/publish-all-diagnostics!
                         |        +-- producer/refresh-test-tree
                         |        +-- notify-references
                         |
                         +-- [削除] files-deleted (356-358行目)
                                |
                                +-- db-without-uri
                                +-- publish-empty-diagnostics!
                                +-- notify-references
```

### データフロー図

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

DidChangeWatchedFilesParams
    |
    +-- changes -----+
         |           |
         +-- uri     +---> normalize-uri
         +-- type         |
                          v
                    group-by :type
                          |
                    +-----+-----+
                    |           |
              [created/   [deleted]
               changed]        |
                    |          v
                    |     files-deleted
                    |          |
                    v          +---> db-without-uri
              watched-files-chan     |
                    |          +---> publish-empty-diagnostics!
                    v (1000ms debounce)
              analyze-watched-files!
                    |
                    +---> run-kondo-on-paths!
                    |          |
                    |          v
                    +---> db* [:analysis uri]
                    |
                    +---> publish-all-diagnostics! -> textDocument/publishDiagnostics
                    |
                    +---> refresh-test-tree -> clojure/textDocument/testTree
                    |
                    +---> notify-references -> 関連ファイル再解析
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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-all関数の定義 |
| 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-watched-files定義 |
