# 機能設計書 38-診断マネージャ

## 概要

本ドキュメントは、複数の診断プロバイダ（DScanner・DUB・dfmt）からの診断結果を統合し、LSPクライアントに配信する診断マネージャの設計を記述する。

### 本機能の処理概要

**業務上の目的・背景**：serve-dには複数の診断プロバイダ（DScanner静的解析、DUBビルド診断、dfmtリント）が存在し、それぞれが独立して診断結果を生成する。診断マネージャはこれらの結果を統合して、各ドキュメントの全診断を一括してクライアントに配信する。LSPプロトコルでは `textDocument/publishDiagnostics` 通知で全診断をまとめて送信するため、プロバイダ間の統合が必要である。

**機能の利用シーン**：各診断プロバイダ（DScanner/DUB/dfmt）がリント処理を完了した際に呼び出される。診断プロバイダは自身のスロットに結果を格納し、updateDiagnosticsを呼び出すことで全プロバイダの結果が統合・配信される。

**主要な処理内容**：
1. 3つの診断プロバイダスロットで診断結果を管理する
2. createDiagnosticsForで特定URIの診断配列への参照を取得・作成する
3. combineDiagnosticsで全プロバイダの結果をURI別に統合する
4. updateDiagnosticsで統合結果をRPC通知として送信する
5. 以前に公開されたURIで診断がなくなった場合は空の診断を送信してクリアする

**関連システム・外部連携**：LSP RPCレイヤーを通じて `textDocument/publishDiagnostics` 通知をクライアントに送信する。

**権限による制御**：特になし。各診断プロバイダの設定制御は各プロバイダ側で行われる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 34 | DScanner リント（served/doDscanner） | 補助機能 | DScanner診断結果を診断マネージャ経由でクライアントに配信 |
| 35 | dfmt リント | 補助機能 | dfmt診断結果を診断マネージャ経由でクライアントに配信 |
| 36 | DUB ビルドリント | 補助機能 | DUBビルド診断結果を診断マネージャ経由でクライアントに配信 |

## 機能種別

データ統合・配信処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| slot (createDiagnosticsFor) | int | Yes | 診断プロバイダのスロット番号（0=DScanner, 1=DUB, 2=dfmt） | 0-2の範囲 |
| uri (createDiagnosticsFor) | DocumentUri | Yes | 診断対象ドキュメントのURI | - |
| uriHint (updateDiagnostics) | string | No | 更新対象URIのヒント。空の場合は全URIを送信 | - |

### 入力データソース

各診断プロバイダ（DScanner, DUB, dfmt）からの診断結果。diagnostics[slot] 配列に PublishDiagnosticsParams[] として格納される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| textDocument/publishDiagnostics通知 | PublishDiagnosticsParams | URI別の統合診断結果 |

### 出力先

LSP RPCを通じて `textDocument/publishDiagnostics` 通知としてクライアントに送信。

## 処理フロー

### 処理シーケンス

```
1. 診断格納（各プロバイダ側）
   └─ createDiagnosticsFor!slot(uri) で特定URIの診断配列参照を取得
   └─ 取得した参照にDiagnostic[]を格納
2. 診断統合（combineDiagnostics）
   └─ 全3スロットの全診断をスキャン
   └─ URI別にDiagnostic配列を結合
   └─ combinedDiagnosticsに格納
3. 診断配信（updateDiagnostics）
   └─ combineDiagnosticsを呼び出し
   └─ 各URIの統合診断をtextDocument/publishDiagnosticsで送信
   └─ uriHintが指定されている場合はそのURIのみ送信
4. 古い診断のクリア
   └─ 前回publishedUrisに含まれていて今回含まれないURIに空の診断を送信
   └─ publishedUrisを更新
```

### フローチャート

