# lua-language-server コードリーディングガイドライン

## はじめに

このガイドラインは、lua-language-serverのコードベースを効率的に理解するための手引きです。
Luaに精通していないエンジニアでも、段階的に学習できるよう構成されています。

**対象読者:**
- プロジェクトに新規参画するエンジニア
- 他言語からの経験者
- コードレビューを行う担当者

---

## 1. 言語基礎

> このセクションでは、Luaの基本構文と概念を解説します。

### 1.1 プログラム構造

Luaはシンプルで軽量なスクリプト言語です。本プロジェクトはLua 5.5をターゲットとしています。

**基本的なモジュール構造の例:**
```lua
-- ファイル: script/vm/vm.lua:1-14
local guide     = require 'parser.guide'
local files     = require 'files'
local timer     = require 'timer'

local setmetatable   = setmetatable
local log            = log
local xpcall         = xpcall
local mathHuge       = math.huge

local weakMT = { __mode = 'kv' }

---@class vm
local m = {}
```

**ポイント:**
- `local` でローカル変数を宣言
- `require` で他のモジュールを読み込み
- `---@class` はLuaDocアノテーション（型注釈）
- モジュールはテーブル `m = {}` として定義し、最後に `return m` で公開

### 1.2 データ型と変数

Luaの主要なデータ型は以下の通りです:

| 型 | 説明 | 例 |
|---|---|---|
| nil | 値がないことを示す | `nil` |
| boolean | 真偽値 | `true`, `false` |
| number | 数値（整数・浮動小数点） | `42`, `3.14` |
| string | 文字列 | `"hello"`, `'world'` |
| table | 配列・連想配列（オブジェクト） | `{}`, `{1, 2, 3}` |
| function | 関数 | `function() end` |

**実例: 変数宣言パターン** (`script/files.lua:44-51`)
```lua
m.watchList      = {}
m.notifyCache    = {}
m.assocVersion   = -1

function m.reset()
    m.openMap        = {}
    ---@type table<string, file>
    m.fileMap        = {}
```

### 1.3 制御構造

**条件分岐:** (`main.lua:7-17`)
```lua
local function getValue(value)
    if     value == 'true' or value == nil then
        value = true
    elseif value == 'false' then
        value = false
    elseif tonumber(value) then
        value = tonumber(value)
    elseif value:sub(1, 1) == '"' and value:sub(-1, -1) == '"' then
        value = value:sub(2, -2)
    end
    return value
end
```

**ループ:** (`script/core/diagnostics/init.lua:178-190`)
```lua
for _, name in ipairs(buildDiagList()) do
    await.delay()
    local clock = os.clock()
    local suc = check(uri, name, isScopeDiag, response, ignoreFileOpenState)
    if suc then
        local cost = os.clock() - clock
        diagCosts[name] = (diagCosts[name] or 0) + cost
        diagCount[name] = (diagCount[name] or 0) + 1
    end
end
```

### 1.4 関数/メソッド定義

**通常の関数定義:** (`script/service/service.lua:26-36`)
```lua
local function countMemory()
    local mems = {}
    local total  = 0
    mems[0] = collectgarbage 'count'
    total = total + collectgarbage 'count'
    for id, brave in ipairs(pub.allBraves) do
        mems[id] = brave.memory
        total = total + brave.memory
    end
    return total, mems
end
```

**モジュールメソッド:** (`script/proto/proto.lua:96-101`)
```lua
function m.notify(name, params)
    m.send {
        method = name,
        params = params,
    }
end
```

**非同期関数（コルーチン）:** (`script/proto/proto.lua:103-123`)
```lua
---@async
function m.awaitRequest(name, params)
    local id  = reqCounter()
    m.send {
        id     = id,
        method = name,
        params = params,
    }
    local result, error = await.wait(function (resume)
        m.waiting[id] = {
            id     = id,
            method = name,
            params = params,
            resume = resume,
        }
    end)
    return result
end
```

### 1.5 モジュール/インポート

