# 機能設計書 2-カーソル位置追跡フォーマット（formatWithCursor）

## 概要

本ドキュメントは、Prettierのカーソル位置追跡機能付きフォーマット関数`formatWithCursor`の設計について記述する。

### 本機能の処理概要

フォーマット実行時にカーソル位置を追跡し、フォーマット後の適切なカーソル位置を返す機能である。エディタ統合において、ユーザーのカーソル位置を保持しながらコードをフォーマットするために使用される。

**業務上の目的・背景**：エディタでコードをフォーマットする際、カーソル位置がずれると作業効率が著しく低下する。本機能により、フォーマット後もユーザーは元の編集位置から継続して作業できる。これはエディタ統合の品質を大きく左右する重要な機能である。

**機能の利用シーン**：
- エディタでの保存時自動フォーマット（カーソル位置維持）
- エディタでのフォーマットコマンド実行時
- リアルタイムフォーマット機能（Playgroundなど）
- エディタプラグイン開発時のAPI利用

**主要な処理内容**：
1. ソースコードのパースとAST生成
2. カーソル位置に最も近いASTノードの特定
3. フォーマット処理の実行（Doc生成・文字列出力）
4. フォーマット後のカーソル位置計算（diff アルゴリズム使用）
5. フォーマット結果とカーソル位置の返却

**関連システム・外部連携**：
- 各言語パーサー（Babel、TypeScript、PostCSS等）
- diffライブラリ（カーソル位置計算用）
- エディタ統合API（VSCode、Vim、Emacs等）

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Playground | 主機能 | カーソル位置を追跡しながらフォーマットを実行 |
| 16 | Usage - API | 補助機能 | APIでのformatWithCursor関数の使い方を説明 |
| 22 | Editors - Editor Integration | 主機能 | エディタ統合時のカーソル追跡フォーマットを説明 |

## 機能種別

計算処理 / テキスト変換 / 位置計算

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| text | string | Yes | フォーマット対象のソースコード | 空文字列・空白のみは早期リターン |
| options | object | No | フォーマットオプション | - |
| options.cursorOffset | number | No | カーソル位置（0始まりのオフセット） | 0以上text.length以下、範囲外は-1に正規化 |
| options.parser | string | No | 使用するパーサー名 | 未指定時はfilepathから推論 |
| options.filepath | string | No | ファイルパス（パーサー推論用） | - |
| options.plugins | array | No | 追加プラグインの配列 | - |
| options.rangeStart | number | No | 範囲フォーマット開始位置 | - |
| options.rangeEnd | number | No | 範囲フォーマット終了位置 | - |

### 入力データソース

- エディタからのAPI呼び出し（テキストとカーソル位置）
- プログラマティックなAPI利用

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| formatted | string | フォーマット済みのソースコード |
| cursorOffset | number | フォーマット後のカーソル位置（追跡不可の場合は-1） |
| comments | array | 処理されたコメントの配列 |

### 出力先

- API戻り値としてオブジェクトを返却

## 処理フロー

### 処理シーケンス

```
1. formatWithCursor関数呼び出し
   └─ プラグインのロード（withPluginsラッパー経由）

2. オプション正規化
   └─ normalizeFormatOptionsでパーサー解決、デフォルト値適用

3. 入力・オプション正規化
   └─ BOM処理、改行コード正規化、cursorOffset調整

4. 早期リターン判定
   ├─ 範囲エラー（rangeStart >= rangeEnd）
   ├─ プラグマ必須でプラグマなし
   └─ 無視プラグマ存在

5. フォーマット処理分岐
   ├─ 範囲指定あり → formatRange関数
   └─ 全体フォーマット → coreFormat関数

6. カーソルノード特定（cursorOffset >= 0の場合）
   └─ getCursorLocation関数でAST内のカーソル位置を特定

7. Doc生成と文字列出力
   └─ cursorシンボルを挿入しながら出力

8. カーソル位置計算
   └─ diffアルゴリズムで新旧テキストを比較しカーソル位置を算出

9. 結果返却
   └─ BOM復元、cursorOffset調整
```

### フローチャート

