# 機能設計書 10-リネーム準備

## 概要

本ドキュメントは、Clojure LSP サーバーにおけるリネーム準備機能の設計仕様を記載する。この機能は LSP プロトコルの `textDocument/prepareRename` リクエストに対応し、リネーム操作の可否を事前チェックする。

### 本機能の処理概要

**業務上の目的・背景**：リネーム操作を実行する前に、その操作が成功するかどうかを事前に確認することは、ユーザー体験の向上に重要である。本機能は、カーソル位置のシンボルがリネーム可能かどうかを判定し、可能な場合はリネーム対象の範囲を返却する。これにより、エディタはリネーム不可能な位置でのリネームダイアログ表示を防ぎ、また正確なシンボル範囲をハイライト表示できる。

**機能の利用シーン**：開発者がシンボル上で「名前の変更」コマンドを実行しようとした際、エディタが本機能を呼び出す。リネーム可能な場合はシンボルの範囲が返却され、エディタはその範囲をハイライトしてリネームダイアログを表示する。リネーム不可能な場合はエラーが返却され、エディタはエラーメッセージを表示する。

**主要な処理内容**：
1. カーソル位置のシンボル要素を特定
2. シンボルの定義と参照の存在を確認
3. 各種リネーム可否条件をチェック
4. 可能な場合はシンボルの範囲を返却

**関連システム・外部連携**：特になし

**権限による制御**：本機能に権限による実行制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 本プロジェクトは LSP サーバー実装であり、UI 画面は存在しない。エディタのリネーム機能の事前チェックから呼び出される。 |

## 機能種別

データ検索処理 / バリデーション処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| text-document.uri | string | Yes | 対象ドキュメントの URI | 有効な file:// URI |
| position.line | integer | Yes | カーソル行位置（0-indexed） | 0 以上 |
| position.character | integer | Yes | カーソル列位置（0-indexed） | 0 以上 |

### 入力データソース

- clj-kondo による静的解析結果（db の :analysis）
- クライアント機能情報（:client-capabilities）
- ソースパス設定（:source-paths）

## 出力仕様

### 出力データ（成功時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| start.line | integer | シンボル開始行（0-indexed） |
| start.character | integer | シンボル開始列（0-indexed） |
| end.line | integer | シンボル終了行（0-indexed） |
| end.character | integer | シンボル終了列（0-indexed） |

### 出力データ（失敗時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| error.code | keyword | エラーコード（:invalid-params） |
| error.message | string | エラーメッセージ |

### 出力先

LSP プロトコルを通じてエディタクライアントへ Range または ResponseError として返却

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ handlers/prepare-rename が textDocument/prepareRename を受信

2. 解析待ち
   └─ process-after-changes で解析完了を待機

3. 要素特定
   └─ q/find-element-under-cursor でカーソル下の要素を取得

4. リネーム可否判定
   └─ rename-status で各種条件をチェック

5. レスポンス返却
   └─ 成功: シンボルの Range を返却
   └─ 失敗: ResponseError を返却
