# 機能設計書 62-選択範囲拡張

## 概要

本ドキュメントは、Clojure LSPにおける選択範囲拡張機能（textDocument/selectionRange）の設計を定義する。この機能は、構文に基づいて選択範囲を段階的に拡張する情報を提供し、効率的なコード選択操作を可能にする。

### 本機能の処理概要

**業務上の目的・背景**：Clojureのようなリスト言語では、括弧で囲まれた式（S式）が階層的にネストしている。開発者がコードを選択する際、単語→式→親の式→さらに親の式…と段階的に選択範囲を拡張したいニーズがある。この機能は、エディタのキーボードショートカット（通常 Ctrl+Shift+→ など）と連携して、構文に基づいた効率的な選択範囲拡張を実現する。

**機能の利用シーン**：開発者がコード内の特定位置にカーソルを置き、選択範囲拡張のキーボードショートカットを押すと、エディタがこの機能を呼び出す。LSPサーバーは、現在位置から親方向への階層的な選択範囲情報を返し、エディタは段階的に選択範囲を広げていく。

**主要な処理内容**：
1. 指定されたURI・位置からファイルテキストを取得
2. rewrite-cljパーサーでS式ツリーを構築し、指定位置のzipperロケータを取得
3. zipperを親方向に遡りながら、各レベルの範囲情報をネストした構造で返却

**関連システム・外部連携**：LSPクライアント（エディタ）との通信はJSON-RPC経由で行われる。rewrite-cljライブラリを使用してClojureコードの構文解析を行う。

**権限による制御**：特に権限制御は存在しない。全ユーザーが同等にこの機能を利用できる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 該当なし | 該当なし | 該当なし | 本機能はLSPプロトコルを通じてエディタから呼び出される |

## 機能種別

データ取得処理 / 構文解析処理 / LSPレスポンス生成

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | 対象ファイルのURI | 有効なファイルURIであること |
| positions | array | Yes | 選択範囲拡張の起点位置リスト | 各要素は {line, character} 形式 |

### 入力データソース

- LSPクライアントからの textDocument/selectionRange リクエスト
- ドキュメントテキスト（内部DBまたはファイルシステム）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| range | Range | 現在レベルの選択範囲 |
| parent | SelectionRange | 親レベルの選択範囲（再帰的構造） |

### 出力先

LSPクライアント（JSON-RPC レスポンス）

## 処理フロー

### 処理シーケンス

```
1. LSPクライアントから textDocument/selectionRange リクエストを受信
   └─ handlers.clj の selection-range ハンドラが呼び出される
2. ファイルテキストを取得
   └─ file-management.clj の force-get-document-text を使用
3. rewrite-cljでパースし、指定位置のzipperを取得
   └─ parser.clj の safe-zloc-of-string と to-pos を使用
4. zipperを親方向に遡りながら範囲情報を収集
   └─ 内側の範囲と外側の範囲を交互にネスト構造で構築
5. SelectionRange 形式でレスポンスを返却
```

### フローチャート

