# 画面設計書 10-名前変更

## 概要

本ドキュメントは、Lua Language Serverの「名前変更」機能の設計を記載したものである。LSP（Language Server Protocol）の `textDocument/rename` メソッドに対応し、シンボル名をワークスペース全体で一括変更する機能を提供する。

### 本画面の処理概要

**業務上の目的・背景**：
リファクタリングにおいて、変数名・関数名・クラス名などの識別子を変更することは頻繁に発生する作業である。手動での変更は漏れや不整合を引き起こしやすいため、本機能によりワークスペース全体で安全かつ一括的にシンボル名を変更できる。

**画面へのアクセス方法**：
エディタ（VS Code等）上でLuaファイルを開き、変数名・関数名などのシンボル上で以下の操作を行う：
- 右クリックメニューから「シンボルの名前変更」を選択
- F2キーを押下
- コマンドパレットから「Rename Symbol」を実行

**主要な操作・処理内容**：
1. （準備）`textDocument/prepareRename` で名前変更可能かを事前確認
2. エディタが新しい名前の入力ダイアログを表示
3. エディタからLSPサーバーへ `textDocument/rename` リクエストを送信
4. カーソル位置からソースオブジェクトを特定
5. シンボルの種類に応じた参照検索を実行
6. 各参照箇所に対して適切な変更テキストを生成
7. WorkspaceEdit 形式でエディタへ返却
8. エディタが全変更を適用

**画面遷移**：
- 遷移元：エディタのコード編集画面
- 遷移先：名前入力ダイアログ → 同じコード編集画面（変更適用後）
- 関連遷移：参照検索（事前の影響確認として）

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | リネーム (textDocument/rename) | 主機能 | シンボル名をワークスペース全体で変更する主処理 |
| 16 | 参照検索 (textDocument/references) | 補助機能 | リネーム対象の全参照箇所を特定 |

## 画面種別

編集

## URL/ルーティング

LSPメソッド: `textDocument/rename`

**リクエストパラメータ**:
```json
{
  "textDocument": {
    "uri": "file:///path/to/file.lua"
  },
  "position": {
    "line": 10,
    "character": 15
  },
  "newName": "newVariableName"
}
```

## 入出力項目

### 入力項目

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| textDocument.uri | string | Yes | 対象ファイルのURI |
| position.line | integer | Yes | カーソル位置の行番号（0始まり） |
| position.character | integer | Yes | カーソル位置の列番号（0始まり） |
| newName | string | Yes | 新しいシンボル名 |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| changes | object | ファイルURIをキーとした編集リストのマップ |
| changes[uri] | TextEdit[] | 各ファイルの編集リスト |
| changes[uri][].range | Range | 編集位置の範囲 |
| changes[uri][].newText | string | 置換テキスト |

## 表示項目

### 名前変更確認

| 表示項目 | 形式 | 説明 |
|---------|------|------|
| 入力ダイアログ | テキストボックス | 新しい名前を入力 |
| プレビュー | 変更箇所リスト | 変更される箇所の一覧（エディタ機能） |
| 適用結果 | コード変更 | 全ての参照箇所が新しい名前に変更 |

## イベント仕様

### 1-名前変更準備リクエスト処理（prepareRename）

1. エディタからLSPサーバーへ `textDocument/prepareRename` リクエストを送信
2. `provider.lua` の `textDocument/prepareRename` ハンドラが呼び出される
3. `core/rename.lua` の `prepareRename` 関数を呼び出し
4. カーソル位置のソースオブジェクトを特定
5. 名前変更可能なシンボル種類かを判定
6. 可能な場合、シンボルの範囲と現在の名前を返却
7. 不可能な場合、`nil` を返却

### 2-名前変更リクエスト処理（rename）

1. エディタからLSPサーバーへ `textDocument/rename` リクエストを送信
2. `provider.lua` の `textDocument/rename` ハンドラが呼び出される
3. ワークスペースの準備状態を確認
4. 進捗表示を開始（WINDOW_PROCESSING_RENAME）
5. `core/rename.lua` の `rename` 関数を呼び出し
6. `findSource` でカーソル位置のソースオブジェクトを特定
7. シンボルの種類に応じた処理を実行：
   - `label`/`goto`: ラベル参照を全て変更
   - `local`/`setlocal`/`getlocal`: ローカル変数と参照を変更
   - `field`/`method`: フィールド/メソッド参照を変更
   - `setglobal`/`getglobal`: グローバル変数を変更
   - `doc.class.name`等: LuaDoc型名を変更
   - `doc.param.name`: パラメータ名と対応するローカル変数を変更
   - 文字列インデックス: 対応するフィールドを変更