**モジュールの読み込み:** (`script/service/service.lua:1-16`)
```lua
---@diagnostic disable: deprecated
local pub    = require 'pub'
local await  = require 'await'
local timer  = require 'timer'
local proto  = require 'proto'
local vm     = require 'vm'
local util   = require 'utility'
local files  = require 'files'
local lang   = require 'language'
local ws     = require 'workspace'
local time   = require 'bee.time'
local fw     = require 'filewatch'
local furi   = require 'file-uri'
local net    = require 'service.net'
local client = require 'client'
```

**モジュールパスのルール:**
- `script/` ディレクトリがベースパス
- `require 'parser.guide'` は `script/parser/guide.lua` を参照
- `require 'parser'` は `script/parser/init.lua` を参照

---

## 2. プロジェクト固有の概念

> このセクションでは、当プロジェクト特有の概念を解説します。

### 2.1 Language Server Protocol (LSP)

本プロジェクトはLanguage Server Protocolを実装したLua言語サーバーです。

**主要なLSP機能:**
- `textDocument/completion` - コード補完
- `textDocument/hover` - ホバー情報
- `textDocument/definition` - 定義へ移動
- `textDocument/references` - 参照検索
- `textDocument/rename` - リネーム
- `textDocument/diagnostics` - 診断（エラー・警告）

**LSPハンドラの登録パターン:** (`script/provider/provider.lua:340-385`)
```lua
m.register 'textDocument/hover' {
    capability = {
        hoverProvider = true,
    },
    abortByFileUpdate = true,
    ---@async
    function (params)
        local doc    = params.textDocument
        local uri    = files.getRealUri(doc.uri)
        -- ... 処理
        local core = require 'core.hover'
        local hover, source, maxLevel = core.byUri(uri, pos, params.level or 1)
        return {
            contents = {
                value = value,
                kind  = 'markdown',
            },
        }
    end
}
```

### 2.2 LuaDocアノテーション

本プロジェクトでは型注釈にLuaDocを使用しています。

**主要なアノテーション:**

| アノテーション | 説明 | 例 |
|---|---|---|
| `---@class` | クラス定義 | `---@class vm` |
| `---@param` | パラメータ型 | `---@param uri uri` |
| `---@return` | 戻り値型 | `---@return string?` |
| `---@type` | 変数型 | `---@type table<string, file>` |
| `---@async` | 非同期関数 | `---@async` |
| `---@field` | フィールド定義 | `---@field uri uri` |
| `---@alias` | 型エイリアス | `---@alias uri string` |

**実例:** (`script/parser/guide.lua:3-86`)
```lua
---@class parser.object
---@field bindDocs              parser.object[]
---@field bindGroup             parser.object[]
---@field bindSource            parser.object
---@field value                 parser.object
---@field parent                parser.object
---@field type                  string
---@field uri                   uri
---@field start                 integer
---@field finish                integer
```

### 2.3 非同期処理（await）

コルーチンベースの非同期処理システムを使用しています。

**awaitモジュールの使用パターン:** (`script/await.lua:42-66`)
```lua
--- 創建一個任務
---@param callback async fun()
function m.call(callback, ...)
    local co = coroutine.create(callback)
    local closers = {}
    m.coMap[co] = {
        closers  = closers,
        priority = false,
    }
    -- ...
    return m.checkResult(co, coroutine.resume(co))
end
```

---

## 3. 命名規則

> このセクションでは、プロジェクト全体で使用される命名規則を解説します。

### 3.1 ファイル・ディレクトリ命名

| パターン | 意味 | 例 |
|---------|------|-----|
| `小文字-ハイフン区切り` | 一般的なファイル名 | `code-action.lua`, `file-uri.lua` |
| `init.lua` | ディレクトリのエントリーポイント | `script/vm/init.lua` |
| `小文字アンダースコア` | 一部のファイル | `workspace_symbol.lua` |

### 3.2 クラス・関数・変数命名

| プレフィックス/サフィックス | 意味 | 例 |
|---------------------------|------|-----|
| `m.` | モジュールのパブリックメンバー | `m.reset()`, `m.fileMap` |
| `local function` | モジュール内プライベート関数 | `local function countMemory()` |
| `_` プレフィックス | プライベートまたは未使用変数 | `_ENV = nil`, `_*` |
| `UPPER_CASE` | グローバル定数 | `ROOT`, `LOGPATH`, `METAPATH` |

