# 機能設計書 56-構文ハイライト

## 概要

本ドキュメントは、clojure-lspにおける構文ハイライト（Semantic Tokens）機能の設計を記述する。この機能はLSPプロトコルの`textDocument/semanticTokens/full`リクエストを実装し、セマンティック情報に基づく高度な構文ハイライトを提供する。

### 本機能の処理概要

**業務上の目的・背景**：
従来の構文ハイライトは、正規表現やテキストマッチングに基づく字句解析で実現されていた。しかし、Clojureのようなホモイコニックな言語では、同じシンボルが文脈によって関数、マクロ、変数、名前空間など異なる役割を持つ。Semantic Tokens機能は、clj-kondoの解析結果を活用してシンボルの意味的な役割を特定し、より正確で豊富な構文ハイライトを実現する。

**機能の利用シーン**：
1. エディタでコードを閲覧する際の視認性向上
2. マクロと関数の視覚的な区別
3. ローカル変数とグローバル変数の識別
4. 名前空間エイリアスの認識

**主要な処理内容**：
1. ドキュメントの解析データを取得
2. 各要素をトークンタイプとモディファイアにマッピング
3. 絶対位置から相対位置への変換
4. LSP形式のトークン配列として返却

**関連システム・外部連携**：
- clj-kondo：解析データ（bucket情報）の提供
- rewrite-clj：unevalノード（#_コメント）の検出
- LSPクライアント（エディタ）：セマンティックハイライトの描画

**権限による制御**：
`semantic-tokens?`設定で機能を有効/無効化可能（デフォルト: true）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| N/A | エディタ | 主画面 | 構文ハイライトの表示 |

## 機能種別

情報表示処理 / データ変換処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| textDocument.uri | string | Yes | 対象ドキュメントのURI | 有効なファイルURI形式 |

### 入力データソース

- LSPクライアントからの`textDocument/semanticTokens/full`リクエスト
- 解析データ（db.analysis）の各bucket

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| data | array<integer> | 相対位置エンコードされたトークン配列 |

各トークンは5つの整数で表現:
1. deltaLine: 前のトークンからの行差分
2. deltaStart: 前のトークンからの列差分（同一行の場合）/ 列位置（新しい行の場合）
3. length: トークンの長さ
4. tokenType: トークンタイプのインデックス
5. tokenModifiers: トークンモディファイアのビットマスク

### 出力先

LSPクライアントへのレスポンス

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ textDocument/semanticTokens/fullを受信
2. 解析データ取得
   └─ URIに対応するbucket情報を取得
3. 要素分類
   └─ 各要素をbucketタイプに応じてトークンに変換
4. クジラノードトークン追加
   └─ #_（uneval）ノードをcommentトークンとして追加
5. ソート
   └─ 行/列でトークンをソート
6. 相対位置変換
   └─ 絶対位置を相対位置にエンコード