8. 新しい名前の妥当性を検証
9. 無効な名前の場合、代替構文を生成（例: `obj['name']` 形式）
10. メタファイル・ライブラリファイルへの変更を除外
11. WorkspaceEdit 形式でレスポンスを返却

### 3-シンボル種類別の変更処理

| シンボル種類 | 処理関数 | 変更内容 |
|------------|---------|---------|
| local/setlocal/getlocal | ofLocal | ローカル変数と全参照 + @param アノテーション |
| label/goto | ofLabel | ラベルと全goto文 |
| field/method | ofField | フィールド/メソッド参照（`vm.getRefs` 使用） |
| setglobal/getglobal | ofGlobal | グローバル変数の全参照 |
| doc.class.name 等 | ofDocTypeName | LuaDoc型定義と全参照 |
| doc.param.name | ofDocParamName | @param名と対応するローカル変数 |

### 4-無効な名前の変換処理

新しい名前が識別子として無効な場合、代替構文を生成：

| 元の形式 | 変換後の形式 | 例 |
|---------|------------|-----|
| obj.field | obj['field'] | `obj.my-name` → `obj['my-name']` |
| tablefield | tableindex | `{field = 1}` → `{['my-name'] = 1}` |
| function name() | _ENV['name'] = function() | グローバル関数定義 |
| function obj:method() | obj['method'] = function(self) | メソッド定義 |

## データベース更新仕様

本機能はデータベース操作を行わない（ファイル編集のみ）。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 発生条件 |
|-------------|------|---------------|---------|
| WINDOW_PROCESSING_RENAME | 情報 | 処理中表示 | 名前変更実行中 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| ファイルが存在しない | `nil` を返却 |
| カーソル位置にシンボルがない | `nil` を返却 |
| 名前変更不可能なシンボル | `nil` を返却（prepareRename で事前判定） |
| メタファイルへの変更 | 変更対象から除外 |
| ライブラリファイルへの変更 | 変更対象から除外 |

## 備考

- `prepareRename` により、名前変更可能かを事前に判定できる
- Unicode文字を含む名前は `Lua.runtime.unicodeName` 設定で制御
- 無効な識別子名でも、ブラケット記法への変換により対応可能
- メソッド定義（`function obj:method()`）のリネームは、`self` 引数の追加も自動で行う
- メタファイル（組み込みAPIの定義ファイル）は変更対象から自動的に除外される
- ライブラリファイルからの参照元のみ変更（ライブラリファイル自体は変更しない）

---

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

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

### 推奨読解順序

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

名前変更で扱うデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | parser/guide.lua | `script/parser/guide.lua` | ASTノードの種類と構造 |
| 1-2 | vm/init.lua | `script/vm/init.lua` | `getRefs`, `getGlobal` 関数の動作 |

**読解のコツ**: シンボルの種類によって変更処理が異なることを理解する。

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

処理の起点となるプロバイダーファイルを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | provider.lua | `script/provider/provider.lua` | LSPメソッドの登録とリクエスト処理 |

**主要処理フロー**:
1. **559-596行目**: `textDocument/rename` ハンドラの定義
2. **569行目**: `workspace.awaitReady` でワークスペース準備待ち
3. **570行目**: `progress.create` で進捗表示開始
4. **575行目**: `core.rename` モジュールの読み込み
5. **577行目**: `core.rename.rename` の呼び出し
6. **581-593行目**: WorkspaceEdit の構築
7. **598-621行目**: `textDocument/prepareRename` ハンドラの定義

#### Step 3: コア名前変更ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | rename.lua | `script/core/rename.lua` | 名前変更のメインロジック |

