# 機能設計書 50-ドキュメントフォーマット

## 概要

本ドキュメントは、clojure-lspの「ドキュメントフォーマット」機能（textDocument/formatting, textDocument/rangeFormatting）の設計を詳細に記載する。この機能は、Clojureコードを自動整形するLSP機能である。

### 本機能の処理概要

**業務上の目的・背景**：コードの一貫したフォーマットは、可読性向上とチーム開発における統一性確保に不可欠である。この機能により、手動でのインデント調整やスタイル修正の手間を省き、コーディング標準を自動的に適用できる。

**機能の利用シーン**：開発者がファイル保存時やコマンド実行時にコード整形を適用する。また、選択範囲のみを整形することも可能。

**主要な処理内容**：
1. cljfmt設定の解決（デフォルト + :style/indentメタデータ + ユーザー設定）
2. ファイル全体または指定範囲のコードを整形
3. 変更がある場合のみTextEditを返却

**関連システム・外部連携**：cljfmtライブラリ、.cljfmt.ednファイル。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| N/A | エディタ | 主画面 | ファイル保存時・コマンド実行時 |

## 機能種別

コード整形 / ドキュメント編集

## 入力仕様

### 入力パラメータ

#### textDocument/formatting

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| text-document.uri | String | Yes | 対象ファイルURI | 有効なファイルURI |

#### textDocument/rangeFormatting

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| text-document.uri | String | Yes | 対象ファイルURI | 有効なファイルURI |
| range.start | Position | Yes | 開始位置 | line, character |
| range.end | Position | Yes | 終了位置 | line, character |

### 入力データソース

- cljfmtデフォルト設定
- :style/indentメタデータ（解析結果から抽出）
- ユーザー設定（:cljfmt, :cljfmt-config-path）
- .cljfmt.ednファイル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| edits | Array[TextEdit] | 編集操作の配列 |
| edits[].range | Range | 編集対象範囲 |
| edits[].new-text | String | 新しいテキスト |

### 出力先

- LSP textDocument/formatting レスポンス
- LSP textDocument/rangeFormatting レスポンス

## 処理フロー

### 処理シーケンス

```
1. cljfmt設定を解決
   ├─ cljfmt.config/default-config（デフォルト）
   ├─ extract-style-indent-metadata（:style/indent）
   └─ resolve-user-cljfmt-config（ユーザー設定）
2. 整形対象テキストを取得
   └─ formatting: ファイル全体
   └─ range-formatting: 指定範囲のトップレベルフォーム
3. cljfmtで整形実行
   └─ reformat-string または reformat-form
4. 結果を比較
   └─ 変更なし: 空配列返却
   └─ 変更あり: TextEdit返却
```

### フローチャート

```mermaid
flowchart TD
    A[フォーマットリクエスト] --> B{範囲指定?}
    B -->|全体| C[ファイルテキスト取得]
    B -->|範囲| D[範囲のフォーム取得]
    C --> E[cljfmt設定解決]
    D --> F[alias-map追加]
    E --> G[reformat-string]
    F --> H[reformat-form]
    G --> I{変更あり?}
    H --> J[TextEdit生成]
    I -->|いいえ| K[空配列返却]
    I -->|はい| L[全体TextEdit生成]
    J --> M[結果返却]
    K --> M
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 設定優先順位 | デフォルト < :style/indent < ユーザー設定 | 常時 |
| BR-02 | 設定ファイルパス | :cljfmt-config-pathで指定（デフォルト: .cljfmt.edn） | 設定時 |
| BR-03 | メモ化キャッシュ | 設定は3秒間キャッシュ | パフォーマンス |
| BR-04 | 変更なし最適化 | 整形後テキストが同一なら空配列を返却 | formatting |
| BR-05 | 範囲拡張 | 範囲内のトップレベルフォーム全体を整形 | range-formatting |

### 計算ロジック

:style/indent変換:
```clojure
(defn style-indent->cljfmt-spec [spec]
  ;; CIDER形式の:style/indentをcljfmt形式に変換
  ;; - 数値 → [:block n]
  ;; - :defn → [:inner 0]
  ;; - ネスト指定 → [:inner depth index]
  ...)
```

設定マージ:
```clojure
(defn merge-configs [a b]
  (-> (merge a b)
      (assoc :indents (merge (:indents a {}) (:indents b)))
      (assoc :extra-indents (merge (:extra-indents a {}) (:extra-indents b)))))
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| N/A | N/A | SELECT | 参照のみ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | パース失敗 | ファイル解析失敗 | 空配列返却 |
| N/A | 設定読込失敗 | .cljfmt.edn解析失敗 | デフォルト設定使用 |