7. レスポンス返却
```

### フローチャート

```mermaid
flowchart TD
    A[semanticTokens/full受信] --> B[解析データ取得]
    B --> C[各bucketを処理]
    C --> D{bucket種別は?}
    D -->|var-usages| E[var-usage->tokens]
    D -->|var-definitions| F[var-definition->tokens]
    D -->|locals/local-usages| G[variable token]
    D -->|namespace-definitions| H[namespace token]
    D -->|keywords| I[keyword->tokens]
    D -->|java-class-*| J[class token]
    D -->|instance-invocations| K[method token]
    D -->|protocol-impls| L[method+implementation token]
    E --> M[トークン収集]
    F --> M
    G --> M
    H --> M
    I --> M
    J --> M
    K --> M
    L --> M
    M --> N{#_ノードあり?}
    N -->|Yes| O[commentトークン追加]
    N -->|No| P[ソート]
    O --> P
    P --> Q[相対位置変換]
    Q --> R[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-56-01 | トークンタイプ | namespace, type, function, macro, keyword, class, variable, method, event, interface, comment | 常時 |
| BR-56-02 | モディファイア | definition, defaultLibrary, implementation | 常時 |
| BR-56-03 | マクロ優先 | :macroフラグがあればmacroタイプを優先 | var-usages |
| BR-56-04 | エイリアス分割 | alias/name形式は3トークン（alias, /, name）に分割 | var-usages, keywords |
| BR-56-05 | イヤーマフ変数 | *name*形式はdefaultLibraryモディファイアを付与 | var-usages |
| BR-56-06 | #_コメント | unevalノードはcommentタイプとして扱う | #_が含まれる場合 |

### 計算ロジック

**トークンモディファイアのビットマスク計算**:
```clojure
;; モディファイア配列: [:definition :defaultLibrary :implementation]
;; 例: [:definition] -> 0b001 -> 1
;;     [:defaultLibrary] -> 0b010 -> 2
;;     [:definition :implementation] -> 0b101 -> 5
(defn token-modifiers->decimal-bit [modifiers]
  (as-> token-modifiers $
    (mapv #(if (contains? (set modifiers) %) 1 0) $)
    (rpad-seq $ 8 "0")
    (reverse $)
    (string/join "" $)
    (Integer/parseInt $ 2)))
```

**相対位置への変換**:
```clojure
;; 前のトークンと同じ行の場合: [0, col-diff, length, type, mod]
;; 新しい行の場合: [row-diff, col, length, type, mod]
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 解析データ取得 | db.analysis | SELECT | 対象URIのbucket情報を取得 |
| テキスト取得 | db.documents | SELECT | #_検出用にテキストを取得 |

### テーブル別操作詳細

#### db.analysis

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全buckets | uri = 対象URI | var-usages, var-definitions等 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | タイムアウト | 変更待機超過 | エラーレスポンス返却 |
| - | キャンセル | クライアントキャンセル | 処理中断 |

### リトライ仕様

リトライは行わない。

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

読み取り専用のため、トランザクション管理は不要。

## パフォーマンス要件

- ファイル表示時に呼び出されるため、高速な応答が必要
- 大きなファイルでも数百ミリ秒以内を目標

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

- 特になし

## 備考

- サポートするトークンタイプとモディファイアは`semantic-tokens-provider`でクライアントに通知
- `semantic-tokens?`設定でこの機能を無効化可能

---

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

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

### 推奨読解順序

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

Semantic Tokensのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **13-24行目**: token-types定義 |
| 1-2 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **31-38行目**: token-modifiers定義 |

**読解のコツ**: トークンは[行, 列, 長さ, タイプインデックス, モディファイアビット]の5整数タプル。最終的に相対位置にエンコードされる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | server.clj | `cli/src/clojure_lsp/server.clj` | **389-394行目**: semanticTokens/fullハンドラ |
| 2-2 | handlers.clj | `lib/src/clojure_lsp/handlers.clj` | **454-461行目**: semantic-tokens-full関数 |

**主要処理フロー**:
1. **389-394行目**: `textDocument/semanticTokens/full`リクエストを受信
2. **454-461行目**: `process-after-changes`で変更待機後に`f.semantic-tokens/full-tokens`呼び出し

#### Step 3: トークン生成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **257-263行目**: `full-tokens`関数 |

**主要処理フロー**:
- **258行目**: URIに対応するbucketsを取得
- **259行目**: `elements->absolute-tokens`でkondoトークンを生成
- **260行目**: `rewrite-clj-tokens*`で#_トークンを生成
- **261-262行目**: 全トークンをソート
- **263行目**: `absolute-tokens->relative-tokens`で相対位置に変換

#### Step 4: 要素からトークンへの変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **174-207行目**: `elements->absolute-tokens`関数 |
| 4-2 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **110-147行目**: `var-usage-element->absolute-tokens` |
| 4-3 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **94-108行目**: `var-definition-element->absolute-tokens` |
| 4-4 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **149-172行目**: `keywords->absolute-tokens` |

**主要処理フロー**:
- **174-207行目**: bucketタイプに応じて適切な変換関数を呼び出し
- **110-147行目**: var-usageをfunction/macro/typeトークンに変換
- **94-108行目**: var-definitionをfunction/interfaceトークンに変換
- **149-172行目**: キーワードをtype/event/keywordトークンに変換

#### Step 5: 相対位置変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **209-228行目**: `absolute-token->relative-token` |
| 5-2 | semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | **230-235行目**: `absolute-tokens->relative-tokens` |

**主要処理フロー**:
- **209-228行目**: 前のトークンとの差分を計算
- **230-235行目**: partition 2 1でペアを作り差分変換を適用

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

```
textDocument/semanticTokens/full (server.clj:389-394)
    │
    ├─ handler/semantic-tokens-full (handlers.clj:454-461)
    │      │
    │      └─ f.semantic-tokens/full-tokens (semantic_tokens.clj:257-263)
    │             │
    │             ├─ elements->absolute-tokens (semantic_tokens.clj:174-207)
    │             │      │
    │             │      ├─ var-usage-element->absolute-tokens (110-147)
    │             │      ├─ var-definition-element->absolute-tokens (94-108)
    │             │      ├─ keywords->absolute-tokens (149-172)
    │             │      ├─ java-class-element->absolute-tokens (80-92)
    │             │      └─ element->absolute-token (68-78)
    │             │
    │             ├─ rewrite-clj-tokens* (semantic_tokens.clj:245-255)
    │             │      └─ z/find-tag :uneval
    │             │
    │             └─ absolute-tokens->relative-tokens (230-235)
    │                    └─ absolute-token->relative-token (209-228)
    │
    └─ conform-or-log
```

### データフロー図

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

semanticTokens/full        ┌──────────────────────────────────┐
Request                    │                                  │
  └─ uri ─────────────────►│  full-tokens                     │
                           │                                  │
db.analysis                │    ├─ elements->absolute-tokens  │
  ├─ var-usages ──────────►│    │    ├─ var-usage変換        │
  ├─ var-definitions ─────►│    │    ├─ var-definition変換   │
  ├─ locals ──────────────►│    │    ├─ local変換            │
  ├─ namespace-defs ──────►│    │    ├─ namespace変換        │
  ├─ keywords ────────────►│    │    └─ keyword変換          │
  ├─ java-class-* ────────►│    │                             │
  └─ protocol-impls ──────►│    │                             │
                           │    │                             │
db.documents               │    ├─ rewrite-clj-tokens*        │
  └─ text ────────────────►│    │    └─ #_ノード検出         │         {data: [...]}
                           │    │                             │────────►
                           │    ├─ ソート(行,列)              │         [deltaLine,
                           │    │                             │          deltaStart,
                           │    └─ absolute->relative変換    │          length,
                           │                                  │          tokenType,
                           └──────────────────────────────────┘          tokenMod]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| server.clj | `cli/src/clojure_lsp/server.clj` | ソース | LSPサーバーハンドラ、semantic-tokens-provider設定 |
| handlers.clj | `lib/src/clojure_lsp/handlers.clj` | ソース | 各機能のハンドラ実装 |
| semantic_tokens.clj | `lib/src/clojure_lsp/feature/semantic_tokens.clj` | ソース | Semantic Tokensの実装 |
| semantic_tokens_test.clj | `lib/test/clojure_lsp/feature/semantic_tokens_test.clj` | テスト | Semantic Tokensのテスト |
