# 機能設計書 31-要素の前方移動

## 概要

本ドキュメントは、Clojure LSPの「要素の前方移動（drag-backward）」機能の設計仕様を記載する。この機能は、コレクション内の要素やキー/値ペアを前方（上方向）に移動させるリファクタリング操作を提供する。

### 本機能の処理概要

**業務上の目的・背景**：Clojureコードを編集する際、マップのキー/値ペア、ベクタの要素、関数呼び出しの引数など、コレクション内の項目の順序を変更したい場面が頻繁に発生する。従来はカット＆ペーストで行っていたこの操作を、単一のコマンドで安全かつ正確に実行できるようにすることで、開発者の生産性を向上させる。

**機能の利用シーン**：この機能は以下の場面で利用される：
- マップリテラル内のキー/値ペアの並べ替え（重要度順、アルファベット順への調整など）
- letバインディング内の変数定義の順序変更
- 関数呼び出しの引数順序の調整
- case/cond式の条件節の優先度調整

**主要な処理内容**：
1. カーソル位置から移動対象の「句（clause）」を特定
2. 句の「幅（breadth）」に基づき、移動単位を決定（マップなら2要素、ベクタなら1要素など）
3. 対象句と前方の句を入れ替えるテキスト編集を生成
4. カーソル位置を移動後の句内の同じ相対位置に更新
5. エディタにテキスト編集とカーソル移動を適用

**関連システム・外部連携**：LSPプロトコルを通じてエディタ（VS Code、Emacs、Vim等）と連携し、`workspace/executeCommand`リクエストで呼び出される。

**権限による制御**：特になし。すべてのユーザーが利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | エディタ画面 | 主画面 | コードアクションメニューから「Drag backward」を選択、またはキーバインドで実行 |

## 機能種別

リファクタリング（コード変換処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | 対象ファイルのURI | 有効なファイルURIであること |
| line | number | Yes | カーソル行（0-indexed） | 0以上の整数 |
| character | number | Yes | カーソル列（0-indexed） | 0以上の整数 |

### 入力データソース

- エディタからのLSP `workspace/executeCommand` リクエスト
- コマンド名: `drag-backward`

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| changes-by-uri | map | URI別のテキスト編集リスト |
| show-document-after-edit | map | 編集後に表示・フォーカスするドキュメントとカーソル位置 |

### 出力先

- LSPレスポンスとしてエディタに返却
- `workspace/applyEdit` でエディタにテキスト編集を適用

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ コマンドと引数（URI、行、列）を取得

2. zlocの取得
   └─ ファイルをパースしてカーソル位置のzloc（zipper location）を取得

3. 移動可否判定
   └─ clause-specを取得し、前方移動が可能か判定
   └─ 不可の場合はnilを返却

4. 句の特定
   └─ identify-clausesで句とパディングを特定
   └─ origin-clause（移動対象）を特定

5. ノード交換の計算
   └─ nodes-to-dragで交換対象ノードを抽出
   └─ node-editsで編集操作を生成

6. カーソル位置の再計算
   └─ reposition-cursorで移動後のカーソル位置を算出

7. 結果の返却
   └─ changes-by-uriとshow-document-after-editを含むマップを返却
