# 機能設計書 11-自動require (auto-require)

## 概要

本ドキュメントは、Lua Language Serverにおける自動require機能の設計を記述する。この機能は、コード補完時にモジュールの自動require文挿入を提供する。

### 本機能の処理概要

自動require機能は、ユーザーがコード補完を行う際に、まだrequireされていないモジュールの変数名を入力すると、自動的にファイル先頭にrequire文を挿入する機能である。

**業務上の目的・背景**：Lua開発において、外部モジュールを使用する際には明示的なrequire文が必要である。しかし、開発者がモジュール名を入力するたびに手動でrequire文を追加するのは煩雑であり、開発効率を低下させる。この機能により、モジュール参照時のrequire文挿入を自動化し、コーディング効率を向上させる。

**機能の利用シーン**：
- 新しいモジュールを使用する際に、変数名を入力するだけでrequire文が自動挿入される
- 既存プロジェクト内の他のLuaファイルをモジュールとして参照する場合
- ライブラリパスに存在するモジュールを利用する場合

**主要な処理内容**：
1. ワークスペース内の有効なモジュール（return文を持つファイル）をキャッシュする
2. 補完時に入力された単語とモジュール名のステム名をマッチングする
3. マッチしたモジュールに対してrequire文を生成する
4. 補完候補選択時にファイル先頭にrequire文を追加する

**関連システム・外部連携**：
- ファイルシステム監視機能（files.watch）によるモジュールキャッシュの更新
- ワークスペースのrequireパス解決機能との連携

**権限による制御**：特になし（エディタの設定によりコード補完自体の有効/無効は制御可能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | コード補完 | 補助機能 | モジュールの自動require文挿入 |
| 33 | 自動require | 主機能 | requireステートメントを自動追加するコマンド処理 |

## 機能種別

データ連携 / コード補完補助

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| state | parser.state | Yes | 現在のファイルのパーサー状態 | nil不可 |
| word | string | Yes | 入力中の単語 | 空文字可 |
| position | integer | Yes | カーソル位置 | 正の整数 |
| callback | function | Yes | マッチ結果のコールバック関数 | callable |

### 入力データソース

- 現在編集中のLuaファイルのAST
- ワークスペース内の全Luaファイル
- requireパス設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| uri | uri | マッチしたモジュールのファイルURI |
| stemName | string | モジュールのステム名（変数名として使用） |
| targetSource | parser.object | ターゲットファイルのreturnソース |
| fullKeyPath | string? | 列挙型の場合の完全キーパス |

### 出力先

コールバック関数経由で補完システムに渡される

## 処理フロー

### 処理シーケンス

```
1. 有効なモジュールキャッシュを確認
   └─ m.validUrisにキャッシュされた有効モジュール一覧を使用
2. ワークスペース内の各ファイルをチェック
   └─ 現在のファイルと同一のファイルはスキップ
   └─ 有効でないモジュールはスキップ
3. requireパスからステム名を抽出
   └─ メタファイルの場合はsr.nameをそのまま使用
   └─ 通常ファイルはパターンマッチでステム名を抽出
4. ステム名と入力単語をマッチング
   └─ ローカル変数として既に存在する場合はスキップ
   └─ グローバル変数として既に存在する場合はスキップ
   └─ matchKeyで部分マッチを確認
5. ターゲットファイルのreturnソースを検証
   └─ returnが存在しない場合はスキップ
   └─ deprecatedな場合はスキップ
6. コールバック関数を呼び出し
   └─ マッチしたモジュール情報を返す
7. 列挙型のチェック（モジュールがマッチしなかった場合）
   └─ doc.enumを検索してマッチング
```

### フローチャート

```mermaid
flowchart TD
    A[補完開始] --> B{有効なモジュールキャッシュを確認}
    B --> C[各ファイルをループ]
    C --> D{現在のファイルか?}
    D -->|Yes| C
    D -->|No| E{有効なモジュールか?}
    E -->|No| C
    E -->|Yes| F[requireパスからステム名を抽出]
    F --> G{ローカル/グローバルに存在?}
    G -->|Yes| C
    G -->|No| H{入力単語とマッチ?}
    H -->|No| C
    H -->|Yes| I[ターゲットソースを検証]
    I --> J{有効なreturn?}
    J -->|No| C
    J -->|Yes| K[コールバック呼び出し]
    K --> L{全ファイル処理完了?}
    L -->|No| C
    L -->|Yes| M{マッチあり?}
    M -->|Yes| N[終了]
    M -->|No| O[列挙型をチェック]
    O --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 有効モジュール判定 | return文を持つファイルのみ有効なモジュールとして扱う | ファイルコンパイル時 |
| BR-02 | 重複防止 | 既にローカル/グローバル変数として存在する名前は候補から除外 | 補完候補生成時 |
| BR-03 | 非推奨除外 | @deprecatedアノテーションが付いたモジュールは除外 | 補完候補生成時 |
| BR-04 | ステム名抽出 | ファイルパスから英数字とアンダースコアで構成される名前を抽出 | requireパス解析時 |

### 計算ロジック

ステム名抽出のパターンマッチング:
```lua
-- searcherパターンを正規表現に変換
local pattern = sr.searcher
    : gsub("(%p)", "%%%1")  -- 特殊文字をエスケープ
    : gsub("%%%?", "(.-)")  -- ?を任意文字にマッチするパターンに置換

