# 帳票設計書 4-format

## 概要

本ドキュメントは、clojure-lspのformat（フォーマット）機能によって出力される帳票の設計仕様を定義する。

### 本帳票の処理概要

format帳票は、cljfmtによるコードフォーマット結果を出力する帳票である。フォーマット前後の差分をunified diff形式で表示し、`--dry`オプションで変更を適用せずに差分のみを確認することが可能である。

**業務上の目的・背景**：Clojureプロジェクトでは、コードスタイルの統一がチーム開発において重要である。本帳票は、cljfmtライブラリを使用してコードを自動フォーマットし、インデント、空白、改行などを一貫したスタイルに整形することで、コードの可読性向上とレビュー効率化を実現する。また、`:style/indent`メタデータによるカスタムインデント設定にも対応し、プロジェクト固有のスタイルを維持できる。

**帳票の利用シーン**：本帳票は以下の場面で利用される。(1) コミット前の自動フォーマット、(2) CI/CDパイプラインでのフォーマットチェック（dryモード）、(3) プロジェクト全体の一括フォーマット、(4) エディタ保存時の自動整形、(5) コードレビュー前の事前整形。

**主要な出力内容**：
1. フォーマット前後の差分（unified diff形式）
2. 変更対象ファイルのリスト
3. 変更件数のサマリー
4. インデント/空白/改行の修正箇所

**帳票の出力タイミング**：CLIコマンド`clojure-lsp format`の実行時、またはAPI経由での`clojure-lsp.api/format!`関数呼び出し時に出力される。`--dry`オプション指定時は差分表示のみ、未指定時はファイル変更も実行される。

**帳票の利用者**：開発者、コードレビュアー、CI/CDシステム

## 帳票種別

差分レポート / フォーマット結果レポート

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| CLI-01 | コマンドライン | `clojure-lsp format` | コマンド実行 |
| CLI-02 | コマンドライン（ドライラン） | `clojure-lsp format --dry` | コマンド実行 |
| API-01 | Clojure REPL | `(clojure-lsp.api/format! opts)` | 関数呼び出し |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | unified diff（テキスト） |
| 用紙サイズ | N/A（コンソール出力） |
| 向き | N/A |
| ファイル名 | 標準出力 |
| 出力方法 | 標準出力 / 標準エラー出力 |
| 文字コード | UTF-8 |

### 出力フォーマット詳細

#### unified diff形式
```diff
--- a/src/my_project/core.clj
+++ b/src/my_project/core.clj
@@ -5,7 +5,7 @@
 (defn my-function
-  [arg1  arg2]
+  [arg1 arg2]
   (let [x 1
-        y  2]
+        y 2]
     (+ x y)))
```

#### 実行モード別出力

**dryモード（`--dry`）**:
- 差分のみ表示、ファイル変更なし
- result-code: 1（変更あり）、0（変更なし）

**実行モード（デフォルト）**:
- ファイル変更を実行
- 変更した名前空間をログ出力
- result-code: 0

## 帳票レイアウト

### レイアウト概要

unified diff形式で差分を出力。各ファイルごとにヘッダーと変更内容が表示される。

```
┌─────────────────────────────────────────────────────────────────┐
│  --- a/src/namespace/file.clj                                   │
│  +++ b/src/namespace/file.clj                                   │
│  @@ -n,m +n,m @@                                                │
│   (defn function-name                                           │
│  -  [arg1  arg2]                                                │
│  +  [arg1 arg2]                                                 │
│     body)                                                       │
└─────────────────────────────────────────────────────────────────┘
```

### 出力項目

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 旧ファイルパス | 変更前ファイル | URI | `--- a/path` |
| 2 | 新ファイルパス | 変更後ファイル | URI | `+++ b/path` |
| 3 | 変更範囲 | 行番号範囲 | range | `@@ -n,m +n,m @@` |
| 4 | 削除行 | 削除されるコード | old-text | `-` プレフィックス |
| 5 | 追加行 | 追加されるコード | new-text | `+` プレフィックス |
| 6 | コンテキスト行 | 変更なし行 | - | スペースプレフィックス |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| project-root | プロジェクトルートディレクトリ | No（デフォルト: カレントディレクトリ） |
| namespace | 対象名前空間（複数指定可） | No（デフォルト: 全名前空間） |
| filenames | 対象ファイル（複数指定可） | No |
| ns-exclude-regex | 除外名前空間の正規表現 | No |
| dry? | ドライランモード | No（デフォルト: false） |