### 3.3 プログラム分類一覧

| カテゴリ | ディレクトリ | 説明 |
|---------|-------------|------|
| コア機能 | `script/core/` | LSP機能の実装（completion, hover等） |
| パーサー | `script/parser/` | Luaソースコードの構文解析 |
| 仮想マシン | `script/vm/` | 型推論・シンボル解析 |
| プロトコル | `script/proto/` | LSP通信処理 |
| サービス | `script/service/` | サーバーメインループ |
| プロバイダー | `script/provider/` | LSPメソッドハンドラ登録 |

---

## 4. ディレクトリ構造

> このセクションでは、プロジェクトのディレクトリ構造を解説します。

```
lua-language-server/
├── main.lua              # エントリーポイント
├── script/               # メインソースコード
│   ├── core/             # LSP機能の実装
│   │   ├── completion/   # コード補完
│   │   ├── diagnostics/  # 診断機能
│   │   ├── hover/        # ホバー情報
│   │   ├── command/      # LSPコマンド
│   │   └── ...
│   ├── parser/           # Luaパーサー
│   │   ├── compile.lua   # ソースコンパイル
│   │   ├── guide.lua     # AST操作ユーティリティ
│   │   └── luadoc.lua    # LuaDocパーサー
│   ├── vm/               # 仮想マシン（型推論）
│   │   ├── compiler.lua  # 型コンパイラ
│   │   ├── infer.lua     # 型推論
│   │   └── ...
│   ├── proto/            # LSPプロトコル
│   │   ├── proto.lua     # 通信処理
│   │   └── define.lua    # 定数定義
│   ├── provider/         # LSPメソッド登録
│   │   ├── provider.lua  # メインプロバイダー
│   │   └── diagnostic.lua# 診断プロバイダー
│   ├── service/          # サーバーサービス
│   │   └── service.lua   # メインループ
│   ├── workspace/        # ワークスペース管理
│   ├── config/           # 設定管理
│   └── ...
├── test/                 # テストコード
│   ├── definition/       # 定義テスト
│   ├── completion/       # 補完テスト
│   ├── diagnostics/      # 診断テスト
│   └── ...
├── locale/               # 多言語対応
│   ├── en-us/
│   ├── ja-jp/
│   └── ...
├── meta/                 # メタデータ
└── 3rd/                  # サードパーティライブラリ
```

### 各ディレクトリの役割

| ディレクトリ | 役割 | 主要ファイル |
|-------------|------|-------------|
| `script/core/` | LSP機能の実装 | `completion.lua`, `hover/init.lua` |
| `script/parser/` | Luaパーサー | `compile.lua`, `guide.lua` |
| `script/vm/` | 型推論エンジン | `compiler.lua`, `infer.lua` |
| `script/proto/` | LSP通信 | `proto.lua`, `define.lua` |
| `script/provider/` | LSPハンドラ | `provider.lua` |
| `script/service/` | サーバー管理 | `service.lua` |

---

## 5. アーキテクチャ

> このセクションでは、プロジェクトのアーキテクチャパターンを解説します。

### 5.1 全体アーキテクチャ

本プロジェクトはレイヤードアーキテクチャを採用しています。

```
┌─────────────────────────────────────────────────┐
│                  LSP Client                      │
│              (VS Code, NeoVim等)                 │
└────────────────────┬────────────────────────────┘
                     │ JSON-RPC (stdin/stdout or socket)
                     ▼
┌─────────────────────────────────────────────────┐
│              Protocol Layer                      │
│         (script/proto/proto.lua)                │
│    - メッセージのエンコード/デコード              │
│    - リクエスト/レスポンス管理                    │
└────────────────────┬────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────┐
│              Provider Layer                      │
│      (script/provider/provider.lua)             │
│    - LSPメソッドのルーティング                   │
│    - 各機能への振り分け                          │
└────────────────────┬────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────┐
│               Core Layer                         │
│           (script/core/*)                        │
│    - hover, completion, definition等             │
│    - 各LSP機能の実装                             │
└────────────────────┬────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────┐
│             VM/Analysis Layer                    │
│           (script/vm/*, script/parser/*)        │
│    - 型推論、シンボル解析                        │
│    - AST生成・操作                               │
└────────────────────┬────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────┐
│              Files Layer                         │
│           (script/files.lua)                     │
│    - ファイル管理                                │
│    - キャッシュ管理                              │
└─────────────────────────────────────────────────┘
```

