# 機能設計書 70-ワークスペース管理

## 概要

本ドキュメントは、Lua Language Serverにおける「ワークスペース管理」機能の設計を記述する。本機能は、プロジェクトのルートディレクトリとライブラリパスを管理し、ファイルのスキャン、ロード、監視を統括する。

### 本機能の処理概要

ワークスペース管理機能は、Language Serverが解析対象とするディレクトリの範囲を定義し、その中のファイルを効率的にスキャンしてプリロードする。スコープ（scope）という概念でワークスペースを階層的に管理し、設定やキャッシュをスコープ単位で保持する。

**業務上の目的・背景**：開発者が複数のフォルダをワークスペースとして開いたり、外部ライブラリを参照したりする場合、それぞれの範囲を適切に管理する必要がある。ワークスペース管理は、これらの範囲を定義し、ファイルのスキャンとキャッシュを統括する。

**機能の利用シーン**：
- VSCodeでフォルダを開いた時のワークスペース初期化
- マルチルートワークスペースの管理
- 外部ライブラリパスの追加と管理
- .gitignoreに基づくファイル除外
- ファイル変更時のワークスペース再スキャン

**主要な処理内容**：
1. ワークスペースの作成と初期化
2. スコープ（folder/fallback/override）の管理
3. ファイルのスキャンとプリロード
4. 除外パターン（.gitignore等）の適用
5. ライブラリパスの管理
6. ファイル変更イベントの処理
7. 設定変更時のリロード

**関連システム・外部連携**：
- scope.lua：スコープ管理
- loading.lua：ファイルローディング
- filewatch.lua：ファイル変更監視
- files.lua：ファイルキャッシュ
- config.lua：設定管理
- glob.lua：ファイルパターンマッチング

**権限による制御**：ルートディレクトリとして `/` や `$HOME` を開くことは禁止される（FORCE_ACCEPT_WORKSPACEで上書き可能）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はバックグラウンドで動作し、専用の画面は存在しない |

## 機能種別

システム管理 / ファイル管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | ワークスペースのURI | 有効なfile://形式 |
| folderName | string | No | フォルダ名 | - |

### 入力データソース

- LSPプロトコル経由のワークスペースフォルダ情報
- 設定ファイル（Lua.workspace.library等）
- .gitignore、.gitmodulesファイル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| scope | object | スコープオブジェクト |
| scope.uri | string | スコープのURI |
| scope.type | string | 'folder', 'fallback', 'override' |
| ready | boolean | ロード完了フラグ |
| cachedUris | table | キャッシュされたURI一覧 |

### 出力先

- m.folders：ワークスペースフォルダ一覧
- scope._data：スコープ固有データ

## 処理フロー

### 処理シーケンス

```
1. ワークスペース作成
   ├─ m.create(uri, folderName)でスコープ作成
   ├─ 不正なルートパスをチェック
   └─ m.foldersに追加
2. プリロード開始
   ├─ m.awaitPreload(scp)を非同期実行
   ├─ ネイティブマッチャー作成（除外パターン）
   └─ ライブラリマッチャー作成
3. ファイルスキャン
   ├─ ワークスペースディレクトリをスキャン
   ├─ ライブラリディレクトリをスキャン
   └─ ファイル監視を設定
4. ファイルロード
   ├─ loading.create(scp)でローダー作成
   ├─ 各ファイルをloadFile()でキュー追加
   └─ loadAll()でバッチ処理
5. 完了処理
   ├─ ready状態を設定
   └─ 待機中のawaitReadyを解除
6. ファイル変更処理
   ├─ fw.eventコールバックで検知
   ├─ create: ファイル追加
   ├─ delete: ファイル削除
   └─ change: ファイル更新
```

### フローチャート

