# プログラム設計書 24-code-action

## 1. 概要

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

このプログラムは、コードの問題を自動的に修正する「クイックフィックス」や、コードを改善する「リファクタリング」の提案を提供するためのものです。診断情報（エラーや警告）に基づいて適切な修正案を生成し、ユーザーがワンクリックで問題を解決できるようにします。

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

- **呼び出し元**: LSPプロトコルの `textDocument/codeAction` リクエストハンドラ
- **主な処理**: 診断情報やコード範囲に基づいて、適用可能なコードアクションを生成
- **出力**: コードアクションのリスト（クイックフィックス、リファクタリング提案など）

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

| 項目 | 内容 |
|-----|------|
| ファイルパス | `script/core/code-action.lua` |
| ファイル名 | code-action.lua |
| 種別 | ソースコード |
| 使用言語 | Lua |
| 総行数 | 750行 |
| 作成目的 | コードアクション（クイックフィックス・リファクタリング）機能を提供する |

## 3. 用語解説

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

| 用語 | 読み方 | 意味・説明 |
|-----|--------|----------|
| Code Action | コードアクション | コードに対して適用できる操作。クイックフィックスやリファクタリングを含む |
| Quick Fix | クイックフィックス | 問題を素早く修正するための提案。電球アイコンで表示されることが多い |
| Refactoring | リファクタリング | コードの動作を変えずに構造を改善する操作 |
| Diagnostic | ダイアグノスティック | コードの問題（エラー、警告、情報）の診断結果 |
| Text Edit | テキストエディット | テキストに対する編集操作（挿入、削除、置換） |
| Command | コマンド | サーバー側で実行される操作 |

### 言語固有の概念

- **LuaDoc**: `---@diagnostic` などのアノテーションで診断を制御できます
- **require**: モジュール読み込み。自動 require 機能でインポート文を自動生成できます
- **グローバル変数**: Lua では宣言なしで使用された変数はグローバルになります

## 4. 依存関係

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

```
code-action.lua
    │
    ├─ require 'files' ................. ファイル管理
    │
    ├─ require 'language' .............. 多言語対応
    │
    ├─ require 'utility' ............... ユーティリティ
    │
    ├─ require 'parser.guide' .......... AST操作
    │
    ├─ require 'proto.converter' ....... LSPプロトコル変換
    │
    ├─ require 'core.completion.auto-require' ... 自動require
    │
    ├─ require 'workspace.require-path' . requireパス解決
    │
    ├─ require 'file-uri' .............. ファイルURI変換
    │
    └─ require 'vm' .................... 仮想マシン
```

| 参照ファイル | パス | 用途 |
|------------|------|------|
| files | `script/files.lua` | ファイルテキストとAST取得 |
| language | `script/language.lua` | 多言語メッセージ取得 |
| guide | `script/parser/guide.lua` | AST走査・位置操作 |
| converter | `script/proto/converter.lua` | LSP形式への変換 |
| auto-require | `script/core/completion/auto-require.lua` | 自動require検出 |
| require-path | `script/workspace/require-path.lua` | requireパス解決 |

## 5. データ構造詳細

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

#### コードアクション結果オブジェクト

| 項目名 | 型 | 用途・説明 |
|-------|--------|----------|
| title | string | アクションの表示名（ユーザーに見える） |
| kind | string | アクションの種類（'quickfix', 'refactor.rewrite'など） |
| edit | object | テキスト編集の内容（即時適用用） |
| command | object | サーバーコマンド（遅延実行用） |

**解説**: コードアクションには2種類の実行方法があります。`edit` は即座にテキスト編集を適用し、`command` はサーバー側で追加処理を実行します。

#### edit オブジェクト構造

```lua
edit = {
    changes = {
        [uri] = {
            {
                start   = 開始位置,
                finish  = 終了位置,
                newText = 新しいテキスト,
            },
            -- ...
        }
    }
}
```

#### command オブジェクト構造

