# 機能設計書 40-範囲フォーマット (textDocument/rangeFormatting)

## 概要

本ドキュメントは、Lua Language Serverにおける「範囲フォーマット機能」の設計詳細を記載したものである。ユーザーが選択した範囲のLuaコードのみを整形し、部分的なコードスタイルの適用を可能にする機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：
ファイル全体をフォーマットすると、意図しない部分まで変更されることがある。本機能は、ユーザーが選択した範囲のみをフォーマットすることで、より細かい制御を可能にし、既存のコードスタイルを維持しながら部分的な整形を行えるようにする。

**機能の利用シーン**：
- コードの一部分のみを整形したい場合
- 新規追加したコード部分のみをフォーマットしたい場合
- レガシーコードと新規コードが混在するファイルでの部分整形

**主要な処理内容**：
1. 対象ファイルのテキストを取得する
2. 選択範囲（開始行・終了行）を取得する
3. code_formatモジュールで範囲フォーマットを実行する
4. フォーマット結果と実際に変更された行範囲を取得する
5. テキスト編集として返却する

**関連システム・外部連携**：
- LSP (Language Server Protocol)クライアント（VSCode等のエディタ）との連携
- `textDocument/rangeFormatting`リクエストのハンドラ
- code_formatモジュール（C++拡張）

**権限による制御**：
- 設定`Lua.format.enable`でフォーマット機能の有効/無効を制御可能
- 設定`Lua.format.defaultConfig`でデフォルトスタイルを指定可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 23 | 範囲フォーマット | 主機能 | 選択範囲のコード整形を実行する主処理 |

## 機能種別

コードフォーマット / テキスト変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | フォーマット対象ファイルのURI | 有効なファイルURIであること |
| range | table | Yes | フォーマット範囲（start/end行番号） | 有効な範囲であること |
| options | table | No | フォーマットオプション（タブサイズ等） | - |

### 入力データソース

- ファイルシステムから読み込まれたLuaソースコード（originText）
- range.start.line / range["end"].line から開始・終了行
- EditorConfigファイル（.editorconfig）からスタイル設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| start | integer | 変更範囲の開始オフセット |
| finish | integer | 変更範囲の終了オフセット |
| text | string | フォーマット後のテキスト |

### 出力先

- LSPクライアントへのテキスト編集レスポンス
- エディタが選択範囲の内容を更新

## 処理フロー

### 処理シーケンス

```
1. ファイル状態の取得
   └─ files.getState(uri)でファイル状態を取得
2. code_formatモジュールの確認
   └─ pcall(require, 'code_format')でモジュール存在確認
3. テキストの取得
   └─ state.originTextで元テキスト取得
4. 範囲フォーマット実行
   └─ codeFormat.range_format(uri, text, startLine, endLine, options)
5. 結果の変換
   └─ converter.unpackPositionで位置変換
6. テキスト編集の返却
   └─ { start, finish, text }を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[ファイル状態を取得]
    B --> C{状態が存在するか?}
    C -->|No| D[nil返却]
    C -->|Yes| E{code_formatロード可能?}
    E -->|No| D
    E -->|Yes| F[originTextを取得]
    F --> G[codeFormat.range_format実行]
    G --> H{status == true?}
    H -->|No| I[エラーログ出力]
    I --> D
    H -->|Yes| J[位置変換]
    J --> K[テキスト編集を返却]
    K --> L[終了]
    D --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | モジュール必須 | code_formatモジュールが必須 | pcallでロード確認 |
| BR-40-02 | 範囲指定 | range.start.lineとrange["end"].lineで行範囲指定 | クライアントからの指定 |
| BR-40-03 | 実効範囲 | startLine/endLineはフォーマッタが実際に変更した範囲 | codeFormat.range_formatの戻り値 |
| BR-40-04 | 行末位置 | endLine + 1行目の先頭を終了位置とする | 次の行の先頭まで含む |

### 計算ロジック

#### 範囲フォーマット処理

```lua
return function(uri, range, options)
    local state = files.getState(uri)
    if not state then
        return
    end
    local suc, codeFormat = pcall(require, "code_format")
    if not suc then
        return
    end
    local text = state.originText
    local status, formattedText, startLine, endLine = codeFormat.range_format(
        uri, text, range.start.line, range["end"].line, options)

    if not status then
        if formattedText ~= nil then
            log.error(formattedText)
        end
        return
    end

    return {
        {
            start = converter.unpackPosition(state, { line = startLine, character = 0 }),
            finish = converter.unpackPosition(state, { line = endLine + 1, character = 0 }),
            text = formattedText,
        }
    }