**主要処理フロー（rename.lua）**:
- **19-29行目**: `isValidName` - 識別子の妥当性検証
- **34-44行目**: `isValidGlobal` - グローバル名の妥当性検証
- **73-79行目**: `renameLocal` - ローカル変数のリネーム
- **81-125行目**: `renameField` - フィールドのリネーム（構文変換含む）
- **127-145行目**: `renameGlobal` - グローバル変数のリネーム
- **147-163行目**: `ofLocal` - ローカル変数と参照のリネーム
- **207-213行目**: `ofField` - フィールド参照のリネーム
- **216-229行目**: `ofGlobal` - グローバル変数参照のリネーム
- **232-236行目**: `ofLabel` - ラベルのリネーム
- **239-264行目**: `ofDocTypeName` - LuaDoc型名のリネーム
- **285-325行目**: `rename` - メインのリネーム関数
- **327-362行目**: `prepareRename` - リネーム可能性の判定
- **364-387行目**: `accept` テーブル - 処理対象ノード種類
- **392-442行目**: `m.rename` - エントリーポイント
- **444-464行目**: `m.prepareRename` - 準備処理のエントリーポイント

#### Step 4: 補助モジュールを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | find-source.lua | `script/core/find-source.lua` | ソースオブジェクト検索 |
| 4-2 | utility.lua | `script/utility.lua` | `viewString` 関数（文字列リテラル生成） |
| 4-3 | config.lua | `script/config.lua` | `Lua.runtime.unicodeName` 設定 |

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

```
textDocument/rename (provider.lua:559)
    │
    ├─ workspace.awaitReady(uri)
    │
    ├─ progress.create()
    │
    ├─ files.getState(uri)
    │
    ├─ converter.unpackPosition()
    │
    └─ core.rename.rename(uri, pos, newName)
           │
           ├─ findSource(ast, pos, accept)
           │
           └─ rename(source, newname, callback)
                  │
                  ├── ofLabel()      [for label/goto]
                  │
                  ├── ofLocal()      [for local/setlocal/getlocal]
                  │      └── renameLocal()
                  │
                  ├── ofField()      [for field/method]
                  │      ├── vm.getRefs()
                  │      └── renameField()
                  │             └── isValidName() / util.viewString()
                  │
                  ├── ofGlobal()     [for setglobal/getglobal]
                  │      ├── vm.getGlobal()
                  │      └── renameGlobal()
                  │
                  ├── ofDocTypeName() [for doc.class.name etc.]
                  │      └── vm.getGlobal('type', ...)
                  │
                  └── ofDocParamName() [for doc.param.name]
                         └── renameLocal()
```

### データフロー図

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

textDocument.uri    ───▶ files.getState(uri)     ───▶  AST State
position            ───▶ converter.unpackPosition ───▶  内部位置
newName             ───▶ isValidName()           ───▶  名前検証
                         │
                         ▼
                    findSource()                  ───▶  Source Object
                         │
                         ▼
                    rename()                      ───▶  Callback呼び出し
                         │
              ┌──────────┼──────────┐
              ▼          ▼          ▼
         ofLocal()   ofField()  ofGlobal() etc.
              │          │          │
              ▼          ▼          ▼
         renameLocal() renameField() renameGlobal()
              │          │          │
              └──────────┴──────────┘
                         │
                         ▼
                    結果フィルタリング
                    (meta/library除外)            ───▶  Filtered Results
                         │
                         ▼
                    WorkspaceEdit構築             ───▶  LSP Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSPプロバイダー・エントリーポイント |
| rename.lua | `script/core/rename.lua` | ソース | 名前変更のメインロジック |
| find-source.lua | `script/core/find-source.lua` | ソース | ソースオブジェクト検索 |
| guide.lua | `script/parser/guide.lua` | ソース | AST操作ユーティリティ |
| vm/init.lua | `script/vm/init.lua` | ソース | 参照検索・グローバル取得 |
| vm/ref.lua | `script/vm/ref.lua` | ソース | getRefs の実装 |
| vm/global.lua | `script/vm/global.lua` | ソース | getGlobal の実装 |
| utility.lua | `script/utility.lua` | ソース | viewString（文字列リテラル生成） |
| config.lua | `script/config.lua` | ソース | unicodeName設定 |
| converter.lua | `script/proto/converter.lua` | ソース | LSP位置変換 |