```mermaid
flowchart TD
    A[textDocument/selectionRange リクエスト受信] --> B[ファイルテキスト取得]
    B --> C[rewrite-cljでパース]
    C --> D[指定位置のzipperロケータ取得]
    D --> E{トップレベルか?}
    E -->|No| F[現在ノードの範囲を取得]
    F --> G[内側範囲と外側範囲を計算]
    G --> H[親ノードに移動]
    H --> E
    E -->|Yes| I[ネスト構造を構築]
    I --> J[SelectionRange リスト返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 交互ネスト | 各ノードについて内側範囲（括弧内）と外側範囲（括弧含む）を交互にネストする | 常時 |
| BR-02 | 親方向遡り | zipperを親方向（z/up*）に遡りながら範囲を収集 | 常時 |
| BR-03 | 最初の位置のみ | positions配列の最初の要素のみを処理 | 現在の実装 |

### 計算ロジック

**内側範囲の計算（range->inner-range）**:
- col = col + 1（開き括弧の次）
- end-col = end-col - 1（閉じ括弧の前）

**ネスト構造の構築**:
- 各レベルで内側範囲と外側範囲をinterleaveで交互に配置
- reduceで再帰的なparent構造を構築

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| テキスト取得 | db[:documents][uri][:text] | SELECT | ドキュメントテキストを取得 |

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

#### db[:documents][uri]

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | :text | uri = 指定URI | ドキュメントが開かれている場合 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | nil | ファイルが存在しない、またはパース失敗 | nilを返却 |

### リトライ仕様

リトライは不要。

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

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

## パフォーマンス要件

- 通常のClojureファイルに対して数十ミリ秒以内で応答

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

- 特になし（ローカルファイルの読み取り専用）

## 備考

- LSPプロトコル仕様: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_selectionRange

---

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

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

### 推奨読解順序

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

rewrite-cljのzipperとメタデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | parser.clj | `lib/src/clojure_lsp/parser.clj` | to-pos関数（129行目）でzipperを位置に移動 |

**読解のコツ**: rewrite-cljのzipperは各ノードにメタデータとして :row, :col, :end-row, :end-col を持つ。z/node でノードを取得し、meta でメタデータを参照できる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | server.clj | `cli/src/clojure_lsp/server.clj` | textDocument/selectionRange リクエストハンドラ（436-441行目） |
| 2-2 | handlers.clj | `lib/src/clojure_lsp/handlers.clj` | selection-range ハンドラ関数（516-522行目） |

**主要処理フロー**:
1. **436-441行目（server.clj）**: LSPリクエストを受信し、ハンドラに委譲
2. **516-522行目（handlers.clj）**: positions を row-col 形式に変換し、selection-ranges を呼び出し

#### Step 3: コア機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | selection_range.clj | `lib/src/clojure_lsp/feature/selection_range.clj` | 選択範囲拡張のコアロジック（全35行） |

**主要処理フロー**:
- **9-12行目**: range->inner-range - 括弧内の範囲を計算
- **14-27行目**: zloc->selection-range - zlocから階層的なSelectionRange構造を構築
- **29-34行目**: selection-ranges - エントリーポイント関数

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

```
server.clj (textDocument/selectionRange リクエスト受信)
    │
    └─ handlers.clj::selection-range
           │
           └─ feature/selection_range.clj::selection-ranges
                  │
                  ├─ file-management.clj::force-get-document-text
                  │
                  ├─ parser.clj::safe-zloc-of-string
                  │
                  ├─ parser.clj::to-pos
                  │
                  └─ zloc->selection-range (private)
                         │
                         └─ range->inner-range (private)
```

### データフロー図

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

URI + Positions  ───▶  selection-ranges
                              │
                              ▼
                       force-get-document-text
                              │
                              ▼
                       safe-zloc-of-string  (rewrite-clj パース)
                              │
                              ▼
                       to-pos (指定位置のzloc取得)
                              │
                              ▼
                       zloc->selection-range
                              │
                              ▼ (loop: 親方向に遡り)
                       [{:range {:row 2 :col 16 :end-row 2 :end-col 22}} ...]
                              │
                              ▼ (reduce: ネスト構造構築)
                       SelectionRange (nested)  ───▶  LSPクライアント
                       {:range {...}
                        :parent {:range {...}
                                 :parent {...}}}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| selection_range.clj | `lib/src/clojure_lsp/feature/selection_range.clj` | ソース | 選択範囲拡張のコアロジック |
| handlers.clj | `lib/src/clojure_lsp/handlers.clj` | ソース | LSPリクエストハンドラ |
| server.clj | `cli/src/clojure_lsp/server.clj` | ソース | LSPサーバーエントリーポイント |
| parser.clj | `lib/src/clojure_lsp/parser.clj` | ソース | rewrite-cljパーサーラッパー |
| file_management.clj | `lib/src/clojure_lsp/feature/file_management.clj` | ソース | ドキュメントテキスト取得 |
| selection_range_test.clj | `lib/test/clojure_lsp/feature/selection_range_test.clj` | テスト | ユニットテスト |
