# 機能設計書 6-プロジェクト強制ロード (served/forceLoadProjects)

## 概要

本ドキュメントは、serve-d LSPサーバーにおけるプロジェクト強制ロード (served/forceLoadProjects) 機能の設計を記述する。指定されたパスのプロジェクトを、manyProjectsの制限に関わらず強制的にロードする。

### 本機能の処理概要

serve-dはワークスペース内のサブプロジェクト数が閾値（manyProjectsThreshold、デフォルト6）を超えた場合、自動読み込みをスキップする仕組みを持つ。本機能は、スキップされたプロジェクトを後から明示的に強制ロードするためのカスタムLSPメソッドである。指定されたルートパスに対してDUB/FSWorkspaceの初期化とDCDサーバーの起動を行う。

**業務上の目的・背景**：大規模なD言語プロジェクトでは多数のサブパッケージが存在する場合がある。全てを自動ロードするとメモリ消費やCPU使用率が過大になるため、閾値を超えたプロジェクトはスキップされる。しかし、開発者が特定のサブプロジェクトを扱いたい場合に、明示的にロードできる手段が必要である。

**機能の利用シーン**：manyProjectsの閾値によりスキップされたサブプロジェクトを、エディタのUI（coded/skippedLoads通知後の「Load」ボタン等）から強制ロードする場面。

**主要な処理内容**：
1. 各ルートパスに対してワークスペースを特定
2. DUBレシピの存在チェック
3. バックエンドインスタンスの作成と登録
4. delayedProjectActivation（skipManyProjectsAction=true）でプロジェクト初期化
5. DCDサーバーの遅延起動
6. 各パスの成功/失敗をbool配列で返却

**関連システム・外部連携**：LSPクライアント（coded/skippedLoads通知の受信後に呼び出し）。バックエンドコンポーネント。

**権限による制御**：特に権限制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | サブプロジェクト読み込み確認ダイアログ | 遷移先機能 | Loadを選択した場合にプロジェクトの読み込みが続行される |
| 16 | スキップ済みプロジェクト通知 | 遷移先機能 | スキップされたプロジェクトに対してforceLoadProjectsで強制読み込みを要求できる |
| 67 | プロジェクト強制読み込み（served/forceLoadProjects） | 主機能 | 指定パスのプロジェクトをmanyProjectsの制限に関わらず強制ロード |

## 機能種別

データ連携（プロジェクトインスタンス管理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| rootPaths | string[] | Yes | 強制ロードするプロジェクトのファイルシステムパス配列 | 有効なディレクトリパス |

### 入力データソース

LSPクライアントからのJSON-RPCリクエスト（メソッド名: "served/forceLoadProjects"）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | bool[] | 各rootPathの読み込み成功/失敗を示すbool配列 |

### 出力先

LSPクライアントへのJSON-RPCレスポンス

## 処理フロー

### 処理シーケンス

```
1. Root配列の初期化
   └─ rootPaths.lengthサイズのRoot配列を確保
2. 各ルートパスに対するループ処理
   a. URIへの変換
   b. ワークスペースの特定
      └─ fallbackWorkspaceの場合: success = false
   c. DUBレシピ存在チェック（dubRecipeExists）
   d. RootSuggestion構造体の構築
   e. 進捗レポート送信
   f. バックエンドインスタンスの作成（backend.addInstance）
   g. onProjectAvailableイベント発火
   h. LazyInstanceの場合は遅延ロードコールバック設定
   i. 非Lazyの場合は即座にdelayedProjectActivation実行（skipManyProjectsAction=true）
3. DCDサーバーの遅延起動
   └─ 成功した各ルートに対してlazyStartDCDServer呼び出し
4. 結果返却
   └─ roots.map!"a.success".arrayで成功/失敗配列を返却
```

### フローチャート

