# プログラム設計書 23-prepareRename

## 1. 概要

### このプログラムの目的

このプログラムは、シンボルのリネーム（名前変更）が可能かどうかを事前に確認するためのものです。ユーザーがリネーム操作を開始する前に、カーソル位置のシンボルがリネーム可能かどうかをチェックし、可能な場合は現在の名前と位置を返します。

### このプログラムの役割

- **呼び出し元**: LSPプロトコルの `textDocument/prepareRename` リクエストハンドラ
- **主な処理**: カーソル位置のシンボルがリネーム可能かどうかを判定
- **出力**: リネーム可能な場合はシンボルの位置と現在の名前、不可能な場合は null

## 2. ファイル基本情報

| 項目 | 内容 |
|-----|------|
| ファイルパス | `script/core/rename.lua` |
| ファイル名 | rename.lua |
| 種別 | ソースコード（rename.lua の一部機能） |
| 使用言語 | Lua |
| 該当行 | 327-362行目（prepareRename関数）、444-464行目（m.prepareRename関数） |
| 作成目的 | リネーム操作の事前確認を提供する |

## 3. 用語解説

このプログラムを理解するために必要な専門用語を解説します。

| 用語 | 読み方 | 意味・説明 |
|-----|--------|----------|
| PrepareRename | プリペアリネーム | リネームの準備。実際のリネーム前に可否を確認する処理 |
| Source | ソース | ASTにおける構文要素（変数、関数名などのノード） |
| Range | レンジ | テキスト内の範囲。開始位置と終了位置のペア |
| PlaceholderText | プレースホルダーテキスト | リネームダイアログに表示される現在の名前 |

### 言語固有の概念

No.22（rename）と同じファイルのため、用語解説はNo.22を参照してください。

## 4. 依存関係

### このプログラムが使用している外部ファイル

No.22（rename）と同じファイルのため、依存関係はNo.22を参照してください。

## 5. データ構造詳細

### 主要な変数・データ項目

#### prepareRename の戻り値オブジェクト（459-463行目）

| 項目名 | 型 | 用途・説明 |
|-------|--------|----------|
| start | integer | シンボルの開始位置 |
| finish | integer | シンボルの終了位置 |
| text | string | シンボルの現在の名前（リネームダイアログに表示される） |

**解説**: リネーム可能と判定された場合に返されるオブジェクトです。エディタはこの情報を使ってリネームダイアログを表示し、`text` を初期値として入力欄に設定します。

## 6. 処理詳細解説

このプログラムの処理内容をステップバイステップで説明します。

### 6.1 処理一覧

| No | 処理名 | 行番号 | 概要 |
|----|-------|--------|------|
| 1 | prepareRename（内部関数） | 327-362 | ソースノードのリネーム可否を判定 |
| 2 | m.prepareRename | 444-464 | prepareRename のエントリポイント |

### 6.2 prepareRename（内部関数）の詳細（327-362行目）

**この処理は何をするものか**: 渡されたソースノードがリネーム可能かどうかを判定し、可能な場合は現在の名前を返します。

**処理の流れ**:

```
ステップ1: ソースの種類を判定
    │
    ↓（リネーム可能な種類の場合）
ステップ2: ソースと名前を返す
    │
    ↓（特殊な種類の場合）
ステップ3: 親ノードを確認して判定
    │
    ↓（リネーム不可の場合）
ステップ4: nil を返す
```

**ステップバイステップ解説**:

1. **ステップ1: 基本的な種類の判定**（328-345行目）

   ```lua
   if source.type == 'label'
   or source.type == 'goto'
   or source.type == 'local'
   or source.type == 'setlocal'
   or source.type == 'getlocal'
   -- ... 続く
   ```

   まず、ソースの種類が以下のリネーム可能な種類に該当するかをチェックします：
   - `label`, `goto`: ラベルとgoto文
   - `local`, `setlocal`, `getlocal`: ローカル変数
   - `field`, `method`: フィールドとメソッド
   - `tablefield`: テーブルリテラル内のフィールド
   - `setglobal`, `getglobal`: グローバル変数
   - `doc.class.name`, `doc.type.name` など: LuaDocアノテーション内の名前

2. **ステップ2: 名前の取得と返却**（345行目）

   ```lua
   return source, source[1]
   ```

   リネーム可能な種類の場合、ソースノードとその名前（`source[1]` に格納）を返します。

3. **ステップ3: 文字列・数値リテラルの判定**（346-360行目）

   ```lua
   elseif source.type == 'string'
   or     source.type == 'number'
   or     source.type == 'integer'
   or     source.type == 'boolean' then
   ```

   文字列や数値リテラルの場合、それがテーブルのインデックスとして使われているかをチェックします：

   ```lua
   if parent.type == 'setindex'
   or parent.type == 'getindex'
   or parent.type == 'tableindex' then
       return source, source[1]
   end
   ```

   例えば `obj["key"]` の `"key"` 部分は、フィールド名としてリネーム可能です。

4. **ステップ4: リネーム不可の場合**（361行目）

   ```lua
   return nil
   ```

   上記のいずれにも該当しない場合は `nil` を返し、リネーム不可を示します。

**この処理のポイント**:
- `source[1]` は多くのノードで「名前」を保持しています
- 文字列リテラルでもテーブルのキーとして使われている場合はリネーム可能

### 6.3 m.prepareRename の詳細（444-464行目）

**この処理は何をするものか**: LSPリクエストのエントリポイントとして、カーソル位置のシンボルのリネーム可否を確認します。

