# 画面設計書 42-PSIビュー

## 概要

本ドキュメントは、Lua Language Serverの内部LSP機能「PSIビュー」の画面設計書です。PSI（Program Structure Interface）は構文木のビュー表示を行う開発者向けの内部機能であり、エディタ拡張機能と連携してLuaコードの構文構造を階層的に表示します。

### 本画面の処理概要

**業務上の目的・背景**：言語サーバーの開発者やプラグイン開発者が、Luaコードの内部表現（PSI: Program Structure Interface）を確認・デバッグするために使用します。通常のLSP仕様には含まれない独自拡張（`$/psi/view`）として実装されており、VS Code拡張機能のsumneko.lua専用の機能です。PSIビューにより、パーサーがコードをどのように解釈しているかを視覚的に確認でき、言語機能の動作確認やバグ調査に活用できます。

**画面へのアクセス方法**：VS Code拡張機能（sumneko.lua）から内部LSPリクエスト `$/psi/view` を送信することでアクセスします。通常のユーザー操作ではなく、開発者モードまたはデバッグ用の機能として提供されます。

**主要な操作・処理内容**：
1. クライアント（エディタ拡張機能）からLSPリクエスト `$/psi/view` を受信する
2. リクエストパラメータからドキュメントURIを取得する
3. 対象ファイルの状態（パース済みAST）を取得する
4. ASTを再帰的に走査し、PSIノード構造に変換する
5. 各ノードには名前、位置情報（range）、子ノードを含める
6. 階層構造化されたPSIデータをレスポンスとして返却する

**画面遷移**：LSPリクエスト/レスポンス形式のため、画面遷移はありません。クライアントがレスポンスを受け取り、ツリービュー等で表示します。

**権限による表示制御**：開発者向け機能のため、特別な権限制御はありませんが、拡張機能側でこの機能の利用可否を制御する場合があります。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 66 | Luaソースコード解析 | 主機能 | 構文木（PSI）のビュー表示 |
| 67 | 構文木(AST)生成 | 補助機能 | 構文木の生成処理 |

## 画面種別

LSP内部リクエスト（開発者向け）

## URL/ルーティング

LSPメソッド: `$/psi/view`

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|-----|------|------|
| uri | 入力 | string | Yes | 対象ドキュメントのURI |
| data | 出力 | psi.view.node | - | PSIノードの階層構造データ |

### psi.view.node 構造

| フィールド | 型 | 説明 |
|-----------|-----|------|
| name | string | ノード名（型@[行:列 .. 行:列]形式） |
| attr | psi.view.attr | ノードの属性情報（オプション） |
| children | psi.view.node[] | 子ノードの配列（オプション） |

### psi.view.attr 構造

| フィールド | 型 | 説明 |
|-----------|-----|------|
| range | psi.view.range | ノードの範囲情報 |

### psi.view.range 構造

| フィールド | 型 | 説明 |
|-----------|-----|------|
| start | integer | 開始オフセット（バイト位置） |
| end | integer | 終了オフセット（バイト位置） |

## 表示項目

| 項目名 | 説明 | 形式 |
|--------|------|------|
| ノード名 | ASTノードの型と位置情報 | `type@[line:col .. line:col]` |
| テキストプレビュー | 葉ノードの場合、ソーステキストの先頭30文字 | テキスト |
| 範囲情報 | ノードのバイト位置（開始〜終了） | オフセット値 |

## イベント仕様

### 1-PSIビューリクエスト

LSPリクエスト `$/psi/view` を受信すると、以下の処理が行われます：

1. `script/provider/provider.lua` の `$/psi/view` ハンドラが呼び出される（1585-1598行目）
2. URIから実際のファイルURIを取得する
3. ワークスペースの準備完了を待機する
4. ファイルが存在するか確認する
5. `script/core/view/psi-view.lua` の `core` 関数を呼び出す
6. PSIノード構造を構築してレスポンスとして返却する

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

本機能はデータベースを使用しません。

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| - | - | - | データベース操作なし |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| - | - | - | 本機能は直接メッセージを表示しない |

## 例外処理

| 例外条件 | 処理内容 | レスポンス |
|---------|---------|-----------|
| ファイルが存在しない | nil を返却 | null |
| パース状態が取得できない | nil を返却 | null |
| ASTが空 | nil を返却 | null |

## 備考

- 本機能は `$/` プレフィックスで始まるLSP仕様外の拡張リクエストです
- sumneko.lua VS Code拡張機能専用の機能であり、他のエディタでは利用できない場合があります
- ノード名には1-indexed の行・列情報が含まれます
- 子ノードの範囲が親ノードより広い場合、親の範囲が自動調整されます
- 葉ノード（子を持たないノード）にはソーステキストのプレビュー（最大30文字）が付加されます

