# 機能設計書 12-Thread First All

## 概要

本ドキュメントは、clojure-lspのThread First All機能の設計を記述する。この機能はネストされたClojure式を`->`（thread-first）マクロ形式に完全に変換するリファクタリング機能である。

### 本機能の処理概要

Thread First All機能は、ネストされた関数呼び出しを`->`マクロを使用した読みやすい形式に変換する。

**業務上の目的・背景**：Clojureコードでは、関数の結果を次の関数の第一引数として渡すパターンが頻繁に使用される。深くネストされた式は読みにくく、保守性が低下する。`->`マクロを使用することで、データの流れを上から下への線形な形式で表現でき、コードの可読性が大幅に向上する。

**機能の利用シーン**：
- ネストされた関数呼び出しを読みやすく整形したい場合
- 複数の変換処理をパイプライン形式で表現したい場合
- コードレビューで指摘された深いネストを解消したい場合
- 既存コードのリファクタリング時

**主要な処理内容**：
1. カーソル位置の式がスレッド可能かどうかを判定
2. ネストされた式を再帰的に展開
3. 各関数呼び出しの第一引数を抽出して前段に移動
4. 単一引数の括弧を設定に応じて削除
5. 適切なインデントで`->`形式に再構築

**関連システム・外部連携**：rewrite-cljライブラリを使用してASTを操作する。

**権限による制御**：特になし。すべてのユーザーが利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| N/A | エディタ | 主画面 | LSPクライアント（VS Code等）からコードアクションまたはコマンドとして実行 |

## 機能種別

リファクタリング / コード変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | 対象ファイルのURI | file://形式のURI |
| row | integer | Yes | カーソル位置の行番号（1-indexed） | 正の整数 |
| col | integer | Yes | カーソル位置の列番号（1-indexed） | 正の整数 |

### 入力データソース

- LSPクライアントからのtextDocument/codeActionリクエスト
- LSPクライアントからのworkspace/executeCommandリクエスト（thread-first-allコマンド）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| range | Range | 編集対象の範囲（変換対象の式全体） |
| newText | string | thread-first形式に変換後のテキスト |

### 出力先

- LSPクライアントへWorkspaceEditとして返却

## 処理フロー

### 処理シーケンス

```
1. スレッド可能性の判定
   └─ can-thread?関数でリスト形式かつ有効な式かを確認

2. 初回のスレッド処理
   └─ thread-sym関数で最初の引数を抽出

3. 再帰的なスレッド処理
   └─ ネストされた式がある限りthread-symを繰り返し適用

4. 単一引数の括弧削除（設定による）
   └─ :keep-parens-when-threading?がfalseの場合、括弧を削除

5. インデント調整
   └─ 各行を適切にインデント

6. 編集結果の生成
   └─ 範囲とloc（編集後のジッパー）を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[can-thread?判定]
    B --> C{スレッド可能?}
    C -->|No| D[nil返却]
    C -->|Yes| E[thread-sym実行]
    E --> F[第一引数を抽出]
    F --> G[->形式でラップ]
    G --> H{ネストされた式あり?}
    H -->|Yes| I[次の式をthread-sym]
    I --> H
    H -->|No| J[単一引数括弧削除判定]
    J --> K[インデント調整]
    K --> L[編集結果生成]
    L --> M[終了]
    D --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | スレッド不可シンボル | defn, def, ns, :require, :import, when, if, comment等はスレッド対象外 | thread-invalid-symbolsに含まれる |
| BR-02 | 第一引数抽出 | ネストされた式の第一引数（関数名の次）を抽出して前段に移動 | リスト形式の式 |
| BR-03 | 単一引数括弧削除 | 引数が1つの場合、設定により括弧を削除 | :keep-parens-when-threading?がfalse |
| BR-04 | インデント計算 | ->シンボルの長さ+列位置に基づいてインデント | 常に適用 |
| BR-05 | 再帰的展開 | can-thread-list?がtrueである限り再帰的に展開 | ネストされた式が存在 |

### 計算ロジック

- インデント幅: `(+ (count (str sym)) (:col top-meta))` で計算
- 第一引数の位置: `->` の場合は `z/right`、`->>` の場合は `(comp z/rightmost z/right)`

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

本機能はデータベース（db）を読み取り専用で使用する。

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 設定取得 | :settings | SELECT | :keep-parens-when-threading?設定を参照 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | nil返却 | スレッド不可能な式 | nilを返却（何もしない） |
| N/A | 空リスト | 有効な変換対象がない | 空リストを返却 |

### リトライ仕様

特になし。同期的に1回のみ実行。

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

- クライアントに編集結果を返却するのみで、サーバー側でのトランザクション管理は不要

## パフォーマンス要件

- 再帰的な処理だが、通常のネスト深度（~10レベル）では即時完了
- 極端に深いネストでもスタックオーバーフローしないようloop/recurを使用

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

- ファイルシステムへの直接書き込みは行わない（LSP経由でクライアントに委譲）

## 備考

- 変換例:
  - 入力: `(assoc (dissoc (update m :xs reverse) :bye) :hello :world)`
  - 出力:
    ```clojure
    (-> m
        (update :xs reverse)
        (dissoc :bye)
        (assoc :hello :world))
    ```
- `:keep-parens-when-threading?`設定がtrueの場合、単一引数でも括弧を維持

---

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

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

### 推奨読解順序

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

スレッド処理で使用される定数と設定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | thread-invalid-symbols定数（110-112行目） |
| 1-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | thread-first-symbols, thread-last-symbols（158-162行目） |

**読解のコツ**: thread-invalid-symbolsはスレッド処理の対象外となるシンボルのセットである。defn, def, ns等の定義形式や、when, if等の制御構文が含まれる。

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

処理の起点となる関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | command.clj | `lib/src/clojure_lsp/feature/command.clj` | run-command :thread-first-all（110-111行目） |
| 2-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | thread-first-all関数（150-152行目） |

**主要処理フロー**:
1. **150行目**: `thread-first-all`関数定義
2. **152行目**: `thread-all`を`'->` シンボルで呼び出し

