# 機能設計書 17-Let導入

## 概要

本ドキュメントは、clojure-lspのLet導入（introduce-let）機能の設計を記述する。この機能はカーソル位置の式を新しいletバインディングに変換するリファクタリング機能である。

### 本機能の処理概要

Let導入機能は、式を新しいletフォームでラップし、その式をバインディングとして定義する。

**業務上の目的・背景**：Clojure開発において、複雑な式に名前を付けて可読性を向上させたり、同じ計算を複数回使用する際に中間結果を保持したりすることは一般的なパターンである。本機能により、手動でletを記述する手間を省き、素早くletバインディングを導入できる。

**機能の利用シーン**：
- 複雑な式に意味のある名前を付けたい場合
- 同じ計算結果を複数箇所で使用したい場合
- デバッグのために中間結果を確認したい場合
- コードの意図を明確にしたい場合

**主要な処理内容**：
1. カーソル位置の式を特定
2. 式をletフォームでラップ
3. 新しいバインディング名を追加
4. 既存のletがある場合は結合（join-let）
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） | 正の整数 |
| binding-name | string | Yes | 新しいバインディング名 | 有効なClojureシンボル |

### 入力データソース

- LSPクライアントからのworkspace/executeCommandリクエスト（introduce-letコマンド）
- コードアクションから実行

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| range | Range | 編集対象の範囲 |
| newText | string | letフォームを含む新しいテキスト |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. 対象式の特定
   └─ z/skip-whitespaceでカーソル位置の式を取得

2. letフォームでラップ
   └─ edit/wrap-around :listで囲む

3. let構造の構築
   └─ 'let、バインディングベクター、ボディを追加

4. 既存letとの結合（該当する場合）
   └─ edit/join-letで外側のletと結合

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

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[z/skip-whitespaceで式を特定]
    B --> C{式が存在?}
    C -->|No| D[nil返却]
    C -->|Yes| E[edit/wrap-around :list]
    E --> F['let シンボル追加]
    F --> G[改行とインデント追加]
    G --> H[バインディングシンボル追加]
    H --> I[バインディングベクターでラップ]
    I --> J[edit/join-let呼び出し]
    J --> K[編集結果生成]
    K --> L[終了]
    D --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 式のラップ | 対象式をletフォームの最初のバインディングとボディに配置 | 常に |
| BR-02 | インデント | 元の式の列位置に基づいてインデント | 常に |
| BR-03 | 既存let結合 | 直上にletがある場合は結合してネストを避ける | parent-let? = true |
| BR-04 | ホワイトスペーススキップ | カーソルがホワイトスペース上の場合は右または上にスキップ | z/skip-whitespace |

### 計算ロジック

- インデント幅: `(:col (meta (z/node zloc)))` で元の式の列位置を取得

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

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | nil返却 | 対象式が見つからない | nilを返却（何もしない） |
| N/A | nil返却 | トップレベルでの実行 | nilを返却 |

### リトライ仕様

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

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

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

## パフォーマンス要件

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

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

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

## 備考

- 変換例:
  - 入力:
    ```clojure
    (inc a)
    ```
  - 出力:
    ```clojure
    (let [b (inc a)]
      b)
    ```
- 既存letへの結合例:
  - 入力:
    ```clojure
    (let [a 1]
      (inc a))
    ```
  - 選択: `(inc a)`
  - 出力:
    ```clojure
    (let [a 1
          b (inc a)]
      b)
    ```

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | command.clj | `lib/src/clojure_lsp/feature/command.clj` | run-command :introduce-let（101-102行目） |
| 1-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | introduce-let関数（273-294行目） |

**主要処理フロー**:
1. **276-277行目**: `z/skip-whitespace`で対象式を取得
2. **278行目**: シンボル化
3. **281行目**: `edit/wrap-around :list`でラップ
4. **282-290行目**: let構造を構築
5. **292行目**: `edit/join-let`で既存letと結合

#### Step 2: let構造の構築を理解する

letフォームの組み立てロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | introduce-let関数の本体（281-292行目） |

**主要処理フロー**:
- **281行目**: `edit/wrap-around :list`で式をリストで囲む
- **282行目**: `z/insert-child 'let`でletシンボルを追加
- **283-284行目**: 改行とインデントを追加
- **286行目**: バインディングシンボルをボディに追加
- **287行目**: `z/down`でリストに入る
- **288行目**: `z/right`でletをスキップ
- **289行目**: `edit/wrap-around :vector`でベクターでラップ
- **290行目**: バインディングシンボルを追加

#### Step 3: join-let関数を理解する

既存letとの結合ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit.clj | `lib/src/clojure_lsp/refactor/edit.clj` | join-let関数（178-199行目） |

**主要処理フロー**:
- **181行目**: `parent-let?`で外側にletがあるか確認
- **182行目**: バインディングノードを取得
- **183-198行目**: 内側のletを削除して外側に結合

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

```
handlers/execute-command
    │
    └─ f.command/call-command
           │
           └─ run-command :introduce-let
                  │
                  └─ r.transform/introduce-let
                         │
                         ├─ z/skip-whitespace
                         │
                         ├─ edit/wrap-around :list
                         │
                         ├─ z/insert-child ('let)
                         │
                         ├─ z/append-child* (改行、インデント)
                         │
                         ├─ edit/wrap-around :vector
                         │
                         └─ edit/join-let
                                │
                                ├─ parent-let?
                                │
                                └─ z/splice, z/append-child等
```

### データフロー図

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

zloc ─────────────────┬─> z/skip-whitespace ──> 対象式
                      │
binding-name ─────────┼─> symbol変換 ──────────> シンボル
                      │
対象式 + シンボル ────┴─> edit/wrap-around ───> letフォーム
                              │
                              ├─> 'let追加
                              ├─> ベクターラップ
                              └─> バインディング追加
                                      │
                                      └─> edit/join-let
                                              │
                                              └─────────────> [{:range :loc}]
                                                                   │
                                                                   └─> WorkspaceEdit
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | ソース | Let導入のメインロジック |
| 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` | ソース | join-let関数 |
| transform_test.clj | `lib/test/clojure_lsp/refactor/transform_test.clj` | テスト | 単体テスト |
