# 通知設計書 10-workspace/didChangeWorkspaceFolders

## 概要

本ドキュメントは、LSPクライアントからserve-dサーバーへ送信される `workspace/didChangeWorkspaceFolders` 通知の設計を記述する。この通知はLSP標準仕様に準拠し、ワークスペースフォルダの追加・削除をサーバーに通知するために使用される。

### 本通知の処理概要

`workspace/didChangeWorkspaceFolders` 通知は、エディタ（クライアント）でワークスペースフォルダが追加または削除された際にserve-dサーバーに送信される通知である。extension.dのdidChangeWorkspaceFoldersハンドラで処理され、削除されたフォルダについてはremoveWorkspaceで対応するワークスペースインスタンスの解放が行われ、追加されたフォルダについてはワークスペース配列への追加、設定同期、プロジェクト初期化（doStartup）が行われる。

**業務上の目的・背景**：マルチルートワークスペース機能をサポートするため、ユーザーがエディタ上でワークスペースフォルダを動的に追加・削除した際に、サーバー側のプロジェクト管理を適切に更新する必要がある。追加されたフォルダに対してはDubプロジェクトの読み込み、DCDサーバーの起動、インデックスの構築等が実行される。削除されたフォルダに対してはリソースの解放が行われる。

**通知の送信タイミング**：ユーザーがVSCode等のエディタでワークスペースフォルダを追加（File > Add Folder to Workspace）または削除した際にクライアントから送信される。

**通知の受信者**：serve-d LSPサーバーが受信する。

**通知内容の概要**：追加されたワークスペースフォルダの配列と削除されたワークスペースフォルダの配列（WorkspaceFoldersChangeEvent）で構成される。

**期待されるアクション**：サーバーが追加フォルダに対してプロジェクト初期化を実行し、削除フォルダに対してリソースを解放する。

## 通知種別

LSP標準通知（クライアント -> サーバー）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（JSON-RPC通知） |
| 優先度 | 高（ワークスペース構成変更） |
| リトライ | 無し |

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

クライアントがワークスペースフォルダを変更した際に自動的にserve-dサーバーへ送信される。

## 通知テンプレート

### LSP通知の場合

| 項目 | 内容 |
|-----|------|
| メソッド名 | `workspace/didChangeWorkspaceFolders` |
| パラメータ型 | `DidChangeWorkspaceFoldersParams` |
| 形式 | JSON-RPC 2.0 通知 |

### 本文テンプレート

```json
{
  "jsonrpc": "2.0",
  "method": "workspace/didChangeWorkspaceFolders",
  "params": {
    "event": {
      "added": [
        {
          "uri": "file:///path/to/new-folder",
          "name": "new-folder"
        }
      ],
      "removed": [
        {
          "uri": "file:///path/to/old-folder",
          "name": "old-folder"
        }
      ]
    }
  }
}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| event.added | 追加されたワークスペースフォルダの配列 | クライアントのワークスペース管理 | Yes |
| event.removed | 削除されたワークスペースフォルダの配列 | クライアントのワークスペース管理 | Yes |
| added[].uri | 追加フォルダのURI | ファイルシステム | Yes |
| added[].name | 追加フォルダの表示名 | クライアント設定 | Yes |
| removed[].uri | 削除フォルダのURI | ファイルシステム | Yes |
| removed[].name | 削除フォルダの表示名 | クライアント設定 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ユーザー操作 | ワークスペースにフォルダを追加 | 常に送信 | File > Add Folder to Workspace等 |
| ユーザー操作 | ワークスペースからフォルダを削除 | 常に送信 | フォルダの右クリック > Remove Folder等 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| サーバーがworkspaceFoldersをサポートしていない場合 | ServerCapabilities.workspace.workspaceFolders.supported=falseの場合 |

## 処理フロー

### 受信フロー

```mermaid
flowchart TD
    A[workspace/didChangeWorkspaceFolders通知受信] --> B[didChangeWorkspaceFoldersハンドラ]
    B --> C[削除フォルダの処理]
    C --> D[removeWorkspace呼び出し]
    D --> E[backendからインスタンス削除]
    E --> F[workspace.disabled = true]
    B --> G[追加フォルダの処理]
    G --> H[workspaces配列にWorkspace追加]
    H --> I[syncConfiguration呼び出し]
    I --> J[doStartup呼び出し]
    J --> K[ルートプロジェクト検出]
    K --> L[Dubプロジェクト読み込み]
    L --> M[DCDサーバー起動]
    M --> N[インデックス構築]