```mermaid
flowchart TD
    A[formatWithCursor呼び出し] --> B[プラグインロード]
    B --> C[オプション正規化]
    C --> D[入力正規化・cursorOffset調整]
    D --> E{早期リターン条件?}
    E -->|Yes| F[元テキストをそのまま返却]
    E -->|No| G{範囲指定あり?}
    G -->|Yes| H[formatRange実行]
    G -->|No| I{cursorOffset >= 0?}
    I -->|Yes| J[getCursorLocationでノード特定]
    I -->|No| K[coreFormat実行]
    J --> K
    H --> L[カーソル位置調整]
    K --> M{cursorOffset >= 0?}
    M -->|Yes| N[diffでカーソル位置計算]
    M -->|No| O[cursorOffset = -1]
    N --> P[BOM復元・結果返却]
    L --> P
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | カーソル位置正規化 | cursorOffsetが範囲外の場合は-1に設定 | cursorOffset < 0 または > text.length |
| BR-02 | カーソルノード特定 | カーソル位置を含む最小のASTノードを特定 | cursorOffset >= 0 |
| BR-03 | カーソル位置計算 | diffアルゴリズムで元テキストと新テキストを比較しカーソル位置を算出 | cursorOffset >= 0 かつフォーマット実行 |
| BR-04 | BOMオフセット調整 | BOM存在時はcursorOffsetを1減算し、出力時に1加算 | 入力先頭がBOM文字 |
| BR-05 | 範囲外カーソル維持 | 範囲フォーマット時、範囲外のカーソルは差分だけ調整 | rangeStart/rangeEnd指定時 |

### 計算ロジック

**カーソル位置計算アルゴリズム**：
1. 元のカーソル領域テキストにカーソルシンボル（CURSOR）を挿入
2. フォーマット後のカーソル領域テキストと比較
3. diffArraysで差分を計算
4. 削除されていないカーソルシンボルの位置を新しいカーソル位置とする

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

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UndefinedParserError | 入力エラー | パーサーが指定されず推論もできない | parserオプションまたはfilepathを指定 |
| ConfigError | 設定エラー | 指定されたパーサーが見つからない | 正しいパーサー名を指定またはプラグインを追加 |
| SyntaxError | パースエラー | ソースコードの構文エラー | 入力コードの構文を修正 |

### リトライ仕様

リトライは行わない。エラー発生時は即座に例外をスロー。

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

トランザクション管理は不要（ステートレスな処理）。

## パフォーマンス要件

- カーソル位置計算のオーバーヘッドは数ミリ秒以内
- format関数と同等のパフォーマンス

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

- 入力コードの実行は行わない（静的解析のみ）
- カーソル位置情報は数値のみで機密性なし

## 備考

- format関数は内部的にformatWithCursor関数をcursorOffset: -1で呼び出している
- エディタ統合ではこの関数を直接使用することを推奨

---

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

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

### 推奨読解順序

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

formatWithCursor関数のラッパーと実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `src/index.js` | formatWithCursorのエクスポート |

**主要処理フロー**:
1. **66行目**: formatWithCursorはwithPluginsでラップ
2. **134行目**: formatWithCursorをエクスポート

#### Step 2: コア処理を理解する

formatWithCursor関数の本体実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | core.js | `src/main/core.js` | formatWithCursor本体 |

**主要処理フロー**:
- **324-367行目**: formatWithCursor関数
  - **325-328行目**: オプション正規化、BOM・改行処理
  - **330-340行目**: 早期リターン条件判定
  - **344-346行目**: formatRangeまたはcoreFormatの呼び出し分岐
  - **358-364行目**: BOM復元とcursorOffset調整

#### Step 3: カーソル位置特定を理解する

ASTからカーソル位置のノードを特定する処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | get-cursor-node.js | `src/main/get-cursor-node.js` | カーソルノード特定 |

**読解のコツ**: カーソル位置は3つのケースに分類される：
- cursorNode: カーソルがノード内にある場合
- nodeBeforeCursor: カーソルがノードの後にある場合
- nodeAfterCursor: カーソルがノードの前にある場合

#### Step 4: カーソル位置計算を理解する

diff アルゴリズムによるカーソル位置計算を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | core.js | `src/main/core.js` | coreFormat関数内のカーソル位置計算 |

**主要処理フロー**:
- **75-179行目**: カーソル位置計算ロジック
  - **92-135行目**: カーソル領域の特定
  - **152-158行目**: カーソルシンボル挿入
  - **162-165行目**: diffArraysで差分計算
  - **167-176行目**: 新しいカーソル位置の算出

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

```
formatWithCursor (src/index.js:66)
    │
    ├─ withPlugins (src/index.js:43-64)
    │      ├─ loadBuiltinPlugins
    │      └─ loadPlugins
    │
    └─ core.formatWithCursor (src/main/core.js:324-367)
           │
           ├─ normalizeFormatOptions
           │
           ├─ normalizeInputAndOptions
           │
           ├─ hasPragma / hasIgnorePragma
           │
           ├─ formatRange (範囲指定時)
           │      └─ coreFormat
           │
           └─ coreFormat (全体フォーマット時)
                  │
                  ├─ parseText
                  │
                  ├─ getCursorLocation (cursorOffset >= 0)
                  │
                  ├─ printAstToDoc
                  │      └─ cursor シンボル挿入
                  │
                  ├─ printDocToString
                  │      └─ cursorNodeStart/cursorNodeText 記録
                  │
                  └─ diffArrays (カーソル位置計算)
```

### データフロー図

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

ソースコード ───────────▶ パース ────────────────────────▶ AST
    │                                                         │
    │                                                         ▼
cursorOffset ───────────▶ getCursorLocation ───────────▶ cursorNode
                                                         nodeBeforeCursor
                                                         nodeAfterCursor
                                                              │
                                                              ▼
                                           printAstToDoc ───▶ Doc (cursorシンボル付き)
                                                              │
                                                              ▼
                                         printDocToString ───▶ formatted
                                                              cursorNodeStart
                                                              cursorNodeText
                                                              │
                                                              ▼
                                              diffArrays ───▶ 新cursorOffset
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `src/index.js` | ソース | 公開APIのエクスポート |
| core.js | `src/main/core.js` | ソース | コアフォーマット処理・カーソル位置計算 |
| get-cursor-node.js | `src/main/get-cursor-node.js` | ソース | カーソルノード特定 |
| ast-to-doc.js | `src/main/ast-to-doc.js` | ソース | Doc生成（cursorシンボル挿入） |
| index.js | `src/document/index.js` | ソース | cursor シンボル定義・出力処理 |
| diff | 外部ライブラリ | ライブラリ | diffArrays関数提供 |
