# 機能設計書 82-進捗レポート

## 概要

本ドキュメントは、serve-dの各種処理の進捗状況をクライアントに通知するユーティリティ機能について、その処理内容・データ構造・呼び出しフローを記述した機能設計書である。

### 本機能の処理概要

本機能は、serve-dの初期化・ワークスペース起動・DUBリロード・インポートパス更新などの長時間処理の進捗状況を、構造化されたログメッセージとしてLSPクライアントに通知する。進捗情報にはタイムスタンプ・進捗タイプ・ステップ番号・最大ステップ数・付加情報が含まれ、`[progress]`プレフィックス付きのフォーマット済み文字列としてRPC経由でクライアントに送信される。

**業務上の目的・背景**：serve-dはLSPサーバーとしてバックグラウンドで多数の処理を実行するため、ユーザーやクライアントが現在の処理状況を把握できる仕組みが必要である。特にグローバルスタートアップ・ワークスペース初期化・DCD起動・DUB依存関係のダウンロードなどは時間を要するため、処理のどの段階にいるかを通知することで、ユーザーの不安を軽減しデバッグを容易にする。

**機能の利用シーン**：
- serve-dの初期起動時（グローバルスタートアップ）の各初期化ステップ
- 各ワークスペースの読み込み・設定適用時
- DCDサーバーの起動時（completionStartup）
- DUBプロジェクトのリロード時（dubReload）
- インポートパスの再読み込み時（importReload）
- DUB依存関係のアップグレード時（importUpgrades）
- ワークスペース設定の変更・完了時（configLoad, configFinish）

**主要な処理内容**：
1. `ProgressType`列挙型で進捗の種別を定義する（unknown, globalStartup, configLoad, configFinish, workspaceStartup, completionStartup, dubReload, importReload, importUpgrades）
2. `reportProgress`関数でタイムスタンプ付きの進捗ログメッセージをフォーマットし、RPCログとしてクライアントに送信する
3. 進捗メッセージには起動時からの経過秒数（ミリ秒精度）、進捗タイプ、現在ステップ、最大ステップ、付加情報が含まれる
4. 条件付きオーバーロード（`bool condition`パラメータ）により、特定条件下でのみ進捗を報告できる

**関連システム・外部連携**：LSPクライアント（VS Code等）に対してRPCの`rpc.log`メソッドでログメッセージとして進捗情報を送信する。タイムスタンプの基準点は`startupTime`（`served.types`で定義されたグローバル変数）で、`initialize`ハンドラ内で設定される。

**権限による制御**：特になし。全ての進捗情報は同一の権限で送信される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | グローバルスタートアップ | 参照画面 | グローバルスタートアップの進捗状況をクライアントに通知する |
| 3 | ワークスペーススタートアップ | 参照画面 | ワークスペーススタートアップの各段階の進捗をクライアントに通知する |

## 機能種別

ログ出力 / 進捗通知

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| condition | bool | No | 進捗を報告するかどうかの条件フラグ（条件付きオーバーロード用） | true時のみ報告を実行 |
| type | ProgressType | Yes | 進捗の種別（globalStartup, configLoad, configFinish, workspaceStartup, completionStartup, dubReload, importReload, importUpgrades） | ProgressType列挙値 |
| step | size_t | Yes | 現在のステップ番号 | 0以上の整数 |
| max | size_t | Yes | 最大ステップ数 | 0以上の整数。0の場合はステップ/最大表示なし |
| args | 可変長テンプレート引数 (Args...) | No | 付加情報（通常はワークスペースURIやメッセージ文字列） | 文字列等の表示可能な型 |

### 入力データソース

- `startupTime`グローバル変数（`served.types`モジュール、`source/served/types.d` 52行目）。`initialize`ハンドラ内（`source/served/extension.d` 1350行目）で`MonoTime.currTime()`により設定される
- 各呼び出し元からの進捗ステップ情報とワークスペースURI

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 進捗ログメッセージ | string | `[progress] [NNNNNNNNN.NNN] [type] step / max: args` 形式のフォーマット済み文字列 |

出力メッセージの形式は以下の4パターン:
1. `[progress] [009.123] [globalStartup] 3 / 10: Initializing serve-d...` (step + max + args)
2. `[progress] [009.123] [globalStartup] 3: message` (step + args、maxが0の場合)
3. `[progress] [009.123] [globalStartup]: message` (args only、stepが0の場合)
4. `[progress] [009.123] [globalStartup]` (type only、argsがない場合)

### 出力先

`rpc.log`メソッドを通じてLSPクライアントにログメッセージとして送信される（`window/logMessage`通知に相当）。

## 処理フロー

### 処理シーケンス

