# 機能設計書 26-関数昇格

## 概要

本ドキュメントは、clojure-lspの「関数昇格」機能（promote-fnコマンド）の設計仕様を定義する。この機能は、無名関数（#()やfn）をより上位の形式に昇格させるリファクタリング機能を提供する。

### 本機能の処理概要

関数昇格機能は、Clojureの無名関数を段階的に昇格させる。具体的には、リーダーマクロ形式の無名関数（#()）をfn形式に、fn形式をdefn形式に昇格させる。これにより、インラインの無名関数を名前付き関数として抽出し、コードの再利用性と可読性を向上させる。

**業務上の目的・背景**：Clojure開発では、最初は無名関数として実装した処理が、後に複数箇所で再利用したくなることがある。また、複雑になった無名関数をテスト可能な名前付き関数にしたい場合もある。この機能により、無名関数の昇格を段階的かつ安全に行うことができる。

**機能の利用シーン**：
- #()形式の無名関数を引数に名前を付けたい場合
- fn形式の無名関数をdefnとしてトップレベルに抽出したい場合
- 無名関数をテスト可能にしたい場合
- コードの可読性を向上させたい場合
- 無名関数を他の場所から再利用したい場合

**主要な処理内容**：
1. カーソル位置の関数形式を特定（#() or fn）
2. 昇格タイプを判定（:literal-to-fn or :fn-to-defn）
3. #()→fn の場合：%記法を名前付きパラメータに変換
4. fn→defn の場合：fnをdefnに変換し、トップレベルに配置
5. 外部で定義されたローカル変数を引数として追加
6. 元の場所を関数参照またはpartialで置換

**関連システム・外部連携**：LSPプロトコルを通じてエディタと連携し、コードアクションまたはコマンドとして実行される。

**権限による制御**：特に権限制御なし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 本プロジェクトはLSPサーバーであり、UI画面は存在しない。エディタのコードアクションメニューから呼び出される。 |

## 機能種別

リファクタリング（コード変換処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| zloc | zipper location | Yes | カーソル位置のzipperロケーション | 関数形式内にあること |
| uri | string | Yes | 対象ファイルのURI | 有効なURI形式 |
| db | database | Yes | LSPデータベース | - |
| fn-name | string | No | 生成する関数名 | 省略時は自動生成または既存名を使用 |

### 入力データソース

- エディタから送信されるLSPリクエスト
- ドキュメントのパース結果（rewrite-clj zipper）
- clj-kondo解析データ（ローカル変数使用箇所の検索）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| range | map | 変更対象範囲（:row, :col, :end-row, :end-col） |
| loc | zipper location | 変換後のノード |

### 出力先

LSPプロトコルを通じてエディタに返却され、テキスト編集として適用される。

## 処理フロー

### 処理シーケンス