### 5.2 レイヤー構成

| レイヤー | 責務 | 代表的なファイル |
|---------|------|-----------------|
| Protocol | LSP通信、JSON-RPC | `proto/proto.lua` |
| Provider | LSPメソッド登録・ルーティング | `provider/provider.lua` |
| Core | 各LSP機能の実装 | `core/hover/init.lua` |
| VM | 型推論・シンボル解析 | `vm/compiler.lua` |
| Parser | 構文解析 | `parser/compile.lua` |
| Files | ファイル管理・キャッシュ | `files.lua` |

### 5.3 データフロー

**リクエスト処理の流れ:**

1. **受信**: `proto.lua` がJSON-RPCメッセージを受信
2. **ルーティング**: `provider.lua` が対応するハンドラにディスパッチ
3. **処理**: `core/` 配下の機能が処理を実行
4. **解析**: 必要に応じて `vm/` で型推論、`parser/` でAST操作
5. **レスポンス**: 結果を `proto.lua` 経由でクライアントに返却

---

## 6. 主要コンポーネント

> このセクションでは、主要なコンポーネントとその連携を解説します。

### 6.1 エントリーポイント

**main.lua:** サーバーの起動ポイント

```lua
-- main.lua:81-86
require 'cli'

local _, service = xpcall(require, log.error, 'service')

service.start()
```

**service.start():** (`script/service/service.lua:267-288`)
```lua
function m.start()
    util.enableCloseFunction()
    await.setErrorHandle(log.error)
    pub.recruitBraves(4)
    if COMPILECORES and COMPILECORES > 0 then
        pub.recruitBraves(COMPILECORES, 'compile')
    end
    if SOCKET then
        proto.listen('socket', SOCKET)
    else
        proto.listen('stdio')
    end
    m.report()
    m.lockCache()

    require 'provider'

    m.sayHello()
    m.eventLoop()
end
```

### 6.2 ビジネスロジック

**core/completion:** コード補完の実装

```lua
-- script/core/completion/completion.lua:1-26
local define       = require 'proto.define'
local files        = require 'files'
local matchKey     = require 'core.matchkey'
local vm           = require 'vm'
local getName      = require 'core.hover.name'
local getArgs      = require 'core.hover.args'
local getHover     = require 'core.hover'
local config       = require 'config'
local util         = require 'utility'
-- ...
```

**core/diagnostics:** 診断機能の実装

```lua
-- script/core/diagnostics/init.lua:119
require('core.diagnostics.' .. name)(uri, function (result)
    if vm.isDiagDisabledAt(uri, result.start, name) then
        return
    end
    result.level = level or result.level
    result.code  = name
    response(result)
end, name)
```

### 6.3 データアクセス

**files.lua:** ファイル管理

```lua
-- script/files.lua:120-141
--- 打開文件
---@param uri uri
function m.open(uri)
    m.openMap[uri] = {
        cache = {},
    }
    m.onWatch('open', uri)
end

--- 關閉文件
---@param uri uri
function m.close(uri)
    m.openMap[uri] = nil
    local file = m.fileMap[uri]
    if file then
        file.trusted = false
    end
    m.onWatch('close', uri)
end
```

### 6.4 ユーティリティ/共通機能

**utility.lua:** 汎用ユーティリティ関数

```lua
-- script/utility.lua:85-99
---@class Utility
local m = {}

--- 打印表的结构
---@param tbl any
---@param option? table
---@return string
function m.dump(tbl, option)
    if not option then
        option = {}
    end
    if type(tbl) ~= 'table' then
        return ('%s'):format(tbl)
    end
    -- ...
end
```

---

## 7. よく使われるパターン

> このセクションでは、コード内で頻出するパターンを解説します。

### パターン一覧

