# 画面設計書 30-ホバー（textDocument/hover）

## 概要

本ドキュメントは、serve-d LSPサーバーにおける `textDocument/hover` リクエストの処理仕様を定義する画面設計書である。シンボル上にカーソルを置いた際に、そのシンボルのDDocドキュメント情報と宣言テキストをホバーポップアップとして表示する機能を記述する。

### 本画面の処理概要

**業務上の目的・背景**：D言語開発において、関数・変数・型等のシンボルのドキュメントや宣言情報を、コード上から直接参照できるようにする機能である。外部ドキュメントを参照することなく、エディタ上でAPIの仕様や使用法を確認でき、開発効率が大幅に向上する。DCDからDDocドキュメントを取得し、Markdown形式に変換してエディタに表示する。さらに、シンボルの宣言テキスト（dfmtでフォーマット済み）もD言語のシンタックスハイライト付きで表示する。

**画面へのアクセス方法**：エディタ上でD言語ソースファイルのシンボル上にマウスカーソルを置くか、ホバーコマンドを実行する。エディタが `textDocument/hover` リクエストをLSPサーバーに送信する。

**主要な操作・処理内容**：
1. DCDの `getDocumentation` でカーソル位置のシンボルのDDocドキュメントを取得する
2. DDocをMarkdown形式に変換する（`ddocToMarked`）
3. `findDeclarationImpl` でシンボルの宣言テキストを取得する（`includeDefinition=false`）
4. 宣言テキストとDDocドキュメントを `MarkedString` 配列として結合する
5. `Hover` オブジェクトとして返却する

**画面遷移**：本機能は独立した情報提供機能であり、他の画面への遷移は発生しない。ホバーポップアップはカーソル移動で自動的に非表示となる。画面遷移図の「シンボル操作」サブグラフにおいて「ホバー」として位置付けられている。

**権限による表示制御**：権限による表示制御は存在しない。D言語ファイルであれば一律にホバー情報が提供される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | ホバー情報 (textDocument/hover) | 主機能 | カーソル位置のシンボルのDDoc文書・宣言情報をホバー表示用に返却する |
| 78 | DDoc処理 | 補助機能 | ホバー表示用にDDocコメントをパース・Markdown変換するユーティリティを使用する |

## 画面種別

情報提供（LSPプロトコルレスポンス）

## URL/ルーティング

- LSPメソッド: `textDocument/hover`

## 入出力項目

| 項目名 | 型 | 入力/出力 | 必須 | 説明 |
|--------|-----|----------|------|------|
| params.textDocument.uri | DocumentUri | 入力 | Yes | 対象ドキュメントのURI |
| params.position | Position | 入力 | Yes | カーソル位置 |
| Hover | Hover | 出力 | - | ホバー情報 |
| Hover.contents | MarkedString[] | 出力 | - | ホバー表示内容（宣言テキスト + DDocドキュメント） |

## 表示項目

| 項目名 | 表示形式 | 説明 |
|--------|---------|------|
| 宣言テキスト | コードブロック（D言語） | シンボルの宣言テキストがD言語のシンタックスハイライト付きで表示される |
| DDocドキュメント | Markdown | シンボルに付けられたDDocコメントがMarkdown形式で表示される |

## イベント仕様

### 1-ホバー情報取得

シンボル上にカーソルを置いた際にトリガーされる処理フロー。

1. `backend.getBestInstance!DCDComponent` でDCDインスタンスを取得する（行122-123）
2. インスタンスが存在しない場合は `Hover.init` を返却する（行125）
3. ドキュメントを取得し、言語IDが `"d"` でなければ `Hover.init` を返却する（行127-129）
4. `dcd.getDocumentation` でカーソル位置のシンボルのDDocドキュメントを取得する（行133-134）
5. `ddocToMarked` でDDocをMarkdown形式の `MarkedString[]` に変換する（行135）
6. `findDeclarationImpl` を `includeDefinition=false` で呼び出し、宣言テキストを取得する（行139-140）:
   a. 宣言テキストの前後の空白を除去する（行141）
   b. 宣言テキストが空でない場合、D言語のコードブロックとして `MarkedString(declaration, "d")` を作成する（行143）
   c. DDocドキュメントの先頭に宣言テキストを追加する（行143）
7. 例外が発生した場合は握り潰す（空のcatchブロック）（行145-147）
8. `Hover` オブジェクトの `contents` に結果を設定して返却する（行149-151）

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

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

本機能はデータベースに対する操作を行わない。DCDの解析結果を参照する読み取り専用の処理である。

## メッセージ仕様

本機能は明示的なメッセージ表示を行わない。

## 例外処理

