# 機能設計書 27-関数降格

## 概要

本ドキュメントは、clojure-lspの「関数降格」機能（demote-fnコマンド）の設計仕様を定義する。この機能は、fn形式の関数を#()形式（リーダーマクロ形式）に変換するリファクタリング機能を提供する。

### 本機能の処理概要

関数降格機能は、fn形式の無名関数をより簡潔な#()形式（リーダーマクロ形式）に変換する。これは関数昇格（promote-fn）の逆操作であり、シンプルな無名関数をより簡潔に記述したい場合に使用する。

**業務上の目的・背景**：Clojureでは、単純な無名関数は#()形式で簡潔に記述できる。fn形式で書かれた無名関数が実際には単純な処理であり、#()形式に変換することでコードが読みやすくなる場合がある。この機能により、fn形式から#()形式への変換を効率的に行える。

**機能の利用シーン**：
- 単純なfn形式の無名関数を#()形式に変換してコードを簡潔にしたい場合
- mapやfilterのコールバック関数を短く記述したい場合
- コードスタイルの統一のために#()形式に変換したい場合

**主要な処理内容**：
1. カーソル位置のfn形式を特定
2. パラメータベクターを解析
3. 単一アリティで、分割代入がないことを確認
4. 名前付きパラメータを%記法に変換
5. fnノードを#()ノードに変換
6. 複数式の場合はdoを追加、単一式の場合はリストを展開

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

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

## 関連画面

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

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| zloc | zipper location | Yes | カーソル位置のzipperロケーション | fn形式内にあること |

### 入力データソース

- エディタから送信されるLSPリクエスト
- ドキュメントのパース結果（rewrite-clj zipper）

## 出力仕様

### 出力データ

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

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. fn形式の特定
   └─ convert-fn-to-literal-params関数でfn形式を検索
2. 変換可能性チェック
   └─ 単一アリティ、全パラメータがシンボルであることを確認
3. パラメータの変換
   └─ 名前付きパラメータを%記法に変換
4. ボディの変換
   └─ 複数式の場合はdoを追加、単一式はリスト展開
5. #()ノードの生成
   └─ n/fn-nodeで#()形式のノードを生成
6. 結果の返却
   └─ rangeとlocを含むマップのベクターを返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{fn形式?}
    B -->|No| C[nil返却]
    B -->|Yes| D{単一アリティ?}
    D -->|No| C
    D -->|Yes| E{全パラメータがシンボル?}
    E -->|No| C
    E -->|Yes| F[パラメータを%記法に変換]
    F --> G{複数式?}
    G -->|Yes| H[doを追加]
    G -->|No| I[リストを展開]
    H --> J[#()ノード生成]
    I --> J
    J --> K[結果返却]
    K --> L[終了]
    C --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 単一パラメータ変換 | 1つのパラメータは%に変換 | パラメータが1つの場合 |
| BR-002 | 複数パラメータ変換 | 複数パラメータは%1, %2, ...に変換 | パラメータが2つ以上の場合 |
| BR-003 | 可変引数変換 | & restは%&に変換 | 可変引数がある場合 |
| BR-004 | do追加 | 複数式の場合はdoでラップ | ボディが複数式の場合 |
| BR-005 | リスト展開 | 単一リスト式の場合は中身を展開 | ボディが単一リスト式の場合 |
| BR-006 | 分割代入拒否 | 分割代入パラメータがある場合は変換不可 | パラメータに分割代入がある場合 |
| BR-007 | マルチアリティ拒否 | マルチアリティfnは変換不可 | 複数のアリティがある場合 |

### 計算ロジック

特になし。

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

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

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

### リトライ仕様

リトライは不要。

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

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

## パフォーマンス要件

即座に完了すること（100ms以内）。

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

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

## 備考

- #()形式はネストできないため、ネストされたfn literalがある場合は変換不可
- 名前付きfn（(fn name [...] ...)）は名前を失う

---

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

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

### 推奨読解順序

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

fn形式のパラメータ解析を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | convert-fn-to-literal-params関数（789-799行目）でfnパラメータ解析 |

**読解のコツ**: convert-fn-to-literal-paramsはfnとパラメータベクターを返し、分割代入がある場合はnilを返す。

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

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

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | can-demote-fn?（807-808行目）で可能性判定 |
| 3-2 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | convert-fn-to-literal（886-914行目）で変換処理 |
| 3-3 | transform.clj | `lib/src/clojure_lsp/refactor/transform.clj` | demote-fn（995-998行目）でメイン処理 |

**主要処理フロー**:
- **789-799行目**: convert-fn-to-literal-paramsでfnとパラメータを取得
- **886-914行目**: convert-fn-to-literalで変換処理
- **887-893行目**: パラメータ位置に応じて%または%Nに変換、可変引数は%&に変換
- **897-904行目**: fnの内部を取得し、空白を除去
- **905-912行目**: 複数式の場合はdoを追加、単一式の場合は中身を展開

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

```
call-command (command.clj)
    │
    └─ run-command :demote-fn (command.clj:62-63)
           │
           └─ r.transform/demote-fn (transform.clj:995-998)
                  │
                  ├─ convert-fn-to-literal-params (transform.clj:789-799)
                  │      ├─ outer-fn-form? or edit/find-ops-up "fn"
                  │      └─ z/find-tag z/right :vector
                  │
                  └─ convert-fn-to-literal (transform.clj:886-914)
                         ├─ split-with (& の分離)
                         ├─ replace-sexprs (パラメータ置換)
                         └─ n/fn-node (#()生成)
```

### データフロー図

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

LSPリクエスト ───▶ zloc取得 ───▶ fn形式判定 ───▶ パラメータ解析 ───▶ %記法変換 ───▶ #()生成 ───▶ TextEdit
(uri, pos)                                              │
                                                        └─ do追加/展開
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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生成 |
| transform_test.clj | `lib/test/clojure_lsp/refactor/transform_test.clj` | テスト | 関数降格のテスト |