---

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

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

### 推奨読解順序

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

まず、PSIノードのデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | psi-view.lua | `script/core/view/psi-view.lua` | `psi.view.node`, `psi.view.attr`, `psi.view.range` クラス定義（8-19行目）でPSIノード構造を理解 |
| 1-2 | guide.lua | `script/parser/guide.lua` | `parser.object` クラスの定義（1-87行目）で元となるASTノード構造を理解 |

**読解のコツ**: PSIノードはASTノードの簡略化表現であり、`name`（型+位置）、`attr`（範囲）、`children`（子ノード）の3要素で構成されます。

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

処理の起点となるLSPハンドラを特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | provider.lua | `script/provider/provider.lua` | `$/psi/view` ハンドラの登録（1585-1598行目）がエントリーポイント |

**主要処理フロー**:
1. **1586行目**: 非同期関数の定義開始
2. **1588行目**: `files.getRealUri()` でURIを正規化
3. **1589行目**: `workspace.awaitReady()` でワークスペース準備完了を待機
4. **1590行目**: `progress.create()` で進捗表示を作成
5. **1591-1593行目**: ファイル存在確認
6. **1594行目**: `core/view/psi-view` モジュールをrequire
7. **1595行目**: `core(uri)` を呼び出してPSIデータを取得
8. **1596行目**: 結果を返却

#### Step 3: PSIノード生成処理を理解する

PSIノード構造の生成ロジックを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | psi-view.lua | `script/core/view/psi-view.lua` | メイン関数（89-96行目）で全体フローを理解 |
| 3-2 | psi-view.lua | `script/core/view/psi-view.lua` | `collectPsi()` 関数（48-87行目）で再帰的なPSI収集を理解 |
| 3-3 | psi-view.lua | `script/core/view/psi-view.lua` | `toPsiNode()` 関数（23-44行目）でASTノードからPSIノードへの変換を理解 |

**主要処理フロー**:
- **89-96行目**: メイン関数でファイル状態を取得し、`collectPsi()` を呼び出す
- **48-87行目**: `collectPsi()` で再帰的にASTを走査し、PSIノードを構築
- **55-64行目**: `guide.eachChild()` で子ノードを列挙し、再帰的にPSIを収集
- **66-74行目**: 子ノードの範囲が親より広い場合の範囲調整
- **76-84行目**: 葉ノードの場合、ソーステキストのプレビューを追加

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

```
$/psi/view handler [provider.lua:1585]
    │
    ├─ files.getRealUri(params.uri) [1588行目]
    │
    ├─ workspace.awaitReady(uri) [1589行目]
    │
    ├─ progress.create(uri, ...) [1590行目]
    │
    ├─ files.exists(uri) [1591行目]
    │
    └─ core(uri) [psi-view.lua:89]
           │
           ├─ files.getState(uri) [90行目]
           │
           └─ collectPsi(state.ast, state) [95行目]
                  │
                  ├─ toPsiNode(astNode, state) [49行目]
                  │      │
                  │      ├─ guide.positionToOffset() [28-29行目]
                  │      │
                  │      └─ converter.packPosition() [30-31行目]
                  │
                  ├─ guide.eachChild(astNode, callback) [55行目]
                  │      └─ collectPsi(child, state) [再帰呼び出し]
                  │
                  └─ subString(state) [77行目] ※葉ノードの場合
```

### データフロー図

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

LSPリクエスト
{uri: "file://..."} ───▶ files.getRealUri() ───▶ 正規化URI
                                                      │
                                                      ▼
                              files.getState() ───▶ AST (state.ast)
                                                      │
                                                      ▼
                              collectPsi() ───▶ PSIノード階層
                                                      │
                                                      ▼
                                            LSPレスポンス
                                            {data: psi.view.node}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSPリクエストハンドラの登録（1585-1598行目） |
| psi-view.lua | `script/core/view/psi-view.lua` | ソース | PSIビューのコア実装 |
| guide.lua | `script/parser/guide.lua` | ソース | AST操作ユーティリティ（eachChild, positionToOffset等） |
| converter.lua | `script/proto/converter.lua` | ソース | 位置情報の変換ユーティリティ |
| files.lua | `script/files.lua` | ソース | ファイル状態管理 |
| substring.lua | `script/core/substring.lua` | ソース | 部分文字列取得ユーティリティ |
| workspace.lua | `script/workspace/init.lua` | ソース | ワークスペース管理 |
| progress.lua | `script/progress.lua` | ソース | 進捗表示管理 |
