# 機能設計書 81-フォーマット実行

## 概要

本ドキュメントは、clojure-lspのCLI（コマンドラインインターフェース）から実行するコードフォーマット機能の設計書である。cljfmtライブラリを使用してClojureソースコードを整形する。

### 本機能の処理概要

この機能は、CLIコマンド `clojure-lsp format` を通じてClojureソースファイルのコードフォーマットを実行する。cljfmtライブラリを内部で使用し、インデント、空白、改行などを標準的なスタイルに整形する。

**業務上の目的・背景**：Clojureプロジェクトにおいて、コードスタイルの一貫性を保つことは保守性向上に不可欠である。手動でのフォーマット作業は時間がかかり、人為的なミスも発生しやすい。本機能により、CI/CDパイプラインや開発ワークフローに組み込んで自動的にコードスタイルを統一できる。

**機能の利用シーン**：
- コミット前のコードスタイルチェック（dry-runモード）
- CI/CDパイプラインでのフォーマットチェック
- プロジェクト全体の一括フォーマット
- 特定の名前空間やファイルのみを対象とした部分フォーマット

**主要な処理内容**：
1. コマンドライン引数の解析（対象ファイル、dry-runフラグ、設定など）
2. プロジェクトの解析とcljfmt設定の読み込み
3. 対象ファイルの特定（名前空間指定、ファイル指定、または全ファイル）
4. 各ファイルに対するcljfmtによるフォーマット実行
5. 結果の出力（dry-runモードの場合はdiff表示、通常モードの場合はファイル更新）

**関連システム・外部連携**：
- cljfmt: Clojureコードフォーマットライブラリ
- clj-kondo: コード解析に使用

**権限による制御**：権限による制御は行わない。ファイルシステムへの書き込み権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 該当なし | 該当なし | 該当なし | CLIから実行されるため画面は存在しない |

## 機能種別

バッチ処理 / コード変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| --project-root, -p | Path | No | プロジェクトルートパス | 存在するディレクトリであること |
| --namespace, -n | Symbol | No | 対象名前空間（複数指定可） | 有効なClojure名前空間形式 |
| --filenames | String | No | 対象ファイル（カンマまたはコロン区切り） | 空白を含まないこと |
| --dry | Boolean | No | ドライランモード（デフォルト: false） | - |
| --raw | Boolean | No | 必要最小限の出力（デフォルト: false） | - |
| --settings, -s | EDN | No | 追加設定（EDN形式） | 有効なEDNマップ |
| --ns-exclude-regex | Regex | No | 除外する名前空間の正規表現 | 有効な正規表現パターン |

### 入力データソース

- ファイルシステム上のClojureソースファイル（.clj, .cljs, .cljc）
- .cljfmt.edn（プロジェクトルートまたは指定パス）
- clojure-lsp設定ファイル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result-code | Integer | 実行結果コード（0: 変更なしまたは成功、1: dry-runで変更あり） |
| message-fn | Function | 結果メッセージを生成する関数 |
| edits | Vector | 適用された編集のリスト |

### 出力先

- 標準エラー出力: 進捗メッセージ
- 標準出力: 結果メッセージ（dry-runモード時はdiff形式）
- ファイルシステム: フォーマット済みソースファイル（dry-runモードでない場合）

## 処理フロー

### 処理シーケンス

```
1. コマンドライン引数の解析
   └─ babashka.cliでオプションをパース

2. コンポーネントの初期化
   └─ CLILogger、APIProducerの生成

3. プロジェクト解析の実行
   └─ :project-namespaces-only タイプで最小限の解析

4. 対象URIの特定
   └─ namespace/filenames/全ファイルから対象を決定

5. 各ファイルのフォーマット処理
   └─ cljfmt設定の解決とフォーマット適用

6. 結果の処理
   └─ dry-run: diff出力、通常: ファイル更新
```

### フローチャート

