# 機能設計書 20-Defへ抽出

## 概要

本ドキュメントは、clojure-lspのDefへ抽出（extract-to-def）機能の設計を記述する。この機能は選択された式を新しいプライベートdefとして抽出し、元の位置をdefシンボルで置き換えるリファクタリング機能である。

### 本機能の処理概要

Defへ抽出機能は、コードの一部を独立したトップレベルの定義として切り出す。

**業務上の目的・背景**：Clojure開発において、繰り返し使用される値や設定値を定数として定義することは一般的なパターンである。本機能により、開発者は特定の式をトップレベルのdefとして抽出でき、コードの再利用性と保守性が向上する。

**機能の利用シーン**：
- 繰り返し使用される値を定数化したい場合
- 設定値をトップレベルに移動したい場合
- 複雑な式に名前を付けて可読性を向上させたい場合
- テストのためにモック可能な定義にしたい場合

**主要な処理内容**：
1. カーソル位置の式を特定
2. 新しいdef形式（^:private付き）を生成
3. 現在のトップレベルフォームの直前に配置
4. 元の位置をdefシンボルで置き換え

**関連システム・外部連携**：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） | 正の整数 |
| def-name | string | No | 新しいdef名（省略時は"new-value"） | 有効なClojureシンボル |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| range | Range | 編集対象の範囲（複数） |
| newText | string | 新しいdef定義と置換後のシンボル |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. 対象式の特定
   └─ extract-to-def-paramsで式とトップレベルフォームを取得

2. def形式の生成
   └─ "(def ^:private name\n  expr)" 形式で生成

3. シンボル式の生成
   └─ 元の位置を置き換えるシンボル

4. 配置位置の計算
   └─ prepend-preserving-commentでコメントを保持

5. 編集結果の生成
   └─ def追加と元位置の置換を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[extract-to-def-params]
    B --> C{パラメータ取得成功?}
    C -->|No| D[nil返却]
    C -->|Yes| E[expr-nodeとexpr-metaを取得]
    E --> F[def-name決定]
    F --> G[expr-edit生成]
    G --> H[def-loc生成]
    H --> I[prepend-preserving-comment]
    I --> J[編集結果生成]
    J --> K[終了]
    D --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | プライベート定義 | 抽出されるdefは常に^:privateメタデータ付き | 常に |
| BR-02 | デフォルト名 | def-nameが省略された場合は"new-value"を使用 | def-name = nil |
| BR-03 | 配置位置 | 現在のトップレベルフォームの直前に配置 | 常に |
| BR-04 | コメント保持 | トップレベルフォーム前のコメントは保持 | prepend-preserving-comment |

### 計算ロジック

- 配置行: 前のsexpr-ableの終端行 + 1、または現在のフォームの開始行

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

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

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

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

## エラー処理

### エラーケース一覧

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

### リトライ仕様

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

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

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

## パフォーマンス要件

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

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

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

## 備考

- 変換例:
  - 入力:
    ```clojure
    (defn foo []
      (println "hello"))
    ```
  - 選択: `"hello"`
  - 出力:
    ```clojure
    (def ^:private new-value
      "hello")

    (defn foo []
      (println new-value))
    ```
- extract-function（No.16）との違い:
  - extract-function: 関数として抽出（引数を持てる）
  - extract-to-def: 値として抽出（定数のみ）

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | command.clj | `lib/src/clojure_lsp/feature/command.clj` | run-command :extract-to-def（83-84行目） |
| 1-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | extract-to-def関数（762-774行目） |

**主要処理フロー**:
1. **763行目**: `extract-to-def-params`でパラメータを取得
2. **764-765行目**: expr-nodeとexpr-metaを取得
3. **767行目**: def-nameを決定（デフォルトは"new-value"）
4. **768行目**: expr-editを生成
5. **769-771行目**: def-locを生成
6. **772-774行目**: 結果を返却

#### Step 2: パラメータ抽出を理解する

extract-to-def-params関数のロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | extract-to-def-params関数（750-757行目） |

**主要処理フロー**:
- **752-753行目**: `z/skip-whitespace`で対象式を取得
- **755行目**: `edit/to-top`でトップレベルフォームを取得
- **756-757行目**: `{:zloc zloc :form-loc form-loc}`を返却

#### Step 3: def形式の生成を理解する

def-loc生成のロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | extract-to-def関数のdef-loc生成部分（769-771行目） |

**主要処理フロー**:
- **769行目**: `format`で"(def ^:private %s\n  )"形式の文字列を生成
- **770行目**: `z/of-string`でジッパーに変換
- **771行目**: `z/append-child*`でexpr-nodeを追加

#### Step 4: 配置処理を理解する

prepend-preserving-comment関数のロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | prepend-preserving-comment関数（430-452行目） |

**主要処理フロー**:
- **434-440行目**: 配置行を計算（前のsexpr-ableの終端または現在の開始）
- **447-450行目**: 改行を挿入してdef-locを配置

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

```
handlers/execute-command
    │
    └─ f.command/call-command
           │
           └─ run-command :extract-to-def
                  │
                  └─ r.transform/extract-to-def
                         │
                         ├─ extract-to-def-params
                         │      ├─ z/skip-whitespace
                         │      └─ edit/to-top
                         │
                         ├─ z/node (expr-node取得)
                         │
                         ├─ z/of-string ("(def ^:private ...)")
                         │
                         ├─ z/append-child* (expr-node追加)
                         │
                         └─ prepend-preserving-comment
                                │
                                ├─ z/find-next (前のsexpr探索)
                                │
                                └─ z/insert-newline-left/right
```

### データフロー図

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

zloc ─────────────────┬─> extract-to-def-params ──> {:zloc :form-loc}
                      │
def-name (optional) ──┼─> or "new-value" ─────────> def-name
                      │
zloc ─────────────────┼─> z/node ─────────────────> expr-node
                      │
def-name, expr-node ──┴─> format + z/of-string ───> def-loc
                              │
                              └─> z/append-child* (expr追加)
                                      │
form-loc, def-loc ────────────────────┴─> prepend-preserving-comment
                                              │
                                              └─────────────> [{:range :loc}, ...]
                                                                   │
                                                                   └─> WorkspaceEdit
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | ソース | Defへ抽出のメインロジック |
| 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` | テスト | 単体テスト |