```mermaid
flowchart TD
    A[ワークスペース作成] --> B[m.create]
    B --> C{不正なルート?}
    C -->|Yes| D[エラー表示]
    C -->|No| E[m.foldersに追加]
    E --> F[m.awaitPreload]

    F --> G[マッチャー作成]
    G --> H[ネイティブマッチャー]
    G --> I[ライブラリマッチャー]

    H --> J[ワークスペーススキャン]
    I --> K[ライブラリスキャン]

    J --> L[fw.watch設定]
    K --> L

    L --> M[loading.loadFile]
    M --> N[loading.loadAll]
    N --> O[ready = true]

    P[ファイル変更] --> Q{イベント種別}
    Q -->|create| R[awaitLoadFile]
    Q -->|delete| S[files.remove]
    Q -->|change| T[files.setText]

    R --> U[ワークスペース更新]
    S --> U
    T --> U
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ルートパス制限 | /やHOMEディレクトリは開けない | FORCE_ACCEPT_WORKSPACEがfalse |
| BR-02 | スコープ優先順位 | folder > linked > fallbackの順で検索 | URI解決時 |
| BR-03 | .gitignore適用 | useGitIgnore設定時に適用 | 設定有効時 |
| BR-04 | サブモジュール除外 | ignoreSubmodules設定時に.gitmodulesを読む | 設定有効時 |
| BR-05 | ライブラリ除外 | ライブラリパスはワークスペーススキャンから除外 | library設定時 |
| BR-06 | 最大プリロード | maxPreloadを超えるファイルは警告 | プリロード時 |
| BR-07 | スキャン上限 | 100000ファイル超で警告表示 | スキャン時 |

### 計算ロジック

スコープ検索の優先順位：
1. `scope.getFolder(uri)`: URIがフォルダの子要素か確認
2. `scope.getLinkedScope(uri)`: URIがリンク先か確認
3. `scope.fallback`: 上記に該当しない場合のフォールバック

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

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

本機能はデータベースを使用しない（メモリ上でワークスペース状態を管理）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| WORKSPACE_NOT_ALLOWED | Error | /やHOMEをルートとして開いた | FORCE_ACCEPT_WORKSPACEを設定 |
| WORKSPACE_SCAN_TOO_MUCH | Warning | ファイル数が100000を超えた | 除外パターンを追加 |
| MWS_MAX_PRELOAD | Info | プリロード上限を超えた | maxPreloadを増加 |

### リトライ仕様

リトライは不要（設定変更で自動リロード）

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

トランザクション管理は不要（メモリ上の操作）

## パフォーマンス要件

- ファイルスキャンはバックグラウンドで実行
- プログレスバーでユーザーに進捗を表示
- await.delayで他の処理をブロックしない
- スキャン後の読み込みはバッチ処理

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

- ルートディレクトリやHOMEディレクトリの開放を制限
- パス正規化によるディレクトリトラバーサル防止
- 大量ファイルによるメモリ枯渇の防止（上限警告）

## 備考

- workspace.luaはscope.lua、loading.luaと連携して動作
- 設定変更時は自動的にリロードが発生
- マルチルートワークスペースでは各フォルダが独立したスコープを持つ

---

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

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

### 推奨読解順序

#### Step 1: スコープ構造を理解する

まず、ワークスペースのスコープ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | scope.lua | `script/workspace/scope.lua` | `@class scope`の定義（8-16行目） |
| 1-2 | scope.lua | `script/workspace/scope.lua` | `createScope`関数（173-184行目） |
| 1-3 | scope.lua | `script/workspace/scope.lua` | `m.getScope`関数（248-252行目） |

**読解のコツ**: スコープには3種類ある：
- `folder`: ワークスペースフォルダ
- `fallback`: どのフォルダにも属さないファイル用
- `override`: 一時的な上書き用

#### Step 2: ワークスペース初期化を理解する

ワークスペースの作成とプリロードを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | workspace.lua | `script/workspace/workspace.lua` | `m.create`関数（47-58行目） |
| 2-2 | workspace.lua | `script/workspace/workspace.lua` | `m.initRoot`関数（36-44行目） |
| 2-3 | workspace.lua | `script/workspace/workspace.lua` | `m.awaitPreload`関数（318-390行目） |

**主要処理フロー**:
- **47-58行目**: ワークスペース作成、不正ルートチェック
- **318-327行目**: プリロード開始、GCクリア
- **334-358行目**: ワークスペースディレクトリのスキャンと監視設定
- **360-382行目**: ライブラリディレクトリのスキャンと監視設定

#### Step 3: マッチャー（除外パターン）を理解する

ファイル除外パターンの構築を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | workspace.lua | `script/workspace/workspace.lua` | `m.getNativeMatcher`関数（137-203行目） |
| 3-2 | workspace.lua | `script/workspace/workspace.lua` | `m.getLibraryMatchers`関数（207-260行目） |
| 3-3 | workspace.lua | `script/workspace/workspace.lua` | `globInteferFace`オブジェクト（88-132行目） |

**主要処理フロー**:
- **143-148行目**: files.exclude設定の読み込み
- **149-167行目**: .gitignoreの読み込み
- **169-177行目**: .gitmodulesの読み込み
- **196-202行目**: glob.gitignoreでマッチャー作成

#### Step 4: ファイルローディングを理解する

ファイルのバッチロードを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | loading.lua | `script/workspace/loading.lua` | `@class workspace.loading`の定義（10-18行目） |
| 4-2 | loading.lua | `script/workspace/loading.lua` | `mt:loadFile`関数（69-155行目） |
| 4-3 | loading.lua | `script/workspace/loading.lua` | `mt:loadAll`関数（158-183行目） |

**主要処理フロー**:
- **69-76行目**: Luaファイルの最大プリロードチェック
- **80-114行目**: ファイルロード処理のキュー追加
- **158-172行目**: キューからファイルを順次ロード

#### Step 5: ファイル変更イベントを理解する

ファイル変更時の処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | workspace.lua | `script/workspace/workspace.lua` | `fw.event`コールバック（587-621行目） |
| 5-2 | workspace.lua | `script/workspace/workspace.lua` | `m.awaitLoadFile`関数（287-300行目） |
| 5-3 | workspace.lua | `script/workspace/workspace.lua` | `m.removeFile`関数（302-313行目） |

**主要処理フロー**:
- **590-602行目**: create/delete/changeイベントの処理
- **614-619行目**: .gitignore変更時のリロード

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

```
workspace (ワークスペース管理)
    │
    ├─ m.create
    │      └─ scope.createFolder
    │
    ├─ m.awaitPreload
    │      ├─ m.getNativeMatcher
    │      │      └─ glob.gitignore
    │      ├─ m.getLibraryMatchers
    │      │      └─ glob.gitignore
    │      ├─ fw.watch (ファイル監視設定)
    │      └─ loading.create
    │             ├─ mt:loadFile
    │             │      └─ pub.awaitTask('loadFile')
    │             └─ mt:loadAll
    │                    └─ files.setText
    │
    ├─ m.awaitReload
    │      ├─ m.flushFiles
    │      ├─ m.awaitPreload
    │      └─ m.onWatch('reload')
    │
    └─ fw.event (イベントコールバック)
           ├─ create: m.awaitLoadFile
           ├─ delete: files.remove
           └─ change: files.setText