```lua
command = {
    title     = コマンドタイトル,
    command   = コマンド名（'lua.setConfig'など）,
    arguments = { ... 引数 ... },
}
```

## 6. 処理詳細解説

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

### 6.1 処理一覧

| No | 処理名 | 行番号 | 概要 |
|----|-------|--------|------|
| 1 | disableDiagnostic | 60-96 | 診断を無効化するアクションを生成 |
| 2 | markGlobal | 98-115 | グローバル変数として登録するアクションを生成 |
| 3 | solveUndefinedGlobal | 136-156 | 未定義グローバルの解決策を提供 |
| 4 | solveSyntax | 262-279 | 構文エラーの解決策を提供 |
| 5 | checkQuickFix | 453-460 | 診断に対するクイックフィックスを収集 |
| 6 | checkSwapParams | 462-556 | パラメータ入れ替えを提案 |
| 7 | checkJsonToLua | 630-680 | JSONからLuaへの変換を提案 |
| 8 | checkMissingRequire | 690-732 | 不足しているrequireを提案 |
| 9 | メイン関数 | 734-749 | 全アクションを統合して返す |

### 6.2 disableDiagnostic の詳細（60-96行目）

**この処理は何をするものか**: 特定の診断を無効化するためのアクションを生成します。

**処理の流れ**:

```
ステップ1: 設定で診断を無効化するアクションを追加
    │
    ↓
ステップ2: 行単位で診断を無効化するアクションを追加
    │
    ↓
ステップ3: ファイル単位で診断を無効化するアクションを追加
```

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

1. **ステップ1: 設定で無効化**（62-77行目）

   ```lua
   results[#results+1] = {
       title   = lang.script('ACTION_DISABLE_DIAG', code),
       kind    = 'quickfix',
       command = {
           title     = lang.script.COMMAND_DISABLE_DIAG,
           command   = 'lua.setConfig',
           arguments = {
               {
                   key    = 'Lua.diagnostics.disable',
                   action = 'add',
                   value  = code,
                   uri    = uri,
               }
           }
       }
   }
   ```

   設定ファイル（.luarc.json）に診断コードを追加して、プロジェクト全体で無効化するアクションを生成します。

2. **ステップ2: 行単位で無効化**（90-92行目）

   該当行の直前に `---@diagnostic disable-next-line: code` を挿入するアクションを生成します。これにより、その行だけ診断が無効化されます。

3. **ステップ3: ファイル単位で無効化**（93-95行目）

   ファイルの先頭に `---@diagnostic disable: code` を挿入するアクションを生成します。これにより、ファイル全体で診断が無効化されます。

**この処理のポイント**:
- 3つのスコープ（設定/行/ファイル）で無効化を選択可能
- 既存の `---@diagnostic` アノテーションがある場合は、それに追加する形式になります

### 6.3 solveUndefinedGlobal の詳細（136-156行目）

**この処理は何をするものか**: 「未定義のグローバル変数」エラーに対する解決策を提供します。

**処理の流れ**:

```
ステップ1: ASTを取得
    │
    ↓
ステップ2: エラー位置のソースを検索
    │
    ↓
ステップ3: グローバルとして登録するアクションを追加
    │
    ↓
ステップ4: 対応するバージョンがあれば提案
```

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

1. **ステップ1-2: ソースの検索**（137-145行目）

   診断の位置からグローバル変数のソースノードを検索します。

2. **ステップ3: グローバルとして登録**（147-148行目）

   ```lua
   markGlobal(uri, name, results)
   ```

   `Lua.diagnostics.globals` 設定に変数名を追加するアクションを生成します。これにより、その変数は「既知のグローバル」として扱われ、警告が出なくなります。

3. **ステップ4: バージョン変更の提案**（151-155行目）

   診断データに `versions` が含まれている場合（例: `utf8` は Lua 5.3+ で利用可能）、ランタイムバージョンを変更するアクションを提案します。

