# 機能設計書 42-コード折りたたみ (textDocument/foldingRange)

## 概要

本ドキュメントは、Lua Language Serverにおけるコード折りたたみ機能（textDocument/foldingRange）の設計仕様を記述する。この機能は、関数、ブロック構造、コメント等の折りたたみ可能な範囲をエディタに提供する。

### 本機能の処理概要

コード折りたたみ機能は、Luaソースコード内の論理的なブロック（関数、条件分岐、ループ、テーブル、コメント等）を特定し、それらの範囲情報をLSPクライアントに提供することで、エディタ上でのコード折りたたみ機能を実現する。

**業務上の目的・背景**：大規模なLuaソースファイルを閲覧・編集する際、コードの全体構造を把握しつつ、特定の部分に集中するためにはコード折りたたみが有効である。関数定義やブロック構造を折りたたむことで、可読性が向上し、コードナビゲーションが効率化される。また、regionコメント（`--region`/`--endregion`）によるカスタム折りたたみ領域の定義もサポートし、開発者が論理的なセクションを明示できる。

**機能の利用シーン**：
- 大規模ファイルの全体構造を把握したい場合
- 特定の関数やブロックに集中して作業したい場合
- コードレビュー時に関心のない部分を隠したい場合
- ドキュメントコメントを折りたたんでコードのみを表示したい場合

**主要な処理内容**：
1. クライアントからfoldingRangeリクエストを受信
2. 対象ドキュメントのAST（抽象構文木）を取得
3. ASTを走査して折りたたみ可能なソース要素を特定
4. コメント（通常コメント、LuaDoc）の折りたたみ範囲を特定
5. region/endregionコメントによるカスタム領域を処理
6. 折りたたみ範囲情報をLSP形式に変換して返却

**関連システム・外部連携**：
- パーサー（script/parser）からのAST情報
- LSPクライアントとのJSON-RPC通信

**権限による制御**：特に権限による制御は行われない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | 折りたたみ範囲 | 主機能 | コードの折りたたみ可能な範囲を提供する主処理 |

## 機能種別

コードナビゲーション / エディタ補助機能

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| textDocument.uri | string | Yes | 対象ドキュメントのURI | 有効なファイルURIであること |

### 入力データソース

- LSPクライアントからのJSON-RPCリクエスト
- ファイルシステム上のLuaソースコード（AST経由）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| FoldingRange[] | array | 折りたたみ範囲の配列 |
| FoldingRange.startLine | integer | 開始行（0-indexed） |
| FoldingRange.endLine | integer | 終了行（0-indexed） |
| FoldingRange.kind | string | 折りたたみ種別（'region' or 'comment'） |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ textDocument/foldingRangeメッセージを受信

2. ワークスペース準備確認
   └─ workspace.awaitReady(uri)で準備完了を待機

3. ドキュメント状態取得
   └─ files.getState(uri)でAST情報を取得

4. AST走査
   └─ guide.eachSource()で各ソース要素を処理

5. 折りたたみ範囲抽出
   └─ careテーブルの関数で各タイプに応じた範囲を生成

6. コメント処理
   └─ state.commsからコメントの折りたたみ範囲を生成

7. 位置変換
   └─ converter.packPosition()で内部位置をLSP位置に変換