| 条件 | 挙動 |
|------|------|
| DCDコンポーネントが利用不可 | `Hover.init` を返却 |
| D言語ファイル以外 | `Hover.init` を返却 |
| DDocが存在しない | DDoc部分は空で、宣言テキストのみ表示 |
| findDeclarationImplで例外発生 | 例外を握り潰し、DDocドキュメントのみで返却 |
| シンボルが見つからない | `Hover.init` を返却 |

## 備考

- `provideHover` 関数は `definition.d` ファイル内に `provideDefinition` と同居しており、`findDeclarationImpl` を共有している
- ホバーでは `includeDefinition=false` で呼び出すため、宣言部分のみが取得される（定義本体は含まない）
- 宣言テキストの取得処理が失敗した場合でもDDocドキュメントは表示される（例外をcatchして継続）
- DDocからMarkdownへの変換は `served.utils.ddoc` モジュールの `ddocToMarked` 関数で行われる
- `MarkedString(declaration, "d")` は D言語のシンタックスハイライト付きコードブロックとして表示される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | protocol.d | `source/served/lsp/protocol.d` | `Hover`, `MarkedString`, `TextDocumentPositionParams` の定義を確認 |
| 1-2 | ddoc.d | `source/served/utils/ddoc.d` | `ddocToMarked`, `parseDdoc`, DDoc処理の理解 |

**読解のコツ**: `MarkedString` は `language` と `value` を持つ構造体で、`language` が指定されている場合はコードブロックとして表示される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | definition.d | `source/served/commands/definition.d` | `provideHover` 関数（行119-152）がLSPリクエストハンドラ |

**主要処理フロー**:
1. **行119**: `@protocolMethod("textDocument/hover")` でバインド
2. **行122-123**: DCDインスタンス取得
3. **行127-129**: 言語ID確認
4. **行131-134**: `dcd.getDocumentation` でDDocドキュメント取得
5. **行135**: `ddocToMarked` でMarkdown変換
6. **行139-140**: `findDeclarationImpl` で宣言テキスト取得（includeDefinition=false）
7. **行141-143**: 宣言テキストをMarkedString配列の先頭に追加
8. **行149-151**: Hover構築・返却

#### Step 3: 共有関数findDeclarationImplを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | definition.d | `source/served/commands/definition.d` | `findDeclarationImpl` 関数（行24-89）: 定義ジャンプとホバーで共有される宣言検索処理 |

**主要処理フロー**:
- **行27**: DCDのfindDeclarationで定義位置を取得
- **行49-55**: DCDExtで宣言範囲を精密化（includeDefinition=falseの場合は宣言部分のみ）
- **行67-81**: DfmtComponentで宣言テキストをフォーマット

#### Step 4: DDoc処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ddoc.d | `source/served/utils/ddoc.d` | `ddocToMarked` 関数: DDocテキストをMarkdownのMarkedString配列に変換 |

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

```
textDocument/hover リクエスト
    |
    +-- provideHover (definition.d:119)
           |
           +-- backend.getBestInstance!DCDComponent (インスタンス取得)
           +-- documents[uri] (ドキュメント取得)
           |
           +-- DCDComponent.getDocumentation (DDoc取得)
           +-- ddocToMarked (Markdown変換)
           |
           +-- findDeclarationImpl (definition.d:24, includeDefinition=false)
           |      +-- DCDComponent.findDeclaration
           |      +-- DCDExtComponent.getDeclarationRange
           |      +-- DfmtComponent.formatSync (宣言テキストフォーマット)
           |
           +-- MarkedString(declaration, "d") (宣言テキストをコードブロック化)
           +-- Hover.contents = marked (結果構築)
```

### データフロー図

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

TextDocumentPositionParams ---> provideHover ---------------------> Hover
  textDocument.uri                |
  position                        +-- DCDComponent.getDocumentation
                                  |      --> DDocテキスト
                                  +-- ddocToMarked
                                  |      --> MarkedString[] (DDoc部分)
                                  +-- findDeclarationImpl
                                  |      --> declaration (宣言テキスト)
                                  +-- MarkedString(declaration, "d")
                                  |      --> コードブロック
                                  +-- Hover.contents = [宣言] + [DDoc]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| definition.d | `source/served/commands/definition.d` | ソース | ホバーと定義ジャンプの主処理（findDeclarationImpl共有） |
| ddoc.d | `source/served/utils/ddoc.d` | ソース | DDocパース・Markdown変換ユーティリティ |
| types.d | `source/served/types.d` | ソース | 共通型定義 |
| protocol.d | `source/served/lsp/protocol.d` | ソース | LSPプロトコル型定義 |
| extension.d | `source/served/extension.d` | ソース | activeInstance等 |