```
1. 関数形式の特定
   └─ promote-fn-params関数で#()またはfn形式を検索
2. 昇格タイプの判定
   └─ :literal-to-fn または :fn-to-defn
3. 昇格処理の分岐
   └─ convert-literal-to-fn または convert-fn-to-defn
4. #()→fn の場合
   └─ %記法を名前付きパラメータに変換、doの追加/削除
5. fn→defn の場合
   └─ 外部ローカル変数を引数に追加、トップレベルに配置
6. 結果の返却
   └─ 複数の編集（defn追加 + 元の場所の置換）を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{関数形式?}
    B -->|No| C[nil返却]
    B -->|Yes| D{#() ?}
    D -->|Yes| E[literal-to-fn処理]
    D -->|No| F{fn ?}
    F -->|No| C
    F -->|Yes| G[fn-to-defn処理]
    E --> H[%をパラメータに変換]
    H --> I[fn形式を生成]
    G --> J[外部ローカル変数を検索]
    J --> K[defnをトップレベルに生成]
    K --> L{外部変数あり?}
    L -->|Yes| M[partialで置換]
    L -->|No| N[関数名で置換]
    I --> O[結果返却]
    M --> O
    N --> O
    O --> P[終了]
    C --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | #()→fn変換 | %をelement、%1をelement1、%&をargsに変換 | リーダーマクロ形式の場合 |
| BR-002 | do追加/削除 | #()内のdoは削除、複数式の場合はリストでラップ | #()→fn変換時 |
| BR-003 | fn→defn変換 | fnをdefn（またはdefn-）に変換 | fn形式の場合 |
| BR-004 | ローカル変数追加 | 外部で定義されたローカル変数を引数リストの先頭に追加 | fn→defn変換時 |
| BR-005 | partial使用 | 外部ローカル変数がある場合、(partial fn-name vars...)で置換 | 外部変数が存在する場合 |
| BR-006 | fn literal使用 | 外部変数があり、かつfn literal内でない場合、#(fn-name vars %...)で置換 | 単一アリティで外部変数ありの場合 |
| BR-007 | プライバシー設定 | use-metadata-for-privacy?設定に従い、defn- or ^:privateを使用 | fn→defn変換時 |

### 計算ロジック

特になし。

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

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

本機能はデータベース操作を行わない。clj-kondo解析データの読み取りのみ。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 暗黙的エラー | 関数形式でない場合 | nil返却 |
| - | 暗黙的エラー | 分割代入パラメータがある場合（#()→fn） | nil返却 |
| - | 暗黙的エラー | マルチアリティfnの場合（#()→fn） | nil返却 |

### リトライ仕様

リトライは不要。

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

該当なし（メモリ上の操作のみ）。

## パフォーマンス要件

ローカル変数の検索を含め、1秒以内に完了すること。

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

特になし。ローカルファイルの操作のみ。

## 備考

- 生成されるdefnは常にプライベート（defn-または^:private）
- fn内に既存の名前がある場合はそれを使用
- 複数編集（defn追加と元の場所の置換）を1つのトランザクションで返却

---

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

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

### 推奨読解順序

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

関数形式の判定と昇格タイプの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | promote-fn-params関数（801-805行目）で昇格タイプ判定 |

**読解のコツ**: promote-fn-paramsは:promotion（:literal-to-fnまたは:fn-to-defn）と:zlocを含むマップを返す。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | command.clj | `lib/src/clojure_lsp/feature/command.clj` | promote-fnコマンドの定義（59-60行目） |
| 2-2 | code_actions.clj | `lib/src/clojure_lsp/feature/code_actions.clj` | promote-fn-actionでUIアクション生成（203-211行目） |

**主要処理フロー**:
1. **59-60行目**: run-commandのpromote-fnメソッドがr.transform/promote-fnを呼び出し

#### Step 3: 変換処理の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | can-promote-fn?（810-811行目）で可能性判定 |
| 3-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | convert-literal-to-fn（813-884行目）で#()→fn変換 |
| 3-3 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | convert-fn-to-defn（916-993行目）でfn→defn変換 |
| 3-4 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | promote-fn（1000-1004行目）でメイン処理 |

**主要処理フロー**:
- **782-784行目**: outer-fn-form?でfn形式判定
- **786-787行目**: outer-literal-form?で#()形式判定
- **789-799行目**: convert-fn-to-literal-paramsでfnのパラメータ解析
- **813-884行目**: convert-literal-to-fnで%記法を名前付きパラメータに変換
- **916-993行目**: convert-fn-to-defnでdefn生成とpartial/fn-literal置換

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

```
call-command (command.clj)
    │
    └─ run-command :promote-fn (command.clj:59-60)
           │
           └─ r.transform/promote-fn (transform.clj:1000-1004)
                  │
                  ├─ promote-fn-params (transform.clj:801-805)
                  │      ├─ outer-literal-form?
                  │      └─ outer-fn-form?
                  │
                  └─ case promotion
                         │
                         ├─ :literal-to-fn → convert-literal-to-fn (transform.clj:813-884)
                         │                     └─ %記法解析、fn生成
                         │
                         └─ :fn-to-defn → convert-fn-to-defn (transform.clj:916-993)
                                           ├─ q/find-local-usages-defined-outside-form
                                           ├─ prepend-preserving-comment
                                           └─ partial/fn-literal生成
```

### データフロー図

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

LSPリクエスト ───▶ zloc取得 ───▶ 関数形式判定 ───▶ 昇格タイプ判定 ───▶ 変換処理 ───▶ TextEdit
(uri, pos)                                              │
                                                        ├─ #()→fn: %記法変換
                                                        └─ fn→defn: defn生成 + 置換
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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` | ソース | コードアクションUI生成 |
| queries.clj | `lib/src/clojure_lsp/queries.clj` | ソース | ローカル変数使用箇所の検索 |
| transform_test.clj | `lib/test/clojure_lsp/refactor/transform_test.clj` | テスト | 関数昇格のテスト |