```
1. reportProgress呼び出し
   └─ 条件付きオーバーロードの場合: conditionがfalseなら即リターン
2. 経過時間の計算
   └─ (MonoTime.currTime() - startupTime) をミリ秒単位で取得し、秒に変換
3. メッセージフォーマット
   └─ max > 0 の場合: "[progress] [time] [type] step / max: " + args
   └─ step > 0 の場合: "[progress] [time] [type] step: " + args
   └─ args有りの場合: "[progress] [time] [type]: " + args
   └─ args無しの場合: "[progress] [time] [type]"
4. rpc.logで送信
   └─ フォーマット済み文字列をLSPクライアントに送信
```

### フローチャート

```mermaid
flowchart TD
    A[reportProgress呼び出し] --> B{conditionパラメータあり?}
    B -->|Yes| C{condition == true?}
    B -->|No| D[経過時間を計算]
    C -->|Yes| D
    C -->|No| E[即リターン]
    D --> F{max > 0?}
    F -->|Yes| G["rpc.log: [progress] [time] [type] step / max: args"]
    F -->|No| H{step > 0?}
    H -->|Yes| I["rpc.log: [progress] [time] [type] step: args"]
    H -->|No| J{argsあり?}
    J -->|Yes| K["rpc.log: [progress] [type]: args"]
    J -->|No| L["rpc.log: [progress] [type]"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-82-01 | タイムスタンプ形式 | 経過時間は起動時からのミリ秒を秒に変換し、`%09.3f`形式（9桁ゼロ埋め、小数点以下3桁）で出力する | 常時 |
| BR-82-02 | 条件付き報告 | `condition`パラメータ付きオーバーロードでは、conditionがtrue時のみ進捗を報告する | 条件付きオーバーロード使用時 |
| BR-82-03 | メッセージ階層 | max > 0 > step > 0 > args有無の順で出力形式が決定される | 常時 |
| BR-82-04 | ProgressType注釈 | `@withProgress`は進捗が必ず付随する型、`@optionalProgress`は進捗が付随する可能性がある型を示す | ドキュメント用アノテーション |

### 計算ロジック

**経過時間の計算**:
```
time = (MonoTime.currTime() - startupTime).total!"msecs" / 1000.0
```
- `startupTime`は`initialize`ハンドラ内で`MonoTime.currTime()`により設定
- ミリ秒精度のモノトニック時計から経過ミリ秒を取得し、1000.0で割って秒に変換

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

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

該当なし。本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 特になし | reportProgress自体はエラーを発生させない単純なログ出力関数 |

### リトライ仕様

該当なし。ログ出力の失敗時にリトライは行わない。

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

該当なし。本機能はステートレスなログ出力処理である。

## パフォーマンス要件

- `MonoTime`を使用しているため、システムコールのオーバーヘッドは最小限
- `rpc.log`はバッファリングされたI/O出力であり、進捗報告自体が処理のボトルネックになることはない
- フォーマット文字列はコンパイル時に解決される（`format!`テンプレート使用）

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

- 進捗情報にはワークスペースURIが含まれる場合があるが、これはクライアントが既に把握している情報であるためセキュリティリスクは低い
- `rpc.log`はLSPプロトコルの`window/logMessage`通知として送信されるため、クライアント側のログレベル設定に従って表示される

## 備考

- `ProgressType`列挙型のメンバーには`@withProgress`および`@optionalProgress`のUDA（User Defined Attributes）が付与されているが、これらは現在コード上でリフレクションされておらず、ドキュメンテーション目的のアノテーションである
- `startupTime`は`__gshared`として定義されており、全スレッドで共有される
- 進捗ログのプレフィックス`[progress]`は、クライアント側でフィルタリングに使用可能

---

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

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

### 推奨読解順序

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

進捗レポートのデータ構造はProgressType列挙型が中心である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | progress.d | `source/served/utils/progress.d` | `ProgressType`列挙型の定義（15-45行目）。各メンバーのDDocコメントと`@withProgress`/`@optionalProgress`アノテーション |
| 1-2 | types.d | `source/served/types.d` | `startupTime`グローバル変数（52行目）と`rpc`エイリアス（55行目）の定義 |

**読解のコツ**: D言語のUDA（User Defined Attributes）は`@属性名`で宣言される。`@withProgress`と`@optionalProgress`は`progress.d`内で`private enum`として定義されており（9行目、12行目）、コード内で実際にリフレクションされることはなく、ドキュメンテーション目的である。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | progress.d | `source/served/utils/progress.d` | `reportProgress`関数の2つのオーバーロード（47-64行目） |

**主要処理フロー**:
1. **47-51行目**: 条件付きオーバーロード。`condition`がtrueの場合のみ、もう一方のオーバーロードを呼び出す
2. **53-64行目**: 本体のオーバーロード。起動時からの経過秒数を計算し、`rpc.log`でフォーマット済みメッセージを送信
3. **55行目**: `(MonoTime.currTime() - startupTime).total!"msecs" / 1000.0` で経過秒数を計算
4. **56-63行目**: max, step, argsの有無に応じた4パターンのメッセージフォーマット

#### Step 3: startupTimeの初期化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | extension.d | `source/served/extension.d` | `startupTime`の設定箇所（1350行目）。`initialize`ハンドラ内で`MonoTime.currTime()`を代入 |

**主要処理フロー**:
- **1350行目**: `startupTime = MonoTime.currTime();` - LSPのinitializeリクエスト処理時に基準時刻を設定

#### Step 4: 呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | extension.d | `source/served/extension.d` | globalStartup（389行目）、configLoad（67行目）、configFinish（208行目）、workspaceStartup（582行目, 664行目）、completionStartup（624行目, 711行目）、dubReload（790-792行目）、importUpgrades（847-849行目）、importReload（1036-1038行目）の各呼び出し箇所 |
| 4-2 | dub.d | `source/served/commands/dub.d` | updateImports（218行目, 226行目, 230行目）、dubRecipeSave（245行目, 254行目, 270行目, 283行目, 287行目, 308行目）、installDependency（579行目, 583行目, 585行目）、updateDependency（595行目, 600行目, 603行目）、uninstallDependency（613行目, 618行目, 620行目）の各呼び出し箇所 |

**主要処理フロー**:
- **extension.d 389行目**: `reportProgress(ProgressType.globalStartup, 0, 0, "Initializing serve-d...")` - グローバルスタートアップ開始時
- **extension.d 582行目**: `reportProgress(ProgressType.workspaceStartup, i, rootSuggestions.length, root.dir.uriFromFile)` - 各ルートの初期化進捗
- **dub.d 218行目**: `reportProgress(params.reportProgress, ProgressType.dubReload, 0, 5, instance.cwd.uriFromFile)` - DUBリロード進捗（条件付き）

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

```
extension.d (initialize)
    |
    +-- startupTime = MonoTime.currTime()  [基準時刻設定]
    |
    +-- changedConfig (configLoad)
    |       +-- reportProgress(ProgressType.configLoad, ...)
    |
    +-- configFinished
    |       +-- reportProgress(ProgressType.configFinish, ...)
    |
    +-- ensureStartedUp (globalStartup)
    |       +-- reportProgress(ProgressType.globalStartup, ...)
    |
    +-- doStartup (workspaceStartup)
    |       +-- reportProgress(ProgressType.workspaceStartup, ...)
    |       +-- reportProgress(ProgressType.completionStartup, ...)
    |
    +-- delayedProjectActivation
            +-- reportProgress(ProgressType.dubReload, ...)
            +-- reportProgress(ProgressType.importUpgrades, ...)