```mermaid
flowchart TD
    A[forceLoadProjectsリクエスト受信] --> B[Root配列初期化]
    B --> C{rootPathsループ}
    C --> D[URI変換・ワークスペース特定]
    D --> E{fallbackWorkspace?}
    E -->|Yes| F[success = false]
    E -->|No| G[DUBレシピ存在チェック]
    G --> H[backend.addInstance]
    H --> I[delayedProjectActivation skipManyProjectsAction=true]
    I --> J{次のrootPath?}
    F --> J
    J -->|Yes| C
    J -->|No| K[DCDサーバー遅延起動ループ]
    K --> L[bool配列返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | manyProjects制限スキップ | delayedProjectActivationにskipManyProjectsAction=trueを渡し、閾値チェックをバイパスする | 常時 |
| BR-02 | ワークスペース不在時失敗 | ルートパスが既知のワークスペースに属さない場合、success=falseを設定 | workspace()がfallbackWorkspaceを返す場合 |
| BR-03 | DUBレシピ自動判定 | rootPathにdub.json/dub.sdl/package.jsonが存在するか自動判定し、RootSuggestion.useDubを設定 | 常時 |

### 計算ロジック

特になし。

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

本機能ではデータベース操作は発生しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | error | ワークスペースが見つからない場合 | success=falseを設定し、エラーログを出力して継続 |
| なし | Exception | delayedProjectActivation内でプロジェクト初期化に失敗した場合 | 例外がスローされるがループは継続 |

### リトライ仕様

リトライは行わない。ユーザーが再度forceLoadProjectsを呼ぶことで再試行可能。

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

トランザクションは存在しない。

## パフォーマンス要件

各プロジェクトのロードにはDUB初期化やDCDサーバー起動が含まれるため、パス数に比例して時間がかかる。進捗はreportProgressでクライアントに通知される。DCDサーバー起動時間はStopWatchで計測される。

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

特になし。

## 備考

- skippedRoots配列（extension.d 726行目）は、manyProjectsThresholdにより読み込みをスキップされたルートを保持し、coded/skippedLoads通知でクライアントに送信される。
- forceLoadProjectsが成功すると、skippedRoots配列から該当パスが削除される（delayedProjectActivation内、776行目）。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | extension.d | `source/served/extension.d` | RootSuggestion構造体（487-493行目）: dir（パス）とuseDub（DUBプロジェクトか否か）のフィールド |
| 1-2 | extension.d | `source/served/extension.d` | forceLoadProjects内Root構造体（636-641行目）: uri, instance, successを持つローカル構造体 |
| 1-3 | extension.d | `source/served/extension.d` | skippedRoots配列（726行目）: スキップされたルートパスの配列 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | extension.d | `source/served/extension.d` | forceLoadProjects関数（633-721行目）: @protocolMethod("served/forceLoadProjects")ハンドラ |

**主要処理フロー**:
1. **643-644行目**: Root配列の確保
2. **646-701行目**: 各rootPathのループ処理
3. **659行目**: dubRecipeExistsでDUBプロジェクト判定
4. **678行目**: backend.addInstanceでインスタンス登録
5. **697行目**: delayedProjectActivation(skipManyProjectsAction=true)
6. **704-718行目**: DCDサーバー遅延起動ループ
7. **720行目**: success配列を返却

#### Step 3: delayedProjectActivationを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | extension.d | `source/served/extension.d` | delayedProjectActivation関数（727-912行目）: プロジェクトの実際の初期化処理 |

**主要処理フロー**:
- **741-774行目**: manyProjectsActionのチェック（skipManyProjectsAction=trueならスキップ）
- **784-901行目**: DUB初期化とFSWorkspaceフォールバック
- **908行目**: onAddedProjectイベント発火

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

```
forceLoadProjects(rootPaths)  [@protocolMethod("served/forceLoadProjects")]
    |
    +-- [各rootPath]
    |       +-- workspace(rootUri)
    |       +-- dubRecipeExists(rootPath)
    |       +-- backend.addInstance(rootPath, config)
    |       +-- emitExtensionEvent!onProjectAvailable(...)
    |       +-- delayedProjectActivation(..., skipManyProjectsAction=true)
    |               +-- DubComponent / FSWorkspaceComponent 初期化
    |               +-- didLoadDubProject()
    |
    +-- [各成功root]
    |       +-- lazyStartDCDServer(instance, uri)
    |
    +-- [返却] roots.map!"a.success".array
```

### データフロー図

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

string[] rootPaths ───> forceLoadProjects()
                          |
                          +-- workspace特定
                          +-- DUBレシピチェック
                          +-- backend.addInstance()
                          +-- delayedProjectActivation() ──> DUB/FSWorkspace初期化
                          +-- lazyStartDCDServer()       ──> DCDサーバー起動
                          |
                          +-- bool[] (成功/失敗) ─────────> LSPクライアント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| extension.d | `source/served/extension.d` | ソース | forceLoadProjects、delayedProjectActivation、dubRecipeExists関数の実装 |
| types.d | `source/served/types.d` | ソース | Workspace構造体、ManyProjectsAction列挙体の定義 |
| progress.d | `source/served/utils/progress.d` | ソース | 進捗レポート送信 |
| translate.d | `source/served/utils/translate.d` | ソース | ダイアログメッセージの翻訳 |
