# 機能設計書 14-スレッド展開

## 概要

本ドキュメントは、clojure-lspのスレッド展開（unwind-thread）機能の設計を記述する。この機能はスレッドマクロ（`->`, `->>`, `some->`, `some->>`）を一段階展開し、元のネストされた形式に戻すリファクタリング機能である。

### 本機能の処理概要

スレッド展開機能は、スレッドマクロを使用した式を一段階ずつ元のネストされた形式に戻す。

**業務上の目的・背景**：スレッドマクロは可読性を向上させるが、特定の状況では元のネストされた形式の方が適切な場合がある。例えば、スレッドの途中に条件分岐を挿入したい場合や、一部の処理を別の関数に抽出したい場合である。本機能により、開発者は柔軟にコード構造を変更できる。

**機能の利用シーン**：
- スレッドマクロの一部を取り消したい場合
- スレッドの途中に条件分岐や例外処理を挿入したい場合
- スレッドされた式の一部を別の関数に抽出する前処理
- コード構造を変更するリファクタリング時

**主要な処理内容**：
1. カーソル位置からスレッドマクロを探索
2. スレッドの方向（first/last）を判定
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リクエスト（unwind-threadコマンド）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| range | Range | 編集対象の範囲（スレッドマクロ全体） |
| newText | string | 一段階展開後のテキスト |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. スレッドマクロの探索
   └─ can-unwind-thread?でスレッドマクロの位置とシンボルを取得

2. スレッド方向の判定
   └─ thread-first-symbolsまたはthread-last-symbolsで方向を判定

3. 値とターゲットの特定
   └─ val-loc: スレッドされる値
   └─ target-loc: 次の処理対象

4. 値の挿入
   └─ thread-firstの場合: z/insert-rightで第一引数に挿入
   └─ thread-lastの場合: z/rightmostで最後の引数に挿入

5. ターゲットのラップ（必要な場合）
   └─ リスト形式でない場合はwrap-aroundでラップ

6. スレッドの単純化
   └─ 残りが1つの場合はスレッドマクロを削除（raise）

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

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[can-unwind-thread?判定]
    B --> C{展開可能?}
    C -->|No| D[nil返却]
    C -->|Yes| E[スレッド方向判定]
    E --> F{thread-first?}
    F -->|Yes| G[z/insert-right使用]
    F -->|No| H[z/rightmost使用]
    G --> I[ターゲットがリスト?]
    H --> I
    I -->|No| J[wrap-aroundでラップ]
    I -->|Yes| K[値を挿入]
    J --> K
    K --> L{残りが1つ?}
    L -->|Yes| M[raiseでスレッド削除]
    L -->|No| N[スレッド形式維持]
    M --> O[編集結果生成]
    N --> O
    O --> P[終了]
    D --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 対応スレッドシンボル | ->, ->>, some->, some->>の4種類に対応 | thread-symbolsに含まれる |
| BR-02 | thread-first挿入位置 | ->およびsome->は第一引数位置に挿入 | thread-first-symbolsに含まれる |
| BR-03 | thread-last挿入位置 | ->>およびsome->>は最後の引数位置に挿入 | thread-last-symbolsに含まれる |
| BR-04 | リストラップ | ターゲットがリスト形式でない場合はラップ | z/tag != :list |
| BR-05 | スレッド削除 | 残りの式が1つの場合はスレッドマクロ自体を削除 | extra? = nil |

### 計算ロジック

- insert-fn: `(if (some #(string/ends-with? (name thread-sym) (str %)) thread-first-symbols) z/insert-right ...)`
- 挿入位置の判定はスレッドシンボルの末尾で判定

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

本機能はデータベースを使用しない。純粋なAST操作のみ。

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| なし | - | - | データベースアクセスなし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | nil返却 | スレッドマクロが見つからない | nilを返却（何もしない） |
| N/A | nil返却 | 値またはターゲットが見つからない | nilを返却 |

### リトライ仕様

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

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

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

## パフォーマンス要件

- 単一のAST操作であり、即時完了

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

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

## 備考

- 変換例（一段階展開）:
  - 入力:
    ```clojure
    (-> a
        b
        (c)
        d)
    ```
  - 出力（1回目）:
    ```clojure
    (-> (b a)
        (c)
        d)
    ```
  - 出力（2回目）:
    ```clojure
    (-> (c (b a))
        d)
    ```
- 完全に展開するにはunwind-all（No.15）を使用

---

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

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

### 推奨読解順序

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

スレッドシンボルの定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | thread-first-symbols（158行目）: `#{'-> 'some->}` |
| 1-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | thread-last-symbols（159行目）: `#{'->> 'some->>}` |
| 1-3 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | thread-symbols（161-162行目）: 両方の和集合 |

**読解のコツ**: thread-symbolsは展開可能な全てのスレッドマクロシンボルを含む。`some->`系も含まれる点に注意。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | command.clj | `lib/src/clojure_lsp/feature/command.clj` | run-command :unwind-thread（122-123行目） |
| 2-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | unwind-thread関数（173-199行目） |

**主要処理フロー**:
1. **175行目**: `can-unwind-thread?`で展開可能かを確認
2. **176-177行目**: val-loc（値）とtarget-loc（ターゲット）を取得
3. **179-184行目**: insert-fnを決定（first/lastで異なる）
4. **186-198行目**: 実際の展開処理

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

展開可能性の判定と情報取得を理解する。

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

**主要処理フロー**:
- **165行目**: `edit/find-ops-up`でスレッドシンボルを探索
- **166-168行目**: thread-symbolsに含まれるシンボルを取得
- **170-171行目**: `{:thread-loc :thread-sym}`を返却

#### Step 4: 展開処理の詳細を理解する

実際の展開ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | unwind-thread関数の本体（186-198行目） |

**主要処理フロー**:
- **186行目**: thread-locのz/upでスレッド式全体を取得
- **187-192行目**: z/subedit->で編集
- **189行目**: val-locを削除
- **193行目**: ターゲットがリストでない場合はwrap-around
- **195行目**: insert-fnで値を挿入
- **197行目**: 残りが1つならedit/raiseでスレッド削除

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

```
handlers/execute-command
    │
    └─ f.command/call-command
           │
           └─ run-command :unwind-thread
                  │
                  └─ r.transform/unwind-thread
                         │
                         ├─ can-unwind-thread?
                         │      │
                         │      └─ edit/find-ops-up
                         │
                         ├─ z/right (val-loc, target-loc取得)
                         │
                         ├─ insert-fn決定
                         │      └─ string/ends-with?でfirst/last判定
                         │
                         └─ z/subedit->
                                │
                                ├─ z/remove (val-loc削除)
                                ├─ edit/wrap-around (必要時)
                                ├─ insert-fn (値挿入)
                                └─ edit/raise (残り1つの場合)
```

### データフロー図

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

zloc ─────────────────┬─> can-unwind-thread? ──> {:thread-loc :thread-sym}
                      │
                      └─> unwind-thread
                              │
                              ├─> val-loc (スレッドされる値)
                              ├─> target-loc (次のターゲット)
                              │
                              ├─> insert-fn決定
                              │      │
                              │      ├─> thread-first: z/insert-right
                              │      └─> thread-last: z/rightmost + z/insert-right
                              │
                              └─> z/subedit->
                                      │
                                      ├─> remove val
                                      ├─> wrap if needed
                                      └─> insert val
                                              │
                                              └─────────────> [{:range :loc}]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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` | テスト | 単体テスト |