### リトライ仕様

リトライなし

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

読み取り専用のため、トランザクション管理なし。

## パフォーマンス要件

- 設定解決: 3秒間キャッシュ（memoize/ttl）
- 整形処理: 数百ms以内

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

- .cljfmt.clj読込時は*read-eval*をfalseに設定

## 備考

- cljfmtはClojure標準のフォーマッタライブラリ
- alias-mapはcljfmtのバグ回避のため、空の場合はnilに変換

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | cljfmt設定構造を理解 |

**読解のコツ**: merge-configs関数で:indentsと:extra-indentsの統合ルールを把握する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | handlers.clj | `lib/src/clojure_lsp/handlers.clj` | formatting, range-formatting ハンドラ |

**主要処理フロー**:
- **401-405行目**: `formatting` - ファイル全体整形
- **407-419行目**: `range-formatting` - 範囲整形

#### Step 3: 設定解決を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | 設定解決ロジック |

**主要処理フロー**:
- **21-33行目**: `resolve-user-cljfmt-config` - ユーザー設定読込
- **83-94行目**: `extract-style-indent-metadata` - :style/indent抽出
- **105-112行目**: `resolve-cljfmt-config` - 設定統合
- **116-117行目**: `cljfmt-config` - メモ化キャッシュ

#### Step 4: 整形処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | 整形実行ロジック |

**主要処理フロー**:
- **119-127行目**: `formatting` - ファイル全体整形
- **129-156行目**: `range-formatting` - 範囲整形

#### Step 5: :style/indent変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | CIDER形式からcljfmt形式への変換 |

**主要処理フロー**:
- **35-44行目**: `arg-spec->cljfmt-arg` - 引数仕様変換
- **46-81行目**: `style-indent->cljfmt-spec` - インデント仕様変換

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

```
handlers/formatting
    │
    └─ f.format/formatting
           │
           ├─ f.file-management/force-get-document-text
           │
           ├─ cljfmt-config (メモ化)
           │      │
           │      └─ resolve-cljfmt-config
           │             │
           │             ├─ cljfmt.config/default-config
           │             ├─ extract-style-indent-metadata
           │             │      └─ q/xf-analysis->var-definitions
           │             │      └─ style-indent->cljfmt-spec
           │             └─ resolve-user-cljfmt-config
           │                    └─ (slurp .cljfmt.edn)
           │
           └─ cljfmt/reformat-string
                  │
                  └─ wrap-normalize-newlines

handlers/range-formatting
    │
    └─ f.format/range-formatting
           │
           ├─ parser/safe-zloc-of-file
           ├─ q/namespace-aliases (alias-map構築)
           │
           ├─ parser/to-pos (開始・終了位置)
           ├─ edit/to-top (トップレベルフォーム取得)
           │
           └─ cljfmt/reformat-form
                  │
                  └─ n/forms-node
```

### データフロー図

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

cljfmt.config/default-config
        │
        ├─────────────────▶ merge-configs
        │                          │
:style/indent (var-defs) ─▶ extract-style-indent-metadata
                                   │
                                   ▼
.cljfmt.edn ─────────────▶ resolve-user-cljfmt-config
                                   │
                                   ▼
                          cljfmt-config (メモ化)
                                   │
         ┌─────────────────────────┼─────────────────────────┐
         │                         │                         │
   formatting              range-formatting              alias-map
         │                         │                         │
         ▼                         ▼                         │
force-get-document-text    parser/to-pos                     │
         │                         │                         │
         ▼                         ▼                         ▼
 reformat-string            reformat-form ◀─────────── merge alias-map
         │                         │
         ▼                         ▼
   (比較: 変更あり?)          TextEdit生成
         │                         │
         ├─ Yes: TextEdit          │
         └─ No: 空配列             │
                                   ▼
                             結果返却
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| format.clj | `lib/src/clojure_lsp/feature/format.clj` | ソース | フォーマットのメインロジック |
| handlers.clj | `lib/src/clojure_lsp/handlers.clj` | ソース | LSPハンドラ |
| parser.clj | `lib/src/clojure_lsp/parser.clj` | ソース | ファイル解析 |
| queries.clj | `lib/src/clojure_lsp/queries.clj` | ソース | 解析データクエリ |
| edit.clj | `lib/src/clojure_lsp/refactor/edit.clj` | ソース | 編集ユーティリティ |
| file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | ソース | ファイル管理 |
| settings.clj | `lib/src/clojure_lsp/settings.clj` | ソース | 設定管理 |