```

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

該当なし（インメモリ管理）

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| workspaces[] | INSERT/UPDATE | 追加されたフォルダをWorkspaceとして追加、削除されたフォルダをdisabledに設定 |
| backend(WorkspaceD) | INSERT/DELETE | 追加フォルダのインスタンス作成、削除フォルダのインスタンス削除 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ワークスペース初期化失敗 | doStartup内でDubやFSWorkspaceの読み込み失敗 | startupErrorNotificationsに記録し、次回アクセス時にshowErrorMessage |
| 未知のワークスペース削除 | removeWorkspaceで対象URIが見つからない | 空のworkspaceRootが返されスキップ |

### リトライ仕様

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

## 配信設定

### レート制限

該当なし

### 配信時間帯

制限なし

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

ワークスペースフォルダのURIと名前が送信される。ファイルパス情報が含まれるが、セキュリティリスクは低い。

## 備考

- initialize時にworkspaceFoldersCapabilities.supported=true、changeNotifications=trueが設定されている（extension.d:286行目〜289行目）
- 削除処理はremoveWorkspaceを通じてbackend.removeInstance()を呼び出し、workspace.disabled=trueに設定する
- 追加処理では各フォルダに対してsyncConfigurationで設定を同期した後、doStartupでプロジェクト全体の初期化を行う
- doStartupではrootsForProject()でサブプロジェクトも含めたルート候補を検出し、各ルートに対してバックエンドインスタンスを作成する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | protocol.d | `lsp/source/served/lsp/protocol.d` | DidChangeWorkspaceFoldersParams、WorkspaceFoldersChangeEvent、WorkspaceFolder構造体の定義 |
| 1-2 | types.d | `source/served/types.d` | Workspace構造体（270行目〜327行目）。folder、initialized、disabled、startupErrorNotifications等のフィールド |

**読解のコツ**: Workspace構造体はWorkspaceFolderを内包し、初期化状態や設定へのアクセスを提供する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | extension.d | `source/served/extension.d` | didChangeWorkspaceFolders（1101行目〜1112行目）。削除フォルダと追加フォルダの処理 |

**主要処理フロー**:
1. **1104行目〜1105行目**: params.event.removedをイテレートしremoveWorkspace呼び出し
2. **1106行目〜1111行目**: params.event.addedをイテレートし、Workspace追加、syncConfiguration、doStartup呼び出し

#### Step 3: 削除処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | extension.d | `source/served/extension.d` | removeWorkspace（933行目〜940行目）。workspaceRootFor()でルート取得後、backend.removeInstance()とworkspace.disabled=true |

#### Step 4: 追加処理（doStartup）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | extension.d | `source/served/extension.d` | doStartup（556行目〜631行目）。rootsForProjectでプロジェクトルート検出、backend.addInstanceでインスタンス作成、delayedProjectActivationでDub/FSWorkspace読み込み |

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

```
[クライアント: workspace/didChangeWorkspaceFolders送信]
    |
    v
[serverbase.d: processNotify]
    |
    +-- didChangeWorkspaceFolders(params)           [extension.d:1101]
            |
            +-- foreach (removed)
            |       +-- removeWorkspace(uri)         [extension.d:933]
            |               +-- backend.removeInstance()
            |               +-- workspace.disabled = true
            |
            +-- foreach (added)
                    +-- workspaces ~= Workspace(folder)
                    +-- syncConfiguration(uri, i, len, true)
                    +-- doStartup(uri, anyConfig)    [extension.d:556]
                            +-- rootsForProject()    [extension.d:514]
                            +-- backend.addInstance()
                            +-- delayedProjectActivation()  [extension.d:727]
                            |       +-- DubComponent初期化
                            |       +-- FSWorkspaceComponent初期化
                            +-- lazyStartDCDServer() [extension.d:1028]
```

### データフロー図

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

DidChangeWorkspaceFoldersParams -->  removeWorkspace()             -->  インスタンス削除
  event.removed[]               -->  backend.removeInstance()      -->  workspace.disabled=true
  event.added[]                 -->  workspaces配列追加            -->  Workspace作成
                                -->  syncConfiguration()           -->  設定同期
                                -->  doStartup()                   -->  プロジェクト初期化
                                -->  lazyStartDCDServer()          -->  DCDサーバー起動
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| extension.d | `source/served/extension.d` | ソース | didChangeWorkspaceFoldersハンドラ、removeWorkspace、doStartup |
| types.d | `source/served/types.d` | ソース | Workspace構造体、workspaces配列 |
| serverbase.d | `serverbase/source/served/serverbase.d` | ソース | processNotify |
| protocol.d | `lsp/source/served/lsp/protocol.d` | ソース | DidChangeWorkspaceFoldersParams等のLSPプロトコル型定義 |