```mermaid
flowchart TD
    A[開始: format コマンド実行] --> B[引数解析]
    B --> C[コンポーネント初期化]
    C --> D[プロジェクト解析]
    D --> E[対象URI特定]
    E --> F{対象ファイルあり?}
    F -->|No| G[Nothing to format! 出力]
    F -->|Yes| H[各ファイルをフォーマット]
    H --> I{変更あり?}
    I -->|No| G
    I -->|Yes| J{dry-run?}
    J -->|Yes| K[diff形式で出力]
    J -->|No| L[ファイル更新]
    K --> M[result-code: 1]
    L --> N[result-code: 0]
    G --> O[result-code: 0]
    M --> P[終了]
    N --> P
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-81-1 | cljfmt設定の優先順位 | デフォルト設定 < :style/indentメタデータ < .cljfmt.edn < --settings | 常時 |
| BR-81-2 | dry-runモード | dry-run時はファイルを変更せずdiff出力のみ | --dryフラグ指定時 |
| BR-81-3 | 変更検出 | フォーマット前後でテキストが同一の場合は変更なしとみなす | 常時 |

### 計算ロジック

cljfmt設定の解決:
1. cljfmt.config/default-config を基底とする
2. プロジェクト内のvar定義から :style/indent メタデータを抽出
3. .cljfmt.edn ファイルの内容をマージ
4. :alias-map が空の場合は nil に設定（cljfmtのバグ回避）

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

該当なし（ファイルシステムのみ操作）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | 引数エラー | 無効なオプション指定 | エラーメッセージを表示して終了 |
| 1 | 解析エラー | プロジェクト解析失敗 | エラーメッセージを表示して終了 |
| 1 | ファイルエラー | ファイル読み込み/書き込み失敗 | 例外をスローして終了 |

### リトライ仕様

リトライは行わない。エラー発生時は即座に終了する。

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

トランザクション管理は行わない。各ファイルは独立してフォーマットされる。フォーマット中にエラーが発生した場合、それまでに処理されたファイルの変更は維持される。

## パフォーマンス要件

- pmap を使用して並列フォーマット処理を実行
- cljfmt設定は TTL 3000ms でメモ化されキャッシュされる

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

- .cljfmt.clj ファイル読み込み時は *read-eval* を false に設定
- ファイル書き込みは指定されたプロジェクト内のファイルのみに制限

## 備考

- :style/indent メタデータのサポートにより、プロジェクト固有のインデントルールを定義可能
- 解析タイプは :project-namespaces-only をデフォルトとし、パフォーマンスを優先

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cli-spec | `cli/src/clojure_lsp/main.clj` | CLIオプションの定義と型情報 |
| 1-2 | cljfmt設定 | `lib/src/clojure_lsp/feature/format.clj` | cljfmt設定の構造とマージルール |

**読解のコツ**: `cli-spec` はbabashka.cliの仕様に従った定義。`:coerce` でデータ型変換、`:validate` でバリデーションを行う。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | main.clj | `cli/src/clojure_lsp/main.clj` | CLIのエントリーポイント、コマンドディスパッチ |

**主要処理フロー**:
1. **194-215行目**: `handle-action!` 関数でコマンドに応じた処理をディスパッチ
2. **204行目**: `"format"` コマンドで `internal-api/format!` を呼び出し

#### Step 3: internal-api層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | internal_api.clj | `lib/src/clojure_lsp/internal_api.clj` | API実装の中心、コンポーネント構築 |

**主要処理フロー**:
- **374-401行目**: `format!*` 関数 - フォーマット処理の本体
- **378行目**: `dynamic-setup-project-analysis!` でプロジェクト解析タイプを決定
- **381-387行目**: 対象URIを取得し、各ファイルに `handlers/formatting` を適用
- **389-401行目**: dry-runモードの分岐処理

#### Step 4: フォーマット実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | format.clj | `lib/src/clojure_lsp/feature/format.clj` | cljfmtを使用した実際のフォーマット処理 |

**主要処理フロー**:
- **21-33行目**: `resolve-user-cljfmt-config` - ユーザー設定の読み込み
- **105-112行目**: `resolve-cljfmt-config` - 設定のマージ処理
- **119-127行目**: `formatting` 関数 - ファイル単位のフォーマット実行

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

```
main/-main
    │
    └─ main/run!
           │
           └─ main/handle-action!
                  │
                  └─ internal-api/format!
                         │
                         ├─ internal-api/build-components
                         │
                         └─ internal-api/format!*
                                │
                                ├─ internal-api/setup-api!
                                │
                                ├─ internal-api/dynamic-setup-project-analysis!
                                │
                                ├─ internal-api/options->uris
                                │
                                └─ handlers/formatting
                                       │
                                       └─ f.format/formatting
                                              │
                                              └─ cljfmt/reformat-string
```

### データフロー図

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

CLIオプション ──────▶ parse-opts ──────▶ options マップ
                           │
                           ▼
プロジェクトファイル ──▶ analyze! ──────▶ 解析済みDB
                           │
                           ▼
.cljfmt.edn ────────▶ cljfmt-config ──▶ フォーマット設定
                           │
                           ▼
ソースファイル ─────▶ formatting ─────▶ フォーマット済みテキスト
                           │
                           ▼
                    dry-run分岐 ─────▶ diff出力 / ファイル更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| main.clj | `cli/src/clojure_lsp/main.clj` | ソース | CLIエントリーポイント |
| internal_api.clj | `lib/src/clojure_lsp/internal_api.clj` | ソース | CLI用API実装 |
| format.clj | `lib/src/clojure_lsp/feature/format.clj` | ソース | フォーマット処理実装 |
| handlers.clj | `lib/src/clojure_lsp/handlers.clj` | ソース | LSPハンドラー実装 |
| diff.clj | `lib/src/clojure_lsp/diff.clj` | ソース | diff出力生成 |
| .cljfmt.edn | プロジェクトルート | 設定 | cljfmtカスタム設定 |
