# 画面設計書 43-PSI選択

## 概要

本ドキュメントは、Lua Language Serverの内部LSP機能「PSI選択」の画面設計書です。PSI（Program Structure Interface）選択機能は、エディタ上のカーソル位置に対応するAST（抽象構文木）ノードのオフセット位置を取得する開発者向けの内部機能です。

### 本画面の処理概要

**業務上の目的・背景**：言語サーバーの開発者やプラグイン開発者が、エディタ上の任意の位置に対応するASTノードを特定するために使用します。PSIビュー機能と連携して、ユーザーがクリックした位置のノードをハイライトしたり、対応するPSIツリーのノードを選択したりする際に活用されます。この機能により、エディタ上の位置とAST構造の対応関係を把握でき、言語機能のデバッグや開発に役立ちます。

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

**主要な操作・処理内容**：
1. クライアント（エディタ拡張機能）からLSPリクエスト `$/psi/select` を受信する
2. リクエストパラメータからドキュメントURIとカーソル位置を取得する
3. 対象ファイルの状態（パース済みAST）を取得する
4. LSP位置形式（行・列）を内部位置形式に変換する
5. 内部位置をバイトオフセットに変換する
6. オフセット値をレスポンスとして返却する

**画面遷移**：LSPリクエスト/レスポンス形式のため、画面遷移はありません。クライアントがレスポンスを受け取り、対応するPSIノードの選択等に使用します。

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

## 関連機能

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

## 画面種別

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

## URL/ルーティング

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

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|-----|------|------|
| uri | 入力 | string | Yes | 対象ドキュメントのURI |
| position | 入力 | Position | Yes | カーソル位置（行・列） |
| data | 出力 | integer | - | 位置に対応するバイトオフセット |

### Position 構造（入力）

| フィールド | 型 | 説明 |
|-----------|-----|------|
| line | integer | 行番号（0-indexed） |
| character | integer | 文字位置（0-indexed） |

## 表示項目

本機能はデータ取得のみを行い、表示はクライアント側で行われます。

| 項目名 | 説明 | 形式 |
|--------|------|------|
| オフセット | ドキュメント内のバイト位置 | 整数値 |

## イベント仕様

### 1-PSI選択リクエスト

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

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

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

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

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

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

## メッセージ仕様

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

## 例外処理

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

## 備考

- 本機能は `$/` プレフィックスで始まるLSP仕様外の拡張リクエストです
- sumneko.lua VS Code拡張機能専用の機能であり、他のエディタでは利用できない場合があります
- PSIビュー機能（`$/psi/view`）と併用して、クリック位置のノードを特定する用途で使用されます
- 返却されるオフセットはバイト位置であり、PSIノードの `range.start` や `range.end` と照合できます

---

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

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

### 推奨読解順序

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

まず、位置情報のデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | converter.lua | `script/proto/converter.lua` | LSP Position形式と内部位置形式の変換ロジックを理解 |
| 1-2 | guide.lua | `script/parser/guide.lua` | `positionToOffset()` 関数で内部位置からバイトオフセットへの変換を理解 |

**読解のコツ**: LSPのPosition（line, character）は0-indexed、内部位置はオフセット形式です。`unpackPosition`で変換し、`positionToOffset`でバイト位置に変換します。

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

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

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

**主要処理フロー**:
1. **1601行目**: 非同期関数の定義開始
2. **1603行目**: `files.getRealUri()` でURIを正規化
3. **1604行目**: `workspace.awaitReady()` でワークスペース準備完了を待機
4. **1605行目**: `progress.create()` で進捗表示を作成
5. **1606-1608行目**: ファイル存在確認
6. **1609行目**: `core/view/psi-select` モジュールをrequire
7. **1610行目**: `core(uri, params.position)` を呼び出してオフセットを取得
8. **1611行目**: 結果を返却

#### Step 3: オフセット変換処理を理解する

位置からオフセットへの変換ロジックを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | psi-select.lua | `script/core/view/psi-select.lua` | メイン関数（5-13行目）で全体フローを理解 |

**主要処理フロー**:
- **5-9行目**: ファイル状態を取得し、存在しない場合はnilを返却
- **11行目**: `converter.unpackPosition()` でLSP位置を内部位置に変換
- **12行目**: `guide.positionToOffset()` でバイトオフセットに変換して返却

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

```
$/psi/select handler [provider.lua:1600]
    │
    ├─ files.getRealUri(params.uri) [1603行目]
    │
    ├─ workspace.awaitReady(uri) [1604行目]
    │
    ├─ progress.create(uri, ...) [1605行目]
    │
    ├─ files.exists(uri) [1606行目]
    │
    └─ core(uri, params.position) [psi-select.lua:5]
           │
           ├─ files.getState(uri) [6行目]
           │
           ├─ converter.unpackPosition(state, position) [11行目]
           │
           └─ guide.positionToOffset(state, pos) [12行目]
```

### データフロー図

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

LSPリクエスト
{uri: "file://...",
 position: {line: 0,
            character: 5}} ───▶ files.getRealUri() ───▶ 正規化URI
                                                            │
                                                            ▼
                                    files.getState() ───▶ state
                                                            │
                                                            ▼
position ───▶ converter.unpackPosition() ───▶ 内部位置(pos)
                                                            │
                                                            ▼
                         guide.positionToOffset() ───▶ バイトオフセット
                                                            │
                                                            ▼
                                                  LSPレスポンス
                                                  {data: 5}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSPリクエストハンドラの登録（1600-1613行目） |
| psi-select.lua | `script/core/view/psi-select.lua` | ソース | PSI選択のコア実装 |
| guide.lua | `script/parser/guide.lua` | ソース | AST操作ユーティリティ（positionToOffset） |
| converter.lua | `script/proto/converter.lua` | ソース | 位置情報の変換ユーティリティ（unpackPosition） |
| files.lua | `script/files.lua` | ソース | ファイル状態管理 |
| workspace.lua | `script/workspace/init.lua` | ソース | ワークスペース管理 |
| progress.lua | `script/progress.lua` | ソース | 進捗表示管理 |