**この処理のポイント**:
- よく使われるグローバル変数（`print`, `require` など）は通常組み込みとして認識されます
- バージョン固有の機能は、正しいバージョンを設定することで解決できます

### 6.4 checkSwapParams の詳細（462-556行目）

**この処理は何をするものか**: 関数の引数を入れ替えるリファクタリングを提案します。

**処理の流れ**:

```
ステップ1: 選択範囲内の引数リストを検索
    │
    ↓
ステップ2: 選択された引数を特定
    │
    ↓
ステップ3: 他の引数との入れ替えアクションを生成
```

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

1. **ステップ1: 引数リストの検索**（469-514行目）

   選択範囲内にある `callargs`（関数呼び出しの引数）または `funcargs`（関数定義のパラメータ）を検索します。

2. **ステップ2: 選択された引数の特定**（472-484行目）

   選択範囲に含まれる引数を特定します。複数の引数が選択されている場合は処理をスキップします。

3. **ステップ3: 入れ替えアクションの生成**（523-555行目）

   ```lua
   results[#results+1] = {
       title = lang.script('ACTION_SWAP_PARAMS', {
           node  = target.node,
           index = i,
       }),
       kind = 'refactor.rewrite',
       edit = { ... }
   }
   ```

   選択された引数と他の各引数を入れ替えるアクションを生成します。

**この処理のポイント**:
- 引数を1つだけ選択した場合のみ有効
- 関数呼び出しと関数定義の両方で動作します

### 6.5 checkJsonToLua の詳細（630-680行目）

**この処理は何をするものか**: 選択されたJSONテキストをLuaテーブルに変換するアクションを提案します。

**処理の流れ**:

```
ステップ1: 選択範囲からJSON開始位置を検索
    │
    ↓
ステップ2: JSON終了位置を検索
    │
    ↓
ステップ3: JSONとして有効か確認
    │
    ↓
ステップ4: 変換アクションを追加
```

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

1. **ステップ1: JSON開始位置の検索**（636-640行目）

   ```lua
   local jsonStart = text:match('()["%{%[]', startOffset + 1)
   ```

   選択範囲内で `"`, `{`, `[` のいずれかを探し、JSON開始位置とします。

2. **ステップ2-3: JSON終了位置と検証**（641-664行目）

   対応する終了文字（`}` または `]`）を探し、JSONとして有効かどうかを確認します。

3. **ステップ4: 変換アクションの追加**（665-679行目）

   ```lua
   results[#results+1] = {
       title = lang.script.ACTION_JSON_TO_LUA,
       kind = 'refactor.rewrite',
       command = {
           title = lang.script.COMMAND_JSON_TO_LUA,
           command = 'lua.jsonToLua',
           arguments = { ... }
       },
   }
   ```

   `lua.jsonToLua` コマンドを実行するアクションを生成します。

**この処理のポイント**:
- JSONオブジェクトと配列の両方に対応
- 変換はコマンドとして実行されるため、複雑な処理が可能

### 6.6 メイン関数の詳細（734-749行目）

**この処理は何をするものか**: 全てのコードアクションチェックを実行し、結果を統合します。

**処理の流れ**:

```
ステップ1: ASTを取得
    │
    ↓
ステップ2: 各種チェックを実行
    │
    ↓
ステップ3: 結果を返す
```

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

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

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

2. **ステップ2: 各種チェックの実行**（742-746行目）

   ```lua
   checkQuickFix(results, uri, start, diagnostics)
   checkSwapParams(results, uri, start, finish)
   checkJsonToLua(results, uri, start, finish)
   checkMissingRequire(results, uri, start, finish)
   ```

   各チェック関数を呼び出し、該当するアクションを `results` に追加します。

**この処理のポイント**:
- 診断情報（`diagnostics`）が渡された場合のみクイックフィックスが提案されます
- 選択範囲（`start`, `finish`）に基づいてリファクタリングが提案されます

## 7. 処理フロー図

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