**処理の流れ**:

```
ステップ1: ASTを取得
    │
    ↓
ステップ2: カーソル位置のソースを検索
    │
    ↓
ステップ3: prepareRename を呼び出し
    │
    ↓
ステップ4: 結果を返す
```

**ステップバイステップ解説**:

1. **ステップ1: ASTの取得**（445-448行目）

   ```lua
   local ast = files.getState(uri)
   if not ast then
       return nil
   end
   ```

   まず、対象ファイルのAST（抽象構文木）を取得します。ファイルが読み込まれていない場合は `nil` を返します。

2. **ステップ2: ソースの検索**（449-452行目）

   ```lua
   local source = findSource(ast, pos, accept)
   if not source then
       return
   end
   ```

   `findSource` 関数を使って、指定位置（`pos`）にあるソースノードを検索します。`accept` テーブルで定義された種類のノードのみが対象です。

3. **ステップ3: prepareRename の呼び出し**（454-457行目）

   ```lua
   local res, text = prepareRename(source)
   if not res then
       return nil
   end
   ```

   内部の `prepareRename` 関数を呼び出し、リネーム可否を判定します。

4. **ステップ4: 結果の返却**（459-463行目）

   ```lua
   return {
       start  = source.start,
       finish = source.finish,
       text   = text,
   }
   ```

   リネーム可能な場合、シンボルの位置と名前を含むオブジェクトを返します。この情報はエディタでリネームダイアログを表示する際に使用されます。

**この処理のポイント**:
- `source.start` と `source.finish` はシンボルの正確な位置を示します
- `text` はリネームダイアログの初期値として使用されます

## 7. 処理フロー図

プログラム全体の処理の流れを図で示します。

```mermaid
flowchart TD
    A[prepareRename開始] --> B[ASTを取得]
    B --> C{AST存在?}
    C -->|No| Z[nullを返す]
    C -->|Yes| D[カーソル位置のソースを検索]
    D --> E{ソース発見?}
    E -->|No| Z
    E -->|Yes| F{ソースの種類を判定}
    F -->|label/local/field等| G[リネーム可能]
    F -->|string/number等| H{親がindex系?}
    F -->|その他| Z
    H -->|Yes| G
    H -->|No| Z
    G --> I[位置と名前を返す]
```

**図の説明**:
- **prepareRename開始**: ユーザーがリネーム操作を開始（F2キー等）
- **ソースの種類を判定**: シンボルの種類によってリネーム可否を決定
- **位置と名前を返す**: エディタがリネームダイアログを表示するための情報

## 8. 外部連携

### 呼び出し関係図

```
[呼び出し元]                    [このプログラム]                    [呼び出し先]
                                     │
provider.lua ──────────────▶ rename.lua ─────────────────▶ files.getState()
  (textDocument/prepareRename)       │  (m.prepareRename)      (AST取得)
                                     │
                                     └──────────────▶ findSource()
                                                        (ソース検索)
```

### 呼び出し元（このプログラムを呼び出すもの）

| プログラム名 | 呼び出し契機 | 渡されるデータ |
|------------|------------|--------------|
| provider.lua | ユーザーがリネームを開始 | URI、位置（pos） |

### 呼び出し先（このプログラムが呼び出すもの）

| プログラム名 | 呼び出し目的 | 渡すデータ | 受け取るデータ |
|------------|------------|----------|--------------|
| files.getState | ファイルのAST取得 | URI | AST |
| findSource | カーソル位置のソース検索 | AST、位置、acceptテーブル | ソースノード |

## 9. エラー処理

このプログラムで発生する可能性があるエラーと、その対処方法を説明します。

### エラー一覧

| エラー種別 | 発生条件 | 対処内容 | 該当行 |
|----------|---------|---------|--------|
| ASTなし | ファイルが未読み込み | null を返す | 446-448 |
| ソースなし | カーソル位置にシンボルがない | null を返す | 450-452 |
| リネーム不可 | シンボルがリネーム不可能な種類 | null を返す | 455-457 |

### エラー発生時の動作

`null` が返された場合、エディタはリネームダイアログを表示せず、「このシンボルはリネームできません」といったメッセージを表示します。これはユーザーにとって自然な動作です。

## 10. このプログラムを読むときのポイント

このプログラムを理解するためのアドバイスです。

### 推奨する読み方

1. **まず m.prepareRename（444-464行目）を見る**: 全体の流れが分かります
2. **次に prepareRename（327-362行目）を見る**: リネーム可能な種類の判定方法が分かります
3. **accept テーブル（364-387行目）を見る**: リネーム可能な種類の一覧が分かります

### 注意すべきポイント

- `prepareRename` と `m.prepareRename` は別の関数です。前者は内部関数、後者は外部に公開される関数です
- `source[1]` はLuaのASTで「最初の子要素」を意味し、多くのノードでは名前を保持しています
- 文字列リテラルがリネーム可能なのは、テーブルのキーとして使われている場合のみです

### 関連して読むべきファイル

| ファイル | パス | 関連性 |
|---------|------|--------|
| rename.lua | `script/core/rename.lua` | m.rename 関数で実際のリネームが行われます |
| find-source.lua | `script/core/find-source.lua` | ソース検索の詳細なロジック |

## 11. 備考

- prepareRename は LSP 3.12 で追加された機能です
- この機能により、リネーム不可能なシンボルに対してユーザーが無駄にリネームを試みることを防げます
- `accept` テーブルは `m.rename` と共有されているため、リネーム可能な種類は一貫しています