dub.d
    |
    +-- updateImports
    |       +-- reportProgress(condition, ProgressType.dubReload, ...)
    |       +-- reportProgress(condition, ProgressType.importReload, ...)
    |
    +-- onDidSaveDubRecipe
    |       +-- reportProgress(ProgressType.importUpgrades, ...)
    |
    +-- installDependency / updateDependency / uninstallDependency
            +-- reportProgress(ProgressType.importUpgrades, ...)
            +-- reportProgress(ProgressType.dubReload, ...)

        [全て以下に帰結]
              |
              v
    reportProgress(type, step, max, args...)
              |
              +-- (MonoTime.currTime() - startupTime) で経過時間計算
              +-- rpc.log(format!"[progress] [time] [type] step / max: " args)
```

### データフロー図

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

startupTime ────────────┐
MonoTime.currTime() ────┼─▶ [経過時間計算]
                        │    (currTime - startupTime) / 1000.0
                        │
ProgressType ───────────┤
step (size_t) ──────────┤
max (size_t) ───────────┼─▶ [フォーマット生成]
args (URI等) ───────────┘    format!"[progress] [%09.3f] [%s] %d / %d: "
                                     │
                                     v
                             rpc.log(message)
                                     │
                                     v
                         LSPクライアント (window/logMessage)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| progress.d | `source/served/utils/progress.d` | ソース | ProgressType列挙型の定義とreportProgress関数の実装（本体、64行） |
| types.d | `source/served/types.d` | ソース | startupTimeグローバル変数（52行目）とrpcエイリアス（55行目）の定義 |
| extension.d | `source/served/extension.d` | ソース | reportProgressの主要呼び出し元（globalStartup, configLoad, configFinish, workspaceStartup, completionStartup, dubReload, importUpgrades, importReload） |
| dub.d | `source/served/commands/dub.d` | ソース | reportProgressの呼び出し元（updateImports, dubRecipeSave, installDependency, updateDependency, uninstallDependency） |