```

### フローチャート

```mermaid
flowchart TD
    A[コマンド受信] --> B[zlocを取得]
    B --> C{zloc存在?}
    C -->|No| D[nil返却]
    C -->|Yes| E[clause-spec取得]
    E --> F{前方移動可能?}
    F -->|No| D
    F -->|Yes| G[句の特定]
    G --> H[交換ノードの抽出]
    H --> I[テキスト編集生成]
    I --> J[カーソル位置再計算]
    J --> K[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-31-01 | 句幅の決定 | マップは2要素、ベクタは1要素、letバインディングは2要素を1句として扱う | コレクション種別に応じて自動判定 |
| BR-31-02 | 移動境界 | 句がコレクションの先頭にある場合は前方移動不可 | 先頭要素選択時 |
| BR-31-03 | rind除外 | case/condp等の先頭固定要素は移動対象外 | 特定関数呼び出し内 |
| BR-31-04 | コメント連携 | 句の直前のコメントは句と共に移動 | コメントが句に隣接している場合 |

### 計算ロジック

**句幅（breadth）の決定**：
- `:map` → breadth = 2（キー/値ペア）
- `:vector` → breadth = 1（単一要素）、ただしletバインディング等ではbreadth = 2
- `:list/:fn` → 呼び出し先関数に応じて決定（cond → 2、condp → 2 or 3、通常 → 1）

**カーソル位置再計算**：
```clojure
;; 前方移動時：前方句の位置 + カーソルオフセット
new-cursor = nodes-start(earlier-clause) + cursor-offset
```

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

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

本機能はデータベース操作を行わない。clj-kondo解析データベース（インメモリ）を参照のみ行う。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 操作不可 | 先頭要素で前方移動を試行 | nil返却（無視） |
| - | 操作不可 | コレクション外で実行 | nil返却（無視） |
| - | 解析エラー | 構文エラーのあるファイル | nil返却（無視） |

### リトライ仕様

リトライは行わない。エラー時はnilを返却し、操作が適用されない。

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

トランザクション管理は不要。テキスト編集はエディタ側で単一のアンドゥ操作として扱われる。

## パフォーマンス要件

- 応答時間: 100ms以内
- 大規模ファイル（10,000行超）でも200ms以内

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

- ファイルシステムへの書き込みは行わない（エディタ経由で適用）
- 入力URIはサニタイズ済みのものを使用

## 備考

- 本機能は以前「move-coll-entry-up」という名前で提供されていたが、より汎用的な「drag-backward」に改名された
- コマンドエイリアス `move-coll-entry-up` は後方互換性のため維持されている

---

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

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

### 推奨読解順序

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

まず、句（clause）の概念と clause-spec のデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | clauses.clj | `lib/src/clojure_lsp/feature/clauses.clj` | clause-spec の構造（:rind, :pulp, :breadth）を理解する |

**読解のコツ**: clause-specは以下の構造を持つ：
- `:rind [左除外数 右除外数]` - 移動対象外の先頭/末尾要素数
- `:pulp` - 移動可能な要素の総数
- `:breadth` - 1句を構成する要素数

**主要処理フロー**:
- **209-224行目**: clause-spec の構造説明コメント
- **351-368行目**: `clause-spec` 関数 - zlocから clause-spec を生成

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | command.clj | `lib/src/clojure_lsp/feature/command.clj` | コマンドディスパッチの仕組み |
| 2-2 | code_actions.clj | `lib/src/clojure_lsp/feature/code_actions.clj` | コードアクションとしての提供 |

**主要処理フロー**:
- **68-69行目** (command.clj): `:drag-backward` コマンドハンドラ
- **289-294行目** (code_actions.clj): drag-backward アクションの生成

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | drag.clj | `lib/src/clojure_lsp/feature/drag.clj` | 要素移動のコアロジック |

**主要処理フロー**:
- **28-39行目**: `node-edits` - 2つの句を交換するテキスト編集を生成
- **57-71行目**: `reposition-cursor` - 移動後のカーソル位置を計算
- **73-85行目**: `identify-clauses` - 句とパディングを特定
- **87-105行目**: `nodes-to-drag` - 交換対象のノードを抽出
- **107-119行目**: `drag-clause` - 句のドラッグ処理メイン
- **159-162行目**: `plan` - 移動可否の事前判定
- **169行目**: `can-drag-backward?` - 後方移動可否判定
- **177行目**: `drag-backward` - 公開API

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

```
workspace/executeCommand (LSP)
    │
    └─ command/call-command
           │
           └─ run-command :drag-backward
                  │
                  └─ f.drag/drag-backward
                         │
                         ├─ plan
                         │     └─ f.clauses/clause-spec
                         │
                         ├─ identify-clauses
                         │     └─ f.clauses/identify
                         │
                         ├─ nodes-to-drag
                         │
                         ├─ drag-clause
                         │     ├─ node-edits
                         │     └─ reposition-cursor
                         │
                         └─ 結果返却
```

### データフロー図

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

URI, row, col      ──▶  parser/to-pos              ──▶  zloc
                         (ファイルパース)

zloc               ──▶  clause-spec                ──▶  {:breadth, :rind, :pulp}
                         (句仕様の決定)

clause-spec        ──▶  identify-clauses           ──▶  {:clauses+padding, :origin-clause}
                         (句の特定)

clause-data        ──▶  nodes-to-drag              ──▶  {:earlier-clause-nodes, :later-clause-nodes, ...}
                         (交換ノード抽出)

nodes              ──▶  node-edits                 ──▶  [{:range, :loc}]
                         (編集操作生成)

cursor, nodes      ──▶  reposition-cursor          ──▶  {:row, :col, :end-row, :end-col}
                         (カーソル再計算)

                   ──▶  drag-clause                ──▶  {:changes-by-uri, :show-document-after-edit}
                         (結果組み立て)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| drag.clj | `lib/src/clojure_lsp/feature/drag.clj` | ソース | 要素移動のコアロジック |
| clauses.clj | `lib/src/clojure_lsp/feature/clauses.clj` | ソース | 句の特定と分類 |
| command.clj | `lib/src/clojure_lsp/feature/command.clj` | ソース | コマンドディスパッチ |
| code_actions.clj | `lib/src/clojure_lsp/feature/code_actions.clj` | ソース | コードアクション生成 |
| edit.clj | `lib/src/clojure_lsp/refactor/edit.clj` | ソース | zlocユーティリティ |
| drag_test.clj | `lib/test/clojure_lsp/feature/drag_test.clj` | テスト | 要素移動のテスト |