```

### フローチャート

```mermaid
flowchart TD
    A[prepareRename リクエスト受信] --> B[process-after-changes]
    B --> C[find-element-under-cursor]
    C --> D{要素あり?}
    D -->|No| E[error: 要素なし]
    D -->|Yes| F[rename-status]
    F --> G{定義あり?}
    G -->|No| H[error: 定義なし]
    G -->|Yes| I{参照あり?}
    I -->|No| J[error: 参照なし]
    I -->|Yes| K{名前空間?}
    K -->|Yes| L{ソースパス有効?}
    K -->|No| M{キーワード?}
    L -->|No| N[error: ソースパス不正]
    L -->|Yes| O{クライアント対応?}
    M -->|Yes| P{名前空間修飾?}
    M -->|No| Q[->range 返却]
    O -->|No| R[error: クライアント非対応]
    O -->|Yes| S{単一定義?}
    P -->|No| T[error: 非修飾キーワード]
    P -->|Yes| Q
    S -->|No| U[error: 複数定義]
    S -->|Yes| Q
    Q --> V[Range 返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 要素必須 | カーソル位置に要素がなければエラー | 全リクエスト |
| BR-02 | 定義必須 | 定義が見つからない場合はエラー | 全リクエスト |
| BR-03 | 参照必須 | 参照が見つからない場合はエラー | 全リクエスト |
| BR-04 | ソースパス必須 | 名前空間リネームには有効なソースパスが必要 | 名前空間 |
| BR-05 | クライアント対応 | ファイル変更はクライアントの documentChanges 対応が必要 | 名前空間 |
| BR-06 | 単一定義 | 名前空間が複数ファイルで定義されている場合はエラー | 名前空間 |
| BR-07 | 名前空間キーワードのみ | 非名前空間修飾キーワードのリネームは不可 | キーワード |

### 計算ロジック

- 範囲計算: shared/->range により element から LSP Range を生成

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 要素検索 | db :analysis | SELECT | カーソル位置の要素を検索 |
| 参照検索 | db :analysis | SELECT | 全参照を取得 |
| 定義検索 | db :analysis | SELECT | 定義を取得 |

### テーブル別操作詳細

#### db :analysis

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 各バケット | uri, row, col で要素特定 | 要素検索 |
| SELECT | 各バケット | 要素種別に応じた参照検索 | 参照検索 |
| SELECT | 各バケット | 要素種別に応じた定義検索 | 定義検索 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| :invalid-params | 要素なし | カーソル位置に要素がない | "Can't rename - no element found." |
| :invalid-params | 定義なし | 定義が見つからない | "Can't rename - no definition found." |
| :invalid-params | 参照なし | 参照が見つからない | "Can't rename - no other references found." |
| :invalid-params | ソースパス不正 | 名前空間リネームでソースパスが不正 | "Can't rename - invalid source-paths." |
| :invalid-params | クライアント非対応 | ファイル変更をクライアントがサポートしない | "Can't rename - client does not support file renames." |
| :invalid-params | 複数定義 | 名前空間が複数ファイルで定義 | "Can't rename - namespace is defined in multiple files." |
| :invalid-params | 非修飾キーワード | 名前空間修飾されていないキーワード | "Can't rename - only namespaced keywords can be renamed." |

### リトライ仕様

リトライは行わない。

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

本機能は読み取り専用であり、トランザクション管理は不要。

## パフォーマンス要件

- 応答時間: 通常 100ms 以内
- 解析待ちが発生する可能性あり（バックオフ付き）

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

- 外部ネットワーク通信なし
- 読み取り専用の操作

## 備考

- リネーム機能（textDocument/rename）と同じ rename-status 関数を使用
- 解析完了を待機してから判定を行う（process-after-changes）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | rename.clj | `lib/src/clojure_lsp/feature/rename.clj` | rename-status の戻り値構造（224-267行目）を確認。:result, :references, :definition, :source-path を含む |

**読解のコツ**: 成功時は {:result :success ...}、失敗時は {:error {...}} の形式で返却される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | handlers.clj | `lib/src/clojure_lsp/handlers.clj` | prepare-rename 関数（275-280行目）が LSP リクエストのエントリーポイント |

**主要処理フロー**:
1. **276-278行目**: process-after-changes で解析完了を待機
2. **279行目**: position を row, col に変換
3. **280行目**: f.rename/prepare-rename を呼び出し

#### Step 3: リネーム準備のメイン関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | rename.clj | `lib/src/clojure_lsp/feature/rename.clj` | prepare-rename 関数（273-281行目）でリネーム準備のロジックを確認 |

**主要処理フロー**:
- **275行目**: find-element-under-cursor で要素取得
- **276-277行目**: 要素がなければ error-no-element 返却
- **278行目**: rename-status で可否判定
- **280行目**: エラーなら result（エラー）を返却
- **281行目**: 成功なら shared/->range で範囲を返却

#### Step 4: リネーム可否判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | rename.clj | `lib/src/clojure_lsp/feature/rename.clj` | rename-status 関数（224-267行目）で各種条件チェックを確認 |

**主要処理フロー**:
- **225-226行目**: find-references と find-definition を取得
- **232-235行目**: 定義が見つからない場合のエラー
- **237-239行目**: 参照が見つからない場合のエラー
- **241-245行目**: 名前空間リネームでソースパス不正の場合のエラー
- **247-250行目**: クライアントが documentChanges 非対応の場合のエラー
- **252-256行目**: 名前空間が複数定義の場合のエラー
- **258-261行目**: 非名前空間修飾キーワードの場合のエラー
- **263-267行目**: 全条件パス時は :result :success を返却

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

```
handlers/prepare-rename
    │
    ├─ process-after-changes
    │      └─ wait-for-changes
    │
    ├─ shared/position->row-col
    │
    └─ f.rename/prepare-rename
           │
           ├─ q/find-element-under-cursor
           │
           └─ rename-status
                  │
                  ├─ q/find-references
                  ├─ q/find-definition
                  ├─ settings/get [:source-paths]
                  ├─ shared/uri->source-path
                  ├─ client-capabilities チェック
                  └─ q/find-all-project-namespace-definitions
```

### データフロー図

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

textDocument/prepareRename ──▶ handlers/prepare-rename ──▶ Range | Error
  - uri                          │
  - position (line, char)        ▼
                            process-after-changes
                                 │
                                 ▼
                            prepare-rename
                                 │
                                 ├──▶ find-element-under-cursor
                                 │      └─ db :analysis[uri]
                                 │
                                 └──▶ rename-status
                                        ├─ find-references
                                        ├─ find-definition
                                        └─ 各種エラーチェック
                                               │
                                               ├──▶ [エラー]
                                               │      └─ ResponseError
                                               │
                                               └──▶ [成功]
                                                      └─ Range {start, end}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| rename.clj | `lib/src/clojure_lsp/feature/rename.clj` | ソース | リネーム準備のメインロジック |
| handlers.clj | `lib/src/clojure_lsp/handlers.clj` | ソース | LSP リクエストハンドラー |
| queries.clj | `lib/src/clojure_lsp/queries.clj` | ソース | 要素・参照・定義検索 |
| shared.clj | `lib/src/clojure_lsp/shared.clj` | ソース | 範囲変換ユーティリティ |
| settings.clj | `lib/src/clojure_lsp/settings.clj` | ソース | 設定取得 |
| rename_test.clj | `lib/test/clojure_lsp/feature/rename_test.clj` | テスト | ユニットテスト |