8. レスポンス返却
   └─ FoldingRange配列を返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[workspace.awaitReady]
    B --> C{ファイル存在?}
    C -->|No| D[null返却]
    C -->|Yes| E{ドキュメント状態取得}
    E -->|失敗| D
    E -->|成功| F[core/folding.lua呼び出し]
    F --> G[AST走査]
    G --> H[function/do/if/loop等を処理]
    H --> I[コメント処理]
    I --> J[region/endregion処理]
    J --> K[位置変換]
    K --> L{startLine < endLine?}
    L -->|No| M[スキップ]
    L -->|Yes| N[結果に追加]
    M --> O{次の領域}
    N --> O
    O -->|あり| K
    O -->|なし| P[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-42-01 | 行数チェック | startLine < endLineの場合のみ折りたたみ範囲として有効 | 常時 |
| BR-42-02 | 最終行非表示 | hideLastLine=trueの場合、endLineを1減算 | region/endregion, LuaDoc |
| BR-42-03 | regionコメント | `--region`または`--#region`で開始 | 短コメント |
| BR-42-04 | endregionコメント | `--endregion`または`--#endregion`で終了 | 短コメント |
| BR-42-05 | repeat-until | keywordが4つの場合のみ折りたたみ対象 | repeat文 |

### 計算ロジック

endLine調整ロジック:
```lua
if not region.hideLastLine then
    endLine = endLine - 1
end
```

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

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | null返却 | ファイルが存在しない | クライアントは折りたたみ機能を無効化 |
| - | null返却 | ドキュメント状態が取得できない | クライアントは折りたたみ機能を無効化 |

### リトライ仕様

リトライは行わない。

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

該当なし（メモリ内処理のみ）

## パフォーマンス要件

- await.delay()による非同期処理で応答性を確保
- 大規模ファイルでもUIをブロックしない設計

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

- 特になし

## 備考

- 折りたたみ種別は'region'と'comment'の2種類
- LuaDocコメントは関連するソース要素と共に処理される

---

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

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

### 推奨読解順序

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

折りたたみ範囲のデータ構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | folding.lua | `script/core/folding.lua` | 内部folding構造体（start, finish, kind, hideLastLine）を理解 |

**読解のコツ**: 内部では位置を数値（offset）で管理し、最終的にline番号に変換する点に注目。

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

LSPリクエストハンドラを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | provider.lua | `script/provider/provider.lua` | foldingRangeの登録と結果変換を理解 |

**主要処理フロー**:
1. **1142-1145行目**: capability定義（foldingRangeProvider: true）
2. **1147-1150行目**: ワークスペース準備待機とファイル存在確認
3. **1158行目**: core/folding.lua呼び出し
4. **1164-1176行目**: 位置変換とendLine調整

#### Step 3: コアロジックを理解する

折りたたみ範囲抽出のロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | folding.lua | `script/core/folding.lua` | careテーブルの各関数を理解 |

**主要処理フロー**:
- **6-22行目**: function処理（bindDocsも含む）
- **23-30行目**: do文処理
- **31-41行目**: if文処理（各ブロック個別に範囲生成）
- **42-65行目**: loop/in/while処理
- **66-79行目**: repeat処理（keyword数チェック）
- **80-95行目**: table/string処理
- **96-121行目**: 短コメントのregion/endregion処理
- **122-137行目**: 長コメント処理
- **138-146行目**: doc.class/doc.alias処理

#### Step 4: guide.eachSourceの理解

AST走査処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | guide.lua | `script/parser/guide.lua` | eachSource関数の動作を理解 |

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

```
provider.lua (m.register 'textDocument/foldingRange')
    │
    ├─ workspace.awaitReady(uri)
    │      └─ workspace.lua: ワークスペース準備完了を待機
    │
    ├─ files.exists(uri)
    │      └─ files.lua: ファイル存在確認
    │
    ├─ files.getState(uri)
    │      └─ files.lua: AST情報を取得
    │
    ├─ core/folding.lua
    │      │
    │      ├─ files.getState(uri)
    │      │      └─ AST取得（再確認）
    │      │
    │      ├─ files.getText(uri)
    │      │      └─ テキスト取得
    │      │
    │      ├─ guide.eachSource(state.ast, callback)
    │      │      └─ parser/guide.lua: AST走査
    │      │
    │      └─ care[tp](source, text, regions)
    │             └─ 各タイプの折りたたみ範囲生成
    │
    └─ converter.packPosition(state, region.start/finish)
           └─ converter.lua: 位置変換
```

### データフロー図

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

LSPリクエスト             provider.lua
  └─ uri          ───▶    │
                          ├─ workspace.awaitReady()
                          ├─ files.exists()
                          └─ files.getState()
                                    │
                                    ▼
                          folding.lua
                          ├─ guide.eachSource()
                          │    ├─ function → region
                          │    ├─ do/loop/while → region
                          │    ├─ if → region (per block)
                          │    ├─ table/string → region
                          │    └─ comment → comment/region
                          └─ state.comms処理
                                    │
                                    ▼
                          provider.lua
                          ├─ converter.packPosition()
                          └─ hideLastLine調整
                                    │
                                    ▼                  FoldingRange[]
                          LSPレスポンス ────────────▶  ├─ startLine
                                                       ├─ endLine
                                                       └─ kind
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSPリクエストハンドラの登録・実行 |
| folding.lua | `script/core/folding.lua` | ソース | 折りたたみ範囲抽出のコアロジック |
| converter.lua | `script/proto/converter.lua` | ソース | LSPプロトコルと内部表現の変換 |
| files.lua | `script/files.lua` | ソース | ファイル管理・状態取得 |
| guide.lua | `script/parser/guide.lua` | ソース | AST走査ユーティリティ |
| workspace.lua | `script/workspace/workspace.lua` | ソース | ワークスペース管理 |
| await.lua | `script/await.lua` | ソース | 非同期処理サポート |