| パターン | 説明 | 出現頻度 | 代表的なファイル |
|---------|------|---------|-----------------|
| モジュールパターン | `local m = {}` + `return m` | 高 | 全ファイル |
| コールバック登録 | `m.register 'method' {...}` | 高 | `provider/provider.lua` |
| 非同期処理 | `---@async` + `await.xxx` | 高 | `service/service.lua` |
| 型注釈 | `---@param`, `---@return` | 高 | 全ファイル |
| キャッシュパターン | `vm.getCache(name)` | 中 | `vm/vm.lua` |

### 各パターンの詳細

#### パターン1: モジュールパターン

**目的:** モジュールの公開インターフェースを定義

**実装例:**
```lua
-- ファイル: script/vm/vm.lua:12-14
---@class vm
local m = {}

-- ... モジュール実装 ...

return m
```

**解説:** Luaの標準的なモジュール定義パターン。`local m = {}` でモジュールテーブルを作成し、`return m` でエクスポート。

#### パターン2: LSPハンドラ登録パターン

**目的:** LSPメソッドと処理関数を紐付け

**実装例:**
```lua
-- ファイル: script/provider/provider.lua:420-443
m.register 'textDocument/definition' {
    capability = {
        definitionProvider = true,
    },
    abortByFileUpdate = true,
    ---@async
    function (params)
        local uri    = files.getRealUri(params.textDocument.uri)
        workspace.awaitReady(uri)
        local state = files.getState(uri)
        if not state then
            return nil
        end
        local core   = require 'core.definition'
        local pos = converter.unpackPosition(state, params.position)
        local result = core(uri, pos)
        return response
    end
}
```

**解説:** `m.register` でLSPメソッド名と処理関数を登録。`capability` でLSP機能の有効化を宣言。

#### パターン3: 遅延読み込みパターン

**目的:** 必要なときにのみモジュールを読み込み

**実装例:**
```lua
-- ファイル: script/provider/provider.lua:362
local core = require 'core.hover'
```

**解説:** 関数内で `require` することで、初期化時の負荷を軽減し、必要な時だけモジュールをロード。

---

## 8. 業務フロー追跡の実践例

> このセクションでは、実際の業務フローをコードで追跡する方法を解説します。

### 8.1 フロー追跡の基本手順

1. エントリーポイントを特定
2. 処理の流れを追跡（呼び出し関係を追う）
3. データの変換を確認
4. 最終的な出力を確認

### 8.2 フロー追跡の実例

#### 例1: hover機能の処理フロー

**概要:** ユーザーがコード上にマウスをホバーした時の情報表示

**処理フロー:**
```
proto.lua (受信) → provider.lua (ルーティング) → core/hover (処理) → vm (型推論) → proto.lua (送信)
```

**詳細な追跡:**

1. **プロトコル受信** (`script/proto/proto.lua:161-205`)
   ```lua
   function m.applyMethod(proto)
       logRecieve(proto)
       local method, optional = m.getMethodName(proto)
       local abil = m.ability[method]
       -- ...
       await.call(function () ---@async
           local ok = false
           local res
           ok, res = xpcall(abil, log.error, proto.params, proto.id)
       end)
   end
   ```

2. **ハンドラ登録** (`script/provider/provider.lua:340-385`)
   ```lua
   m.register 'textDocument/hover' {
       capability = {
           hoverProvider = true,
       },
       ---@async
       function (params)
           local core = require 'core.hover'
           local hover, source, maxLevel = core.byUri(uri, pos, params.level or 1)
           return {
               contents = {
                   value = value,
                   kind  = 'markdown',
               },
           }
       end
   }
   ```

3. **コア処理** (`script/core/hover/init.lua:13-55`)
   ```lua
   local function getHover(source, level)
       local md        = markdown()
       local defMark   = {}
       local labelMark = {}
       local descMark  = {}
       -- ...
       local function addHover(def, checkLable, oop)
           local label, maxLevel = getLabel(def, oop, level)
           md:add('lua', label)
           local desc  = getDesc(def)
           md:add('md', desc)
       end
       -- ...
   end
   ```

### 8.3 フロー追跡チェックリスト

- [ ] エントリーポイントを特定したか
- [ ] 呼び出し関係を把握したか
- [ ] データの変換ポイントを確認したか
- [ ] エラーハンドリングを確認したか
- [ ] 最終的な出力を確認したか