#### Step 3: thread-all関数を理解する

全展開の核となるループ処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | thread-all関数（138-148行目） |

**主要処理フロー**:
- **140行目**: `can-thread?`で変換可能性を確認
- **141行目**: リスト形式に正規化
- **143行目**: 初回の`thread-sym`呼び出し
- **144-148行目**: `loop`で再帰的に`thread-sym`を適用

#### Step 4: thread-sym関数を理解する

個別のスレッド変換ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | thread-sym関数（69-108行目） |

**主要処理フロー**:
- **71行目**: `:keep-parens-when-threading?`設定取得
- **72行目**: 移動方向の決定（`->`は`z/right`、`->>`は`z/rightmost`）
- **73-74行目**: 第一引数の位置取得
- **82-105行目**: スレッド済みかどうかで処理分岐

#### Step 5: can-thread?関数を理解する

変換可能性の判定ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | can-thread?関数（120-126行目） |
| 5-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | can-thread-list?関数（114-118行目） |

**主要処理フロー**:
- **115行目**: ホワイトスペースをスキップ
- **116-118行目**: リスト形式かつthread-invalid-symbolsに含まれないことを確認

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

```
handlers/execute-command
    │
    └─ f.command/call-command
           │
           └─ run-command :thread-first-all
                  │
                  └─ r.transform/thread-first-all
                         │
                         └─ thread-all (sym = '->)
                                │
                                ├─ can-thread?
                                │      └─ can-thread-list?
                                │
                                └─ loop
                                       │
                                       └─ thread-sym
                                              │
                                              ├─ z/down, z/right (引数抽出)
                                              │
                                              ├─ edit/single-child?
                                              │      └─ edit/raise (括弧削除)
                                              │
                                              └─ edit/wrap-around :list
```

### データフロー図

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

zloc ─────────────────┬─> can-thread? ────────────> boolean
                      │
                      └─> thread-all
                              │
                              ├─> thread-sym (1回目)
                              │       │
                              │       ├─> first-node抽出
                              │       └─> ->形式でラップ
                              │
                              └─> loop (再帰)
                                      │
                                      └─> thread-sym (N回目)
                                              │
                                              └─────────────> [{:range :loc}]
                                                                   │
                                                                   └─> WorkspaceEdit
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | ソース | スレッド変換のメインロジック |
| command.clj | `lib/src/clojure_lsp/feature/command.clj` | ソース | コマンドディスパッチャー |
| code_actions.clj | `lib/src/clojure_lsp/feature/code_actions.clj` | ソース | コードアクション提供 |
| edit.clj | `lib/src/clojure_lsp/refactor/edit.clj` | ソース | ジッパー操作ユーティリティ |
| transform_test.clj | `lib/test/clojure_lsp/refactor/transform_test.clj` | テスト | 単体テスト |
