# 通知設計書 16-clojure/textDocument/testTree

## 概要

本ドキュメントは、clojure-lspにおける`clojure/textDocument/testTree`通知の設計仕様を記載したものです。この通知はサーバーからクライアントへ送信される通知であり、テストファイルの構造（テストツリー）をクライアントに送信します。

### 本通知の処理概要

`clojure/textDocument/testTree`通知は、Clojureテストファイル（deftest、testing）の階層構造をエディタに送信するclojure-lsp独自の機能です。エディタはこの情報を使用して、テストエクスプローラーUIやコードレンズでテスト構造を可視化できます。

**業務上の目的・背景**：この通知はclojure-lsp独自の拡張機能であり、テスト駆動開発（TDD）を支援します。開発者がファイル内のテスト構造を視覚的に把握し、特定のテストを素早く見つけて実行できるようにします。VSCodeやCalvaなどのエディタ拡張がこの情報を利用してテストツリーUIを構築します。

**通知の送信タイミング**：以下のイベントで送信されます：
- プロジェクト初期化完了後（全内部URIに対して）
- ドキュメントが開かれたとき（didOpen）
- ドキュメントが変更され解析完了したとき（didChange後）
- 監視対象ファイルが変更されたとき（didChangeWatchedFiles）

**通知の受信者**：エディタ（クライアント）がこの通知を受信し、テストエクスプローラーやテスト関連のUIを更新します。

**通知内容の概要**：通知には対象ドキュメントのURIとテストツリー構造が含まれます。ツリーはネームスペース→deftest→testingの階層構造を持ち、各ノードには名前、範囲（range/name-range）、種別（kind）、子要素が含まれます。

**期待されるアクション**：ユーザーはテストエクスプローラーUIでテスト構造を確認し、特定のテストを選択して実行したり、テストコードに移動したりすることが期待されます。

## 通知種別

LSP通知（サーバー→クライアント）- clojure-lsp独自拡張

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（async/thread） |
| 優先度 | 低 |
| リトライ | なし |

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

サーバーからクライアントへの一方向通知のため、送信先は接続されているエディタクライアントに固定されます。クライアントがtest-tree機能をサポートしている場合（experimental.test-tree capability）のみ送信されます。

## 通知テンプレート

### LSP通知の場合

| 項目 | 内容 |
|-----|------|
| メソッド名 | `clojure/textDocument/testTree` |
| パラメータ | PublishTestTreeParams |
| レスポンス | なし（通知のため） |

### 本文テンプレート

```json
{
  "uri": "file:///path/to/test/core_test.clj",
  "tree": {
    "name": "core-test",
    "range": {"start": {"line": 0, "character": 0}, "end": {"line": 50, "character": 0}},
    "nameRange": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 13}},
    "kind": 1,
    "children": [
      {
        "name": "my-function-test",
        "range": {"start": {"line": 5, "character": 0}, "end": {"line": 15, "character": 1}},
        "nameRange": {"start": {"line": 5, "character": 9}, "end": {"line": 5, "character": 25}},
        "kind": 2,
        "children": [
          {
            "name": "should return expected value",
            "range": {"start": {"line": 6, "character": 2}, "end": {"line": 10, "character": 3}},
            "nameRange": {"start": {"line": 6, "character": 11}, "end": {"line": 6, "character": 39}},
            "kind": 3,
            "children": []
          }
        ]
      }
    ]
  }
}
```

テストツリー種別（kind）：
- 1: namespace
- 2: deftest
- 3: testing

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| uri | ドキュメントURI | テスト対象ファイル | Yes |
| tree | テストツリー構造 | f.test-tree/tree | Yes |
| tree.name | ノード名（ns/deftest/testing名） | 解析結果 | Yes |
| tree.range | ノードのスコープ範囲 | shared/->scope-range | Yes |
| tree.nameRange | ノード名の位置範囲 | shared/->range | Yes |
| tree.kind | ノード種別（1=namespace, 2=deftest, 3=testing） | test-tree-kind-enum | Yes |
| tree.children | 子ノードの配列 | 再帰的構築 | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 初期化 | initialize完了 | experimental.test-tree capability有効 | 全内部URIに対して送信 |
| ドキュメント操作 | textDocument/didOpen | experimental.test-tree capability有効 | ファイルを開いた時 |
| ドキュメント操作 | textDocument/didChange | experimental.test-tree capability有効、解析完了後 | ファイル編集後 |
| ファイル監視 | workspace/didChangeWatchedFiles | experimental.test-tree capability有効 | 外部変更検出時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| experimental.test-tree capabilityなし | クライアントがtest-treeをサポートしない場合 |
| テストがないファイル | deftestが存在しない場合、tree関数がnilを返す |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[トリガー発生] --> B{test-tree capability?}
    B -->|No| C[送信スキップ]
    B -->|Yes| D[async/thread内で処理]
    D --> E[各URIに対してループ]
    E --> F[f.test-tree/tree呼び出し]
    F --> G{テストあり?}
    G -->|No| H[次のURIへ]
    G -->|Yes| I[conform-or-log検証]
    I --> J[send-notification実行]
    J --> H
    H --> K{全URI処理完了?}
    K -->|No| E
    K -->|Yes| L[終了]
    C --> L
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| db（アトム） | コード解析結果の参照 | インメモリ状態管理 |

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