end
```

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ファイル状態なし | files.getState(uri)がnilを返す場合 | nilを返却して正常終了 |
| - | モジュールなし | code_formatがロードできない場合 | nilを返却して正常終了 |
| - | フォーマット失敗 | status == falseの場合 | エラーをログ出力してnil返却 |

### リトライ仕様

リトライは行わない。ユーザーが再度フォーマットコマンドを実行する。

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

該当なし（読み取り専用処理、変更はクライアント側で適用）

## パフォーマンス要件

- フォーマット処理は通常1秒以内に完了すること
- 範囲が大きくても適切に処理できること

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

- ユーザーコードの読み取りのみを行う
- フォーマット結果はクライアントに返却され、ユーザーの確認後に適用される

## 備考

- code_formatモジュール（C++拡張）が必要
- 実際にフォーマットされる範囲は、指定範囲と異なる場合がある（ブロック単位での調整）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | rangeformatting.lua | `script/core/rangeformatting.lua` | 範囲フォーマット処理のメインロジック |

**読解のコツ**: codeFormat.range_formatは4つの戻り値（status, formattedText, startLine, endLine）を返す点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | rangeformatting.lua | `script/core/rangeformatting.lua` | メイン関数 |

**主要処理フロー**:
1. **5-8行目**: files.getState(uri)でファイル状態取得
2. **9-12行目**: pcall(require, "code_format")でモジュールロード
3. **13行目**: state.originTextでテキスト取得
4. **14-15行目**: codeFormat.range_format実行（開始行、終了行を指定）
5. **17-22行目**: エラーハンドリング
6. **24-31行目**: 位置変換とテキスト編集の返却

#### Step 3: 位置変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | converter.lua | `script/proto/converter.lua` | unpackPosition関数 |

**読解のコツ**: LSPの行番号（0-indexed）をLuaのオフセット位置に変換する。

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

```
LSPクライアント
    │
    ├─ textDocument/rangeFormatting リクエスト
    │      │
    │      ├─ uri: ファイルURI
    │      ├─ range: { start: { line }, end: { line } }
    │      └─ options: フォーマットオプション
    │
    ▼
script/provider/provider.lua (ハンドラ登録)
    │
    ├─ require('core.rangeformatting')
    │      │
    │      ├─ files.getState(uri)
    │      │      └─ state.originText
    │      │
    │      ├─ pcall(require, 'code_format')
    │      │
    │      ├─ codeFormat.range_format(uri, text, startLine, endLine, options)
    │      │      └─ C++拡張による範囲フォーマット処理
    │      │      └─ 戻り値: status, formattedText, startLine, endLine
    │      │
    │      └─ converter.unpackPosition(state, position)
    │
    └─ TextEdit[]をレスポンス
           │
           ▼
    LSPクライアントが選択範囲を更新
```

### データフロー図

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

textDocument/rangeFormatting ───▶ プロバイダー ───▶ rangeformatting.lua
        │                                                │
        ▼                                                ▼
  range.start.line                          files.getState(uri)
  range.end.line                                         │
  options                                                ▼
                                        codeFormat.range_format(
                                            uri, text,
                                            startLine, endLine,
                                            options
                                        )
                                                         │
                                                         ▼
                                        ┌────────────────┴────────────────┐
                                        │                                  │
                                        ▼                                  ▼
                                  フォーマット成功                     フォーマット失敗
                                        │                                  │
                                        ▼                                  ▼
                               converter.unpackPosition()                nil
                                        │
                                        ▼
                                  TextEdit[]
                                        │
                                        ▼
                            LSPクライアントへレスポンス
                                        │
                                        ▼
                                  選択範囲更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| rangeformatting.lua | `script/core/rangeformatting.lua` | ソース | 範囲フォーマットのメインロジック |
| provider.lua | `script/provider/provider.lua` | ソース | LSPハンドラ登録 |
| converter.lua | `script/proto/converter.lua` | ソース | 位置変換ユーティリティ |
| formatting.lua | `script/provider/formatting.lua` | ソース | フォーマット設定管理 |
| files.lua | `script/files.lua` | ソース | ファイル状態管理 |
| log.lua | `script/log.lua` | ソース | ログ出力 |