---

## 9. 設計書の参照順序

> このセクションでは、プロジェクト理解のための設計書参照順序を案内します。

### 9.1 目的別ロードマップ

#### 全体像を把握したい場合
1. `README.md` - プロジェクト概要
2. `main.lua` - エントリーポイント
3. `script/service/service.lua` - サーバー起動フロー
4. `script/provider/provider.lua` - LSP機能一覧

#### 特定機能を理解したい場合
1. `script/provider/provider.lua` で該当機能のハンドラを確認
2. `script/core/` 配下の対応するモジュールを読む
3. 必要に応じて `script/vm/` の型推論処理を確認

#### 改修作業を行う場合
1. 対象機能の `script/core/` モジュール
2. `test/` 配下の対応するテストケース
3. `script/vm/` 配下の関連モジュール

### 9.2 ドキュメント一覧

| ドキュメント | 概要 | 参照タイミング |
|-------------|------|---------------|
| `README.md` | プロジェクト概要 | 初回参照時 |
| `changelog.md` | 変更履歴 | バージョン確認時 |
| `doc/` | 多言語ドキュメント | ユーザー向け説明確認時 |
| `.luarc.json` | 設定スキーマ | 設定項目確認時 |

---

## 10. トラブルシューティング

> このセクションでは、コードリーディング時によくある問題と解決法を解説します。

### よくある疑問と回答

#### Q: `require 'xxx'` のパスがどこを指しているかわからない
A: `.luarc.json` の `runtime.path` を確認。基本的に `script/` がベースパス。`require 'parser.guide'` は `script/parser/guide.lua` を参照。

#### Q: `---@async` の意味は？
A: 非同期関数を示すLuaDocアノテーション。`await.xxx` を使用してコルーチンベースの非同期処理を行う関数に付与される。

#### Q: グローバル変数 `ROOT`, `LOGPATH` はどこで定義されている？
A: `main.lua:52-54` で定義。コマンドライン引数で上書き可能。

#### Q: テストの実行方法は？
A: `test.lua` を実行。特定のテストのみ実行する場合は `--name=テスト名` オプションを使用。

### デバッグのヒント

1. **ログの有効化**: `log.print = true` でコンソール出力を有効化
2. **RPCLOG**: グローバル変数 `RPCLOG = true` でLSP通信ログを出力
3. **DEVELOP**: `DEVELOP = true` で開発モードを有効化

---

## 付録

### A. 用語集

| 用語 | 説明 |
|-----|------|
| LSP | Language Server Protocol。エディタと言語サーバー間の通信プロトコル |
| URI | Uniform Resource Identifier。ファイルの識別子 |
| AST | Abstract Syntax Tree。ソースコードの構文木 |
| LuaDoc | Luaの型注釈記法 |
| vm | Virtual Machine。本プロジェクトでは型推論エンジンを指す |
| node | AST内のノード、または型ノード |
| scope | ワークスペースのスコープ（フォルダ単位） |

### B. ファイル一覧

| ファイル/ディレクトリ | 説明 | 主な内容 |
|---------------------|------|---------|
| `main.lua` | エントリーポイント | サーバー起動処理 |
| `script/service/` | サービス層 | イベントループ、メモリ管理 |
| `script/proto/` | プロトコル層 | JSON-RPC通信 |
| `script/provider/` | プロバイダー層 | LSPハンドラ登録 |
| `script/core/` | コア層 | LSP機能実装 |
| `script/vm/` | 仮想マシン層 | 型推論、シンボル解析 |
| `script/parser/` | パーサー層 | 構文解析、AST生成 |
| `script/files.lua` | ファイル管理 | ファイルキャッシュ |
| `script/workspace/` | ワークスペース | プロジェクト管理 |

### C. 参考資料

- [Language Server Protocol仕様](https://microsoft.github.io/language-server-protocol/)
- [Lua 5.4 Reference Manual](https://www.lua.org/manual/5.4/)
- [LuaLS Wiki](https://luals.github.io/wiki/)
- [EmmyLuaアノテーション](https://emmylua.github.io/annotation.html)