#### db（アトム）

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| :client-capabilities :experimental :test-tree | 機能サポート確認 | 直接参照 |
| :analysis uri :var-definitions | deftest定義の抽出 | URI指定、defined-byでフィルタ |
| :analysis uri :var-usages | testing呼び出しの抽出 | URI指定、name/toでフィルタ |
| :analysis uri :namespace-definitions | ネームスペース情報 | URI指定 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | この通知では状態更新は行いません |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| conform失敗 | テストツリーデータの形式不正 | ログ出力して処理スキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回 |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし

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

テストツリー情報にはファイルパスとテスト名が含まれますが、ローカルで動作するため特別なセキュリティ考慮は不要です。

## 備考

- clojure-lsp独自の拡張機能（LSP標準仕様には含まれない）
- クライアントはcapabilities.experimental.test-treeをtrueに設定して機能を有効化
- clojure.testとcljs.testの両方のdeftestとtestingをサポート
- testingは入れ子構造をサポート

---

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

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

### 推奨読解順序

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

テストツリーのデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | clojure_coercer.clj | `cli/src/clojure_lsp/clojure_coercer.clj` | test-tree-kind-enum（9-10行目）で種別定義を確認 |
| 1-2 | clojure_coercer.clj | `cli/src/clojure_lsp/clojure_coercer.clj` | ::publish-test-tree-params spec（27-28行目）でパラメータ構造を確認 |
| 1-3 | test_tree.clj | `lib/src/clojure_lsp/feature/test_tree.clj` | tree関数（36-54行目）で返却構造を確認 |

**読解のコツ**: `:kind`は1=namespace、2=deftest、3=testingの数値enum。`:range`はスコープ全体、`:name-range`は名前部分のみ。

#### Step 2: プロトコル定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | producer.clj | `lib/src/clojure_lsp/producer.clj` | IProducerプロトコルのrefresh-test-tree定義（13行目）を確認 |

**主要処理フロー**:
- **13行目**: `(refresh-test-tree [this uris])`の2引数シグネチャ

#### Step 3: 実装層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | server.clj | `cli/src/clojure_lsp/server.clj` | ClojureLspProducerのrefresh-test-tree実装（191-201行目）を確認 |

**主要処理フロー**:
- **191行目**: refresh-test-tree関数定義
- **192行目**: async/threadで非同期処理
- **195行目**: experimental.test-tree capabilityチェック
- **198-200行目**: 各URIに対してtree関数呼び出し、conform、send-notification

#### Step 4: テストツリー構築ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | test_tree.clj | `lib/src/clojure_lsp/feature/test_tree.clj` | tree関数（36-54行目）でメイン構築ロジックを確認 |
| 4-2 | test_tree.clj | `lib/src/clojure_lsp/feature/test_tree.clj` | deftest->tree関数（28-34行目）でdeftest変換を確認 |
| 4-3 | test_tree.clj | `lib/src/clojure_lsp/feature/test_tree.clj` | ->testings-children関数（8-26行目）でtesting階層構築を確認 |

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

```
トリガー（initialize/didOpen/didChange/didChangeWatchedFiles）
    │
    └─ producer/refresh-test-tree
           │
           ├─ async/thread
           │
           ├─ capability check
           │      │
           │      └─ :client-capabilities :experimental :test-tree
           │
           └─ doseq [uri uris]
                  │
                  ├─ f.test-tree/tree
                  │      │
                  │      ├─ q/find-namespace-definition-by-uri
                  │      ├─ deftests抽出（var-definitions + defined-by filter）
                  │      ├─ testings抽出（var-usages + name/to filter）
                  │      │
                  │      └─ deftest->tree（各deftestに対して）
                  │             │
                  │             └─ ->testings-children（入れ子testing構築）
                  │
                  ├─ conform-or-log ::clojure-coercer/publish-test-tree-params
                  │
                  └─ jsonrpc.server/send-notification "clojure/textDocument/testTree"
```

### データフロー図

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

URI ──────────────▶ analysis参照
                         │
                         ├─ namespace-definitions ──▶ ルートノード構築
                         │
                         ├─ var-definitions ──▶ deftest抽出
                         │       │                    │
                         │       └─ defined-by filter  │
                         │                            │
                         └─ var-usages ──▶ testing抽出 │
                                 │              │      │
                                 └─ name/to    │      │
                                    filter     │      │
                                               ▼      ▼
                                          テストツリー構築
                                               │
                                               ▼
                              clojure/textDocument/testTree通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| server.clj | `cli/src/clojure_lsp/server.clj` | ソース | ClojureLspProducerでのrefresh-test-tree実装 |
| producer.clj | `lib/src/clojure_lsp/producer.clj` | ソース | IProducerプロトコル定義 |
| test_tree.clj | `lib/src/clojure_lsp/feature/test_tree.clj` | ソース | テストツリー構築ロジック |
| clojure_coercer.clj | `cli/src/clojure_lsp/clojure_coercer.clj` | ソース | テストツリーのspec定義 |
| handlers.clj | `lib/src/clojure_lsp/handlers.clj` | ソース | initialize時のrefresh-test-tree呼び出し |
| file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | ソース | didOpen/didChange時の呼び出し |
| test_tree_test.clj | `lib/test/clojure_lsp/feature/test_tree_test.clj` | テスト | テストツリー構築のテストケース |