```mermaid
flowchart TD
    A[診断プロバイダからの呼び出し] --> B[createDiagnosticsFor でスロットに格納]
    B --> C[updateDiagnostics 呼び出し]
    C --> D[combineDiagnostics]
    D --> E[全スロットの診断をURI別に統合]
    E --> F{uriHintあり?}
    F -->|Yes| G[指定URIの診断のみ送信]
    F -->|No| H[全URIの診断を送信]
    G --> I[古い診断のクリア]
    H --> I
    I --> J[publishedUris更新]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-38-01 | 3プロバイダ固定 | NumDiagnosticProviders=3（DScanner=0, DUB=1, dfmt=2） | 常時 |
| BR-38-02 | URI別統合 | 同一URIの診断は全プロバイダ分が結合される | 常時 |
| BR-38-03 | 古い診断クリア | 前回公開して今回含まれないURIには空の診断を送信 | 常時 |
| BR-38-04 | uriHintフィルタ | uriHintが指定されている場合はそのURIの診断のみ送信（パフォーマンス最適化） | uriHint指定時 |

### 計算ロジック

特になし。

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

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

本機能にデータベース操作は存在しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 範囲外スロット | static assertでコンパイル時チェック | コンパイルエラー |

### リトライ仕様

リトライは行わない。

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

トランザクション管理は不要。各プロバイダが非同期に更新するが、D言語のファイバーベースのシングルスレッドモデルにより競合は発生しない。

## パフォーマンス要件

統合処理はO(N*M)の計算量（N=プロバイダ数、M=URI数）。uriHintによるフィルタリングで不要な送信を削減する。

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

特になし。

## 備考

- diagnostics配列はモジュールレベル変数（グローバル状態）として管理されている。
- publishedUrisは前回配信したURIの一覧を保持し、次回配信時に差分検出に使用される。
- destroyUnset(publishedUris)でメモリ管理を行っている（served.utils.memoryモジュール）。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | NumDiagnosticProviders=3（9行目）、DiagnosticCollection型エイリアス（10行目）、diagnostics配列（11行目）、combinedDiagnostics（13行目）、publishedUris（14行目） |

**読解のコツ**: DiagnosticCollectionは `PublishDiagnosticsParams[]` のエイリアス。diagnosticsは `DiagnosticCollection[3]`（3プロバイダ分のスロット）で、各スロットがURIごとのPublishDiagnosticsParams配列を保持する。

#### Step 2: 主要関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | `createDiagnosticsFor`（43-51行目）- 特定スロット・URIの診断配列参照を取得・作成 |
| 2-2 | diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | `combineDiagnostics`（16-37行目）- 全プロバイダの結果をURI別に統合 |
| 2-3 | diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | `updateDiagnostics`（59-89行目）- 統合結果をRPC送信 |

**主要処理フロー**:
1. **43-51行目**: `createDiagnosticsFor` - static assertでスロット範囲チェック（45行目）、既存URIの診断を検索（46-48行目）、なければ新規作成（50行目）
2. **16-37行目**: `combineDiagnostics` - 全プロバイダの全URIをスキャンし、URI別に結合
3. **59-89行目**: `updateDiagnostics` - combineDiagnostics呼び出し（61行目）、各URIの診断を送信（62-72行目）、古い診断クリア（74-86行目）、publishedUris更新（87-88行目）

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

```
dscanner.lint / dub.lint / dfmt.lint
    |
    +-- createDiagnosticsFor!slot(uri) (diagnosticmanager.d:43)
    |       └─ diagnostics[slot] にアクセス
    |
    +-- updateDiagnostics(uri) (diagnosticmanager.d:59)
            |
            +-- combineDiagnostics() (diagnosticmanager.d:16)
            |       └─ diagnostics[0..2] → combinedDiagnostics
            |
            +-- rpc.send (textDocument/publishDiagnostics)
            |
            +-- 古い診断クリア
            |       └─ publishedUris との差分検出
            |
            +-- publishedUris 更新
```

### データフロー図

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

diagnostics[0] (DScanner)  ──▶ combineDiagnostics()          ──▶ textDocument/publishDiagnostics
diagnostics[1] (DUB)       ──▶    URI別に結合                       (各URI分)
diagnostics[2] (dfmt)      ──▶ updateDiagnostics()
                                  uriHintフィルタ
                                  古い診断クリア
                                  publishedUris更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | ソース | 診断マネージャ本体（統合・配信） |
| dscanner.d | `source/served/linters/dscanner.d` | ソース | DScanner診断プロバイダ（Slot 0） |
| dub.d | `source/served/linters/dub.d` | ソース | DUBビルド診断プロバイダ（Slot 1） |
| dfmt.d | `source/served/linters/dfmt.d` | ソース | dfmtリント診断プロバイダ（Slot 2） |
| memory.d | `source/served/utils/memory.d` | ソース | destroyUnsetメモリ管理ユーティリティ |
| types.d | `source/served/types.d` | ソース | PublishDiagnosticsParams等の型定義 |
