# プログラム設計書 27-jsonToLua

## 1. 概要

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

このプログラムは、選択されたJSONテキストをLuaテーブルの形式に変換するためのものです。設定ファイルやAPIレスポンスなどのJSONデータをLuaコードとして使用したい場合に便利です。

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

- **呼び出し元**: LSPコマンド `lua.jsonToLua` の実行ハンドラ
- **主な処理**: JSONテキストを解析し、対応するLuaテーブル形式に変換
- **出力**: テキスト編集リクエストをエディタに送信（JSONをLuaに置換）

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

| 項目 | 内容 |
|-----|------|
| ファイルパス | `script/core/command/jsonToLua.lua` |
| ファイル名 | jsonToLua.lua |
| 種別 | ソースコード |
| 使用言語 | Lua |
| 総行数 | 55行 |
| 作成目的 | JSONからLuaテーブルへの変換機能を提供する |

## 3. 用語解説

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

| 用語 | 読み方 | 意味・説明 |
|-----|--------|----------|
| JSON | ジェイソン | JavaScript Object Notation。データ交換形式 |
| JSONC | ジェイソンシー | JSON with Comments。コメント付きJSON |
| Lua Table | ルアテーブル | Luaのデータ構造。配列と連想配列の両方を表現可能 |
| Dump | ダンプ | データを文字列形式で出力すること |

### 言語固有の概念

- **pcall**: 保護された呼び出し。エラーが発生しても例外を投げずに戻り値で通知します
- **json.null**: JSONの `null` を表す特別な値。Luaには `nil` がありますが、テーブルのキーとして使えないため別の値が必要です

## 4. 依存関係

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

```
jsonToLua.lua
    │
    ├─ require 'files' ............. ファイル管理
    │
    ├─ require 'utility' ........... ユーティリティ（dump関数）
    │
    ├─ require 'proto' ............. LSPプロトコル通信
    │
    ├─ require 'proto.define' ...... メッセージタイプ定義
    │
    ├─ require 'language' .......... 多言語対応
    │
    ├─ require 'proto.converter' ... LSP形式変換
    │
    ├─ require 'parser.guide' ...... 位置変換
    │
    ├─ require 'json' .............. JSON処理
    │
    └─ require 'jsonc' ............. コメント付きJSON処理
```

| 参照ファイル | パス | 用途 |
|------------|------|------|
| files | `script/files.lua` | ファイルテキストとASTの取得 |
| utility | `script/utility.lua` | dump関数（Luaテーブル文字列化） |
| proto | `script/proto/` | リクエスト送信・通知 |
| define | `script/proto/define.lua` | MessageType定義 |
| jsonc | `script/jsonc.lua` | コメント付きJSONのパース |

## 5. データ構造詳細

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

#### data パラメータ

| 項目名 | 型 | 用途・説明 |
|-------|--------|----------|
| uri | string | 処理対象ファイルのURI |
| start | integer | 変換対象の開始位置 |
| finish | integer | 変換対象の終了位置 |

**解説**: コマンド実行時に渡されるパラメータです。code-action.lua で検出されたJSON範囲が指定されます。

## 6. 処理詳細解説

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

### 6.1 処理一覧

| No | 処理名 | 行番号 | 概要 |
|----|-------|--------|------|
| 1 | ファイル情報取得 | 13-17 | ファイルのテキストとASTを取得 |
| 2 | JSON抽出とパース | 18-28 | 選択範囲からJSONを抽出してパース |
| 3 | Lua変換 | 30-40 | パース結果をLuaテーブル形式に変換 |
| 4 | 編集リクエスト送信 | 41-53 | 変換結果でJSONを置換 |

### 6.2 JSON抽出とパースの詳細（18-28行目）

**この処理は何をするものか**: 選択範囲からJSONテキストを抽出し、Luaのテーブルにパースします。

**処理の流れ**:

```
ステップ1: 位置をオフセットに変換
    │
    ↓
ステップ2: JSONテキストを抽出
    │
    ↓
ステップ3: JSONをパース
    │
    ↓（失敗時）
ステップ4: エラー通知を表示
```

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

1. **ステップ1: 位置変換とテキスト抽出**（18-20行目）

   ```lua
   local start    = guide.positionToOffset(state, data.start)
   local finish   = guide.positionToOffset(state, data.finish)
   local jsonStr  = text:sub(start + 1, finish)
   ```

   LSPの位置情報をテキストのオフセットに変換し、該当範囲のテキストを抽出します。

   注意: Luaの文字列インデックスは1始まりなので、`start + 1` としています。

2. **ステップ2: JSONパース**（21行目）

   ```lua
   local suc, res = pcall(jsonc.decode_jsonc, jsonStr:match '[%{%[].+')
   ```

   - `jsonStr:match '[%{%[].+'` で `{` または `[` から始まる部分を抽出（先頭の引用符キーを除外）
   - `jsonc.decode_jsonc` でコメント付きJSONもパース可能
   - `pcall` でエラーをキャッチ

3. **ステップ3: エラー処理**（22-28行目）

   ```lua
   if not suc or res == json.null then
       proto.notify('window/showMessage', {
           type     = define.MessageType.Warning,
           message  = lang.script('COMMAND_JSON_TO_LUA_FAILED', res:match '%:%d+%:(.+)'),
       })
       return
   end
   ```

   パースに失敗した場合、または結果が `null` の場合は、警告メッセージを表示して終了します。

**この処理のポイント**:
- `jsonc` ライブラリを使用することで、コメント付きJSONもサポート
- エラー時はユーザーにメッセージを表示して通知

### 6.3 Lua変換の詳細（30-40行目）

**この処理は何をするものか**: パースされたJSONデータをLuaテーブル形式の文字列に変換します。

**処理の流れ**:

```
ステップ1: util.dumpでテーブルを文字列化
    │
    ↓
ステップ2: キーが文字列で始まる場合の処理
    │
    ↓
ステップ3: キーをLua形式に変換
```

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

1. **ステップ1: テーブル文字列化**（30行目）

   ```lua
   local luaStr = util.dump(res)
   ```

   `util.dump` 関数でLuaテーブルを文字列に変換します。例えば、`{name = "value", count = 42}` のような形式になります。

2. **ステップ2-3: キーの処理**（31-39行目）

   ```lua
   if jsonStr:sub(1, 1) == '"' then
       local key = jsonStr:match '^"([^\r\n]+)"'
       if key then
           if key:match '^[%a_]%w*$' then
               luaStr = ('%s = %s'):format(key, luaStr)
           else
               luaStr = ('[%q] = %s'):format(key, luaStr)
           end
       end
   end
   ```

   選択範囲が `"key": {...}` のように引用符付きキーで始まる場合：
   - キーが有効なLua識別子なら `key = {...}` 形式に
   - そうでなければ `["key"] = {...}` 形式に

**この処理のポイント**:
- JSONの `"key": value` 形式を Lua の `key = value` 形式に変換
- 特殊文字を含むキーは `["key"]` 形式で安全にアクセス可能

### 6.4 編集リクエスト送信の詳細（41-53行目）

**この処理は何をするものか**: 変換したLuaテキストで元のJSONを置換します。

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

```lua
proto.awaitRequest('workspace/applyEdit', {
    label = 'json to lua',
    edit  = {
        changes = {
            [data.uri] = {
                {
                    range   = converter.packRange(state, data.start, data.finish),
                    newText = luaStr,
                }
            }
        }
    }
})
```

- `label` は操作名（アンドゥ履歴に表示）
- `range` は元のJSON範囲
- `newText` は変換後のLuaテキスト

## 7. 処理フロー図

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