-- パスからステム名を抽出
local stemName = stemPath:match("[%a_][%w_]*$")
```

## データベース操作仕様

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

この機能はデータベースを使用しない。ファイルシステムとメモリキャッシュのみを使用する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無視 | ターゲットファイルのstateが取得できない | スキップして次のファイルを処理 |
| - | 無視 | ターゲットソースが取得できない | スキップして次のファイルを処理 |
| - | 無視 | ステム名が抽出できない | スキップして次のファイルを処理 |

### リトライ仕様

リトライは行わない。失敗した候補はスキップし、他の候補を返す。

## トランザクション仕様

該当なし（データベースを使用しない）

## パフォーマンス要件

- ファイルコンパイル時にvalidUrisキャッシュを更新し、補完時の処理を高速化
- ワークスペース内のファイル数に比例した処理時間となる
- 補完の応答性を維持するため、非同期処理との連携が前提

## セキュリティ考慮事項

- ワークスペース外のファイルへのアクセスは制限される
- ファイルURIはfuriモジュールで正規化される

## 備考

- ファイル更新/削除時にキャッシュが自動的にクリアされる
- メタファイル（[[meta]]）は特別扱いされる

---

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

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

### 推奨読解順序

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

まず、モジュールキャッシュとrequireパス情報の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | auto-require.lua | `script/core/completion/auto-require.lua` | m.validUris（13-17行目）のキャッシュ構造 |
| 1-2 | require-path.lua | `script/workspace/require-path.lua` | getVisiblePathの戻り値構造 |

**読解のコツ**: `m.validUris`はテーブルで、キーがURI、値がtrueのシンプルなハッシュセットとして機能している。

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

補完システムからの呼び出し経路を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | completion.lua | `script/core/completion/completion.lua` | autoRequireモジュールのrequire（25行目） |
| 2-2 | auto-require.lua | `script/core/completion/auto-require.lua` | m.check関数（38-153行目）がメインエントリーポイント |

**主要処理フロー**:
1. **38行目**: `m.check`関数の開始、パラメータとしてstate、word、position、callbackを受け取る
2. **39-40行目**: diagnostics.globalsとvisibleLocalsを取得
3. **42-97行目**: ワークスペース内の各ファイルをループ処理

#### Step 3: ファイル監視とキャッシュ更新を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | auto-require.lua | `script/core/completion/auto-require.lua` | files.watch（155-166行目）によるキャッシュ管理 |

**主要処理フロー**:
- **155-159行目**: update/removeイベントでキャッシュをクリア
- **160-165行目**: compileイベントで有効なモジュールをキャッシュに追加

#### Step 4: ターゲットソース検証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | auto-require.lua | `script/core/completion/auto-require.lua` | getTargetSource関数（19-36行目）でreturn文を検証 |

**主要処理フロー**:
- **22-25行目**: return文が存在しない場合はnilを返す
- **26-28行目**: 最初のreturn値のソースを取得
- **30-34行目**: getlocal、table、functionのいずれかの型であることを確認

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

```
completion.lua (補完メイン)
    │
    └─ auto-require.lua
           │
           ├─ m.check() [メイン処理]
           │      │
           │      ├─ guide.getVisibleLocals() [可視ローカル変数取得]
           │      ├─ files.eachFile() [ファイルループ]
           │      ├─ rpath.getVisiblePath() [requireパス取得]
           │      ├─ matchKey() [キーワードマッチング]
           │      ├─ files.getState() [ファイル状態取得]
           │      ├─ m.getTargetSource() [ターゲットソース検証]
           │      └─ vm.getDeprecated() [非推奨チェック]
           │
           └─ files.watch() [ファイル監視コールバック]
                  └─ m.getTargetSource() [有効性検証]
```

### データフロー図

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

入力単語(word) ──────────┐
                        │
カーソル位置(position) ──┼──▶ m.check() ──▶ callback()
                        │         │
パーサー状態(state) ─────┘         │
                                  ▼
                         ┌────────────────┐
                         │ validUrisキャッシュ │
                         └────────────────┘
                                  │
                                  ▼
                         ┌────────────────┐
                         │ requireパス解決 │
                         └────────────────┘
                                  │
                                  ▼
                         ┌────────────────┐
                         │ マッチング処理  │
                         └────────────────┘
                                  │
                                  ▼
                         [uri, stemName, targetSource]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| auto-require.lua | `script/core/completion/auto-require.lua` | ソース | 自動require機能のメイン実装 |
| completion.lua | `script/core/completion/completion.lua` | ソース | 補完機能のメインモジュール、auto-requireを呼び出す |
| require-path.lua | `script/workspace/require-path.lua` | ソース | requireパスの解決処理 |
| files.lua | `script/files.lua` | ソース | ファイル管理とイベント監視 |
| matchkey.lua | `script/core/matchkey.lua` | ソース | キーワードマッチング処理 |
| guide.lua | `script/parser/guide.lua` | ソース | AST操作ユーティリティ |
| vm/init.lua | `script/vm/init.lua` | ソース | 仮想マシン（型推論等） |