### フォーマット設定

| 設定 | 説明 | 設定場所 |
|------|------|---------|
| cljfmt | cljfmt設定 | clojure-lsp設定 |
| cljfmt-config-path | 外部設定ファイルパス | clojure-lsp設定 |
| :style/indent | カスタムインデント | 変数メタデータ |

### ソート順

N/A（フォーマット処理のため順序は入力依存）

### 改ページ条件

N/A（コンソール出力のため）

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| db.analysis | :style/indentメタデータ取得 | - |
| db.documents | ドキュメントテキスト | URI |
| db.settings | cljfmt設定 | - |

### 設定詳細

#### cljfmt設定マージ順序

1. cljfmt.config/default-config（デフォルト）
2. :style/indentメタデータから抽出した設定
3. ユーザー設定（.cljfmt.edn または clojure-lsp設定の:cljfmt）

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| result-code (dry) | edits存在 ? 1 : 0 | - | dryモード |
| result-code (exec) | 常に0 | - | 実行モード |
| new-text | cljfmt/reformat-string(text, settings) | - | フォーマット後テキスト |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[formatコマンド実行] --> B[オプション解析]
    B --> C[プロジェクト解析初期化]
    C --> D[対象URI取得]
    D --> E[各URIのドキュメント読込]
    E --> F[cljfmt設定解決]
    F --> G[cljfmtによるフォーマット]
    G --> H{変更あり?}
    H -->|No| I[スキップ]
    H -->|Yes| J[編集内容生成]
    J --> K{dry?}
    K -->|Yes| L[差分文字列生成]
    K -->|No| M[ファイル変更適用]
    L --> N[差分出力]
    M --> O[変更ログ出力]
    N --> P[終了コード設定]
    O --> P
    I --> P
    P --> Q[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| プロジェクト解析失敗 | 不正なプロジェクト構成 | Error during project analysis | プロジェクト設定確認 |
| 名前空間未発見 | 指定名前空間が存在しない | Namespace X not found | 名前空間名確認 |
| 変更なし | フォーマット不要 | Nothing to format! | 正常終了 |
| 設定ファイルエラー | .cljfmt.ednが不正 | - | 設定ファイル確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数百〜数千ファイル |
| 目標出力時間 | 1ファイルあたり: 50ms以内 |
| 同時出力数上限 | 並列処理（pmap使用） |

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

- ファイル変更を伴うため、バージョン管理システムでの差分確認を推奨
- `--dry`オプションでの事前確認を推奨
- .cljfmt.edn（.clj形式）の場合、`*read-eval*`はfalseで読み込み

## 備考

- cljfmt設定は3秒間TTLキャッシュされる
- `:style/indent`メタデータはプロジェクト解析時に自動抽出される
- 改行コードは`wrap-normalize-newlines`で正規化される

---

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

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

### 推奨読解順序

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

format機能で使用される主要なデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | **119-127行目**: `formatting`関数の戻り値構造 |
| 1-2 | shared.clj | `lib/src/clojure_lsp/shared.clj` | `full-file-range`定義 |

**読解のコツ**: format機能はcljfmtライブラリに依存している。cljfmtの設定構造（:indents, :extra-indents等）を理解することが重要。

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

CLIからの呼び出しフローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | main.clj | `cli/src/clojure_lsp/main.clj` | **176行目**: `"format"`アクション分岐 |
| 2-2 | main.clj | `cli/src/clojure_lsp/main.clj` | **204行目**: `internal-api/format!`呼び出し |

**主要処理フロー**:
1. **156-180行目**: `parse`関数でコマンドライン引数を解析
2. **194-215行目**: `handle-action!`関数でアクション実行
3. **204行目**: formatの場合`internal-api/format!`を呼び出し

#### Step 3: 内部API層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | internal_api.clj | `lib/src/clojure_lsp/internal_api.clj` | **374-401行目**: `format!*`関数の実装 |

**主要処理フロー**:
- **378行目**: `dynamic-setup-project-analysis!`でプロジェクト解析
- **381-387行目**: 各URIに対してformatting関数実行
- **388-401行目**: dry/実行モードに応じた処理

#### Step 4: format機能コア層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | **119-127行目**: `formatting`関数 |
| 4-2 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | **21-33行目**: `resolve-user-cljfmt-config`関数 |
| 4-3 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | **105-112行目**: `resolve-cljfmt-config`関数 |
| 4-4 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | **83-94行目**: `extract-style-indent-metadata`関数 |

**主要処理フロー**:
- **119行目**: `formatting`がエントリーポイント
- **121行目**: `cljfmt-config`で設定解決（TTLキャッシュ付き）
- **122行目**: `cljfmt/reformat-string`でフォーマット実行
- **123-126行目**: 変更がある場合のみ編集結果を返却

#### Step 5: cljfmt設定マージを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | **96-103行目**: `merge-configs`関数 |
| 5-2 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | **35-81行目**: `style-indent->cljfmt-spec`関数 |

**主要処理フロー**:
- **96-103行目**: 複数の設定をマージ（:indents, :extra-indentsを特別処理）
- **105-112行目**: default → :style/indent → user設定の順でマージ
- **35-81行目**: Cider形式の:style/indentをcljfmt形式に変換

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

```
clojure-lsp.main/-main
    |
    +-- parse (コマンド解析)
    |
    +-- handle-action!
           |
           +-- clojure-lsp.internal-api/format!
                  |
                  +-- build-components (コンポーネント初期化)
                  |
                  +-- format!*
                         |
                         +-- setup-api!
                         |
                         +-- dynamic-setup-project-analysis!
                         |      |
                         |      +-- :project-namespaces-only (デフォルト)
                         |
                         +-- options->uris
                         |
                         +-- [pmap] open-file! + handlers/formatting
                         |              |
                         |              +-- f.format/formatting
                         |                     |
                         |                     +-- cljfmt-config (TTLキャッシュ)
                         |                     |      |
                         |                     |      +-- resolve-cljfmt-config
                         |                     |             |
                         |                     |             +-- cljfmt.config/default-config
                         |                     |             +-- extract-style-indent-metadata
                         |                     |             +-- resolve-user-cljfmt-config
                         |                     |
                         |                     +-- cljfmt/reformat-string
                         |
                         +-- edit->summary
                         |
                         +-- [dry?] edits->diff-string
                         |             |
                         |             +-- diff/unified-diff
                         |             +-- diff/colorize-diff
                         |
                         +-- [!dry?] apply-workspace-edit-summary!
```

### データフロー図

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

CLIオプション ────────▶ parse ────────────────────────▶ options map
     |                                                      |
     v                                                      v
project-root ─────────▶ analyze! ─────────────────────▶ db (analysis)
     |                                                      |
     v                                                      v
namespace/filenames ──▶ options->uris ────────────────▶ [uri ...]
     |                                                      |
     v                                                      v
uri ──────────────────▶ force-get-document-text ──────▶ text
     |                                                      |
     v                                                      v
db.analysis ──────────▶ extract-style-indent-metadata ─▶ {:indents ...}
     |                                                      |
     v                                                      v
.cljfmt.edn ──────────▶ resolve-user-cljfmt-config ───▶ user config
     |                                                      |
     v                                                      v
configs ──────────────▶ merge-configs ────────────────▶ final cljfmt config
     |                                                      |
     v                                                      v
text + config ────────▶ cljfmt/reformat-string ───────▶ new-text
     |                                                      |
     v                                                      v
old/new text ─────────▶ unified-diff ─────────────────▶ diff string
                                                            |
                                                            v
                       colorize-diff ─────────────────▶ stdout
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| main.clj | `cli/src/clojure_lsp/main.clj` | ソース | CLIエントリーポイント |
| api.clj | `lib/src/clojure_lsp/api.clj` | ソース | 公開API定義 |
| internal_api.clj | `lib/src/clojure_lsp/internal_api.clj` | ソース | 内部API実装 |
| format.clj | `lib/src/clojure_lsp/feature/format.clj` | ソース | format機能コア |
| handlers.clj | `lib/src/clojure_lsp/handlers.clj` | ソース | LSPハンドラ |
| diff.clj | `lib/src/clojure_lsp/diff.clj` | ソース | diff生成 |
| shared.clj | `lib/src/clojure_lsp/shared.clj` | ソース | 共通ユーティリティ |
| settings.clj | `lib/src/clojure_lsp/settings.clj` | ソース | 設定管理 |
| format_test.clj | `lib/test/clojure_lsp/feature/format_test.clj` | テスト | format機能テスト |
| format_test.clj | `cli/integration-test/integration/api/format_test.clj` | テスト | format API統合テスト |