```mermaid
flowchart TD
    A[コマンド実行] --> B[ファイル情報取得]
    B --> C{ファイル存在?}
    C -->|No| Z[終了]
    C -->|Yes| D[JSON範囲を抽出]
    D --> E[JSONをパース]
    E --> F{パース成功?}
    F -->|No| G[エラー通知表示]
    G --> Z
    F -->|Yes| H[Luaテーブルに変換]
    H --> I{キー付き?}
    I -->|Yes| J[key = value形式に]
    I -->|No| K[そのまま]
    J --> L[workspace/applyEdit送信]
    K --> L
    L --> Z
```

**図の説明**:
- **JSONをパース**: JSONC（コメント付き）もサポート
- **エラー通知表示**: パース失敗時はユーザーに通知
- **key = value形式に**: JSONのキーがある場合は Lua の代入形式に変換

## 8. 外部連携

### 呼び出し関係図

```
[呼び出し元]                    [このプログラム]                    [呼び出し先]
                                     │
code-action.lua ───────────▶ jsonToLua.lua ────────────────▶ jsonc.decode_jsonc()
  (lua.jsonToLua)                    │                          (JSONパース)
                                     │
                                     ├──────────────▶ util.dump()
                                     │                   (Lua文字列化)
                                     │
                                     └──────────────▶ proto.awaitRequest()
                                                        (編集適用)
```

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

| プログラム名 | 呼び出し契機 | 渡されるデータ |
|------------|------------|--------------|
| code-action.lua | ユーザーがJSON to Luaアクションを選択時 | { uri, start, finish } |

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

| プログラム名 | 呼び出し目的 | 渡すデータ | 受け取るデータ |
|------------|------------|----------|--------------|
| jsonc.decode_jsonc | JSONをパース | JSON文字列 | Luaテーブル |
| util.dump | テーブルを文字列化 | Luaテーブル | Lua文字列 |
| proto.notify | エラー通知 | メッセージ | なし |
| proto.awaitRequest | 編集適用 | 編集データ | 応答 |

## 9. エラー処理

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

### エラー一覧

| エラー種別 | 発生条件 | 対処内容 | 該当行 |
|----------|---------|---------|--------|
| ファイルなし | ファイルが読み込まれていない | 何もせず終了 | 15-17 |
| パース失敗 | JSONの形式が不正 | エラーメッセージを表示 | 22-28 |
| null結果 | パース結果が null | エラーメッセージを表示 | 22 |

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

パースに失敗した場合は、`window/showMessage` 通知でユーザーに警告を表示します。エラーメッセージにはパース失敗の詳細が含まれます。

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

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

### 推奨する読み方

1. **まずパース部分を理解**: JSONからLuaテーブルへの変換
2. **次にキー処理を理解**: 引用符付きキーの Lua 形式への変換
3. **最後に編集処理を理解**: エディタへの適用

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

- `jsonStr:match '[%{%[].+'` はJSONの本体部分（オブジェクトまたは配列）を抽出します
- `pcall` を使用してパースエラーをキャッチしています
- `%q` は文字列をエスケープして引用符で囲むLuaのフォーマット指定子です

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

| ファイル | パス | 関連性 |
|---------|------|--------|
| code-action.lua | `script/core/code-action.lua` | checkJsonToLua 関数でJSON範囲を検出 |
| utility.lua | `script/utility.lua` | dump 関数の実装 |
| jsonc.lua | `script/jsonc.lua` | コメント付きJSONパーサー |

## 11. 備考

- このコマンドは選択範囲内のJSONを自動検出して変換します
- コメント付きJSON（JSONC）もサポートしているため、設定ファイルなども変換可能です
- 変換結果は Lua の文法に従った正しいテーブル形式になります

### 変換例

**入力（JSON）**:
```json
{
    "name": "lua-language-server",
    "version": "3.0.0",
    "enabled": true
}
```

**出力（Lua）**:
```lua
{
    name = "lua-language-server",
    version = "3.0.0",
    enabled = true
}
```