```mermaid
flowchart TD
    A[codeAction開始] --> B[ASTを取得]
    B --> C{AST存在?}
    C -->|No| Z[nullを返す]
    C -->|Yes| D[結果配列を初期化]
    D --> E[checkQuickFix]
    E --> F[checkSwapParams]
    F --> G[checkJsonToLua]
    G --> H[checkMissingRequire]
    H --> I[結果を返す]

    E --> E1{診断あり?}
    E1 -->|Yes| E2[solveDiagnostic]
    E1 -->|No| F

    E2 --> E3[disableDiagnostic]
    E3 --> E4[診断固有の解決策]
```

**図の説明**:
- **codeAction開始**: ユーザーがコードアクションを要求（Ctrl+.等）
- **checkQuickFix**: 診断に対するクイックフィックスを収集
- **checkSwapParams/checkJsonToLua/checkMissingRequire**: リファクタリング提案を収集
- **結果を返す**: 全アクションをLSPクライアントに返却

## 8. 外部連携

### 呼び出し関係図

```
[呼び出し元]                    [このプログラム]                    [呼び出し先]
                                     │
provider.lua ──────────────▶ code-action.lua ──────────────▶ auto-require.check()
  (textDocument/codeAction)          │                          (require候補検出)
                                     │
                                     ├──────────────▶ files.getState()
                                     │                   (AST取得)
                                     │
                                     └──────────────▶ コマンド実行
                                                        (lua.setConfig等)
```

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

| プログラム名 | 呼び出し契機 | 渡されるデータ |
|------------|------------|--------------|
| provider.lua | ユーザーがコードアクションを要求 | URI、範囲（start, finish）、診断リスト |

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

| プログラム名 | 呼び出し目的 | 渡すデータ | 受け取るデータ |
|------------|------------|----------|--------------|
| auto-require.check | 自動require候補の検出 | AST、グローバル名、位置 | モジュール情報 |
| rpath.getVisiblePath | 可視なrequireパスの取得 | URI、モジュールファイル | パスリスト |
| converter.unpackRange | LSP範囲から内部形式に変換 | AST、範囲 | start, finish |

## 9. エラー処理

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

### エラー一覧

| エラー種別 | 発生条件 | 対処内容 | 該当行 |
|----------|---------|---------|--------|
| ASTなし | ファイルが未読み込み | null を返す | 735-738 |
| 診断なし | 診断情報が渡されない | クイックフィックスをスキップ | 454-456 |
| 構文エラー検出失敗 | 構文エラーが見つからない | その診断をスキップ | 263-266 |

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

このプログラムは例外を投げません。エラー状態の場合は該当のアクションをスキップし、利用可能なアクションのみを返します。

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

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

### 推奨する読み方

1. **まずメイン関数（734-749行目）を見る**: 全体の処理の流れが分かります
2. **次に checkQuickFix と solveDiagnostic を見る**: 診断に対する処理が分かります
3. **最後に個別の solve* 関数を見る**: 各診断に対する具体的な解決策が分かります

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

- `edit` と `command` は排他的ではなく、両方を持つアクションもあります
- `kind` は LSP で定義されたアクション種類を示します（'quickfix', 'refactor.rewrite' など）
- 多言語対応のため、タイトルは `lang.script()` を通して取得されます

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

| ファイル | パス | 関連性 |
|---------|------|--------|
| removeSpace.lua | `script/core/command/removeSpace.lua` | lua.removeSpace コマンドの実装 |
| solve.lua | `script/core/command/solve.lua` | lua.solve コマンドの実装 |
| jsonToLua.lua | `script/core/command/jsonToLua.lua` | lua.jsonToLua コマンドの実装 |

## 11. 備考

- コードアクションは LSP の重要な機能で、IDE の生産性を大きく向上させます
- コマンドベースのアクションは、より複雑な処理（確認ダイアログ表示など）が可能です
- 診断グループによって、関連する診断を一括で制御できます