scope (スコープ管理)
    │
    ├─ m.createFolder
    ├─ m.getFolder
    ├─ m.getLinkedScope
    └─ m.getScope
           └─ フォルダ > リンク > fallbackの優先順位

loading (ファイルローディング)
    │
    ├─ m.create
    ├─ mt:loadFile
    │      ├─ files.isLua
    │      ├─ files.isDll
    │      └─ pub.awaitTask('loadFile')
    └─ mt:loadAll
           ├─ files.setText
           └─ files.compileState
```

### データフロー図

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

LSP workspaceFolders
        │
        ▼
  m.create(uri)       ───▶  scope.createFolder
        │                    ↓
        │                    m.folders に追加
        ▼
  m.awaitPreload      ───▶  getNativeMatcher
        │                    (除外パターン構築)
        │                    ↓
        │                    getLibraryMatchers
        │                    ↓
        │                    ディレクトリスキャン
        │                    ↓
        │                    fw.watch (監視設定)
        ▼
  loading.loadFile    ───▶  ファイル読み込み
        │                    ↓
        │                    キューに追加
        ▼
  loading.loadAll     ───▶  files.setText
        │                    ↓
        │                    files.compileState
        ▼
  ready = true        ───▶  awaitReady解除
        │
        │
[ファイル変更イベント]
        │
        ▼
  fw.event            ───▶  イベント種別判定
        │                    ↓
        │                    create/delete/change処理
        ▼
  ワークスペース更新  ───▶  files更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| workspace.lua | `script/workspace/workspace.lua` | ソース | ワークスペース管理メイン |
| init.lua | `script/workspace/init.lua` | ソース | workspace.luaのエクスポート |
| scope.lua | `script/workspace/scope.lua` | ソース | スコープ管理 |
| loading.lua | `script/workspace/loading.lua` | ソース | ファイルローディング |
| filewatch.lua | `script/filewatch.lua` | ソース | ファイル変更監視 |
| files.lua | `script/files.lua` | ソース | ファイルキャッシュ |
| config.lua | `script/config/config.lua` | ソース | 設定管理 |
| glob.lua | `script/glob/init.lua` | ソース | ファイルパターンマッチング |
| gc.lua | `script/gc.lua` | ソース | ガベージコレクション管理 |
| progress.lua | `script/progress.lua` | ソース | プログレスバー表示 |
