# 機能設計書 64-空行検出

## 概要

本ドキュメントは、Prettierのコードフォーマッタにおける「空行検出」機能の設計を記述する。この機能は、コード内の空行を検出するためのユーティリティ関数群（isNextLineEmpty、isPreviousLineEmpty）を提供し、フォーマット時の空行保持・挿入判断に使用される。

### 本機能の処理概要

**業務上の目的・背景**：コードフォーマッタにおいて、開発者が意図的に挿入した空行を保持することは可読性維持の観点から重要である。isNextLineEmptyとisPreviousLineEmptyは、ASTノードの前後に空行が存在するかを判定し、フォーマット後も元のコードの空行構造を維持できるようにする。これにより、論理的なコードブロック間の区切りを保持し、読みやすいコードを出力する。

**機能の利用シーン**：
- ステートメント間の空行保持判定
- import文グループ間の空行挿入判断
- 関数・クラス定義間の空行制御
- オブジェクトプロパティ間の空行保持
- JSX要素間の空行制御

**主要な処理内容**：
1. **isNextLineEmpty**: 指定位置の後に空行があるかを検出
   - インラインコメントとトレイリングコメントをスキップ
   - 改行後にさらに改行があれば空行と判定
2. **isPreviousLineEmpty**: 指定位置の前に空行があるかを検出
   - スペースと改行を逆方向にスキップ
   - 連続する改行があれば空行と判定

**関連システム・外部連携**：
- 各言語のプリンター: 空行保持ロジックで使用
- skipユーティリティ関数群: 空白・コメントスキップに依存

**権限による制御**：特になし（内部ユーティリティ関数）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | Misc - Technical Details | 補助機能 | 空行検出の技術的詳細を説明 |

## 機能種別

検出処理 / ユーティリティ

## 入力仕様

### 入力パラメータ

#### isNextLineEmpty

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| text | string | Yes | 検査対象のソースコード | - |
| startIndex | number | Yes | 検査開始位置 | 0以上の整数 |

#### isPreviousLineEmpty

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| text | string | Yes | 検査対象のソースコード | - |
| startIndex | number | Yes | 検査開始位置 | 0以上の整数 |

### 入力データソース

- フォーマット処理中のソースコード
- ASTノードの位置情報（locStart、locEnd）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| isEmpty | boolean | 空行が存在するかどうか |

### 出力先

- 呼び出し元への戻り値

## 処理フロー

### 処理シーケンス

#### isNextLineEmpty

```
1. 現在位置から行末までスキップ（,; \t）
2. インラインコメント（/* ... */）をスキップ
3. スペースをスキップ
4. 上記1-3を変化がなくなるまで繰り返す
5. トレイリングコメント（// ...）をスキップ
6. 改行をスキップ
7. hasNewlineで次の行に改行があるかチェック
8. 結果を返す
```

#### isPreviousLineEmpty

```
1. 現在位置-1から逆方向にスペースをスキップ
2. 逆方向に改行をスキップ
3. 逆方向にスペースをスキップ
4. さらに改行があるかチェック
5. 結果を返す（改行が2回あれば空行あり）
```

### フローチャート

```mermaid
flowchart TD
    subgraph isNextLineEmpty
        A1[開始] --> B1[skipToLineEnd]
        B1 --> C1[skipInlineComment]
        C1 --> D1[skipSpaces]
        D1 --> E1{位置変化あり?}
        E1 -->|Yes| B1
        E1 -->|No| F1[skipTrailingComment]
        F1 --> G1[skipNewline]
        G1 --> H1[hasNewline]
        H1 --> I1[return result]
    end

    subgraph isPreviousLineEmpty
        A2[開始] --> B2[skipSpaces backwards]
        B2 --> C2[skipNewline backwards]
        C2 --> D2[skipSpaces backwards]
        D2 --> E2[skipNewline backwards]
        E2 --> F2{idx !== idx2?}
        F2 -->|Yes| G2[return true]
        F2 -->|No| H2[return false]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-64-01 | コメントスキップ | isNextLineEmptyはインラインコメントとトレイリングコメントをスキップ | コメントが空行判定に影響しない |
| BR-64-02 | 後方検索の単純化 | isPreviousLineEmptyはコメントを考慮しない | 先行コメントは通常ノードに属するため |
| BR-64-03 | 改行判定 | 連続する改行が存在すれば空行あり | \n\n, \r\n\r\n等 |
| BR-64-04 | レガシーAPI互換 | 3引数の呼び出し形式もサポート | public.jsでラップ |

### 計算ロジック

空行の定義:
- 行末からコメントを除去した後、改行が2回連続すること
- または、改行→空白→改行のパターン

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

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

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

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 本機能では例外を発生させない | - |

### リトライ仕様

該当なし

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

該当なし（データベース操作なし）

## パフォーマンス要件

- 線形時間（O(n)）で処理を完了する（nは検査範囲の文字数）
- 無限ループを避けるため、位置変化の監視が必要

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

- 特別なセキュリティ要件なし
- 入力文字列に対する副作用なし

## 備考

- isNextLineEmptyAfterIndexはpublic.jsから非推奨としてエクスポートされている
- v4で直接的なエクスポートに変更予定

---

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

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

### 推奨読解順序

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

関数シグネチャと依存関係を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | is-next-line-empty.js | `src/utilities/is-next-line-empty.js` | isNextLineEmpty関数のシグネチャ |
| 1-2 | is-previous-line-empty.js | `src/utilities/is-previous-line-empty.js` | isPreviousLineEmpty関数のシグネチャ |

**読解のコツ**: 両関数ともskipユーティリティに依存している。インポート文を確認すること。

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

公開APIとレガシーAPI互換を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | public.js | `src/utilities/public.js` | エクスポート定義とレガシー互換処理 |

**主要処理フロー**:
- **53-59行目**: isPreviousLineEmptyのレガシー互換処理
- **128-134行目**: isNextLineEmptyのレガシー互換処理
- **61-72行目**: legacyIsNextLineEmpty関数

#### Step 3: isNextLineEmptyの実装を理解する

空行検出のロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | is-next-line-empty.js | `src/utilities/is-next-line-empty.js` | メインロジック |

**主要処理フロー**:
- **1-5行目**: 依存関数のインポート
- **12-16行目**: 初期化とメインループ
- **17-23行目**: 行末・コメント・スペースのスキップループ
- **24-26行目**: トレイリングコメントと改行のスキップ、hasNewlineで判定

#### Step 4: isPreviousLineEmptyの実装を理解する

後方検索のロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | is-previous-line-empty.js | `src/utilities/is-previous-line-empty.js` | メインロジック |

**主要処理フロー**:
- **1-2行目**: 依存関数のインポート
- **10-12行目**: startIndex-1から逆方向にスキップ開始
- **13-16行目**: スペース→改行→スペース→改行の順でスキップ
- **17行目**: 位置が変化していれば空行あり

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

```
isNextLineEmpty (is-next-line-empty.js)
    │
    ├─ skipToLineEnd (skip.js)
    │      └─ ",; \t"をスキップ
    │
    ├─ skipInlineComment (skip-inline-comment.js)
    │      └─ /* ... */をスキップ
    │
    ├─ skipSpaces (skip.js)
    │      └─ " \t"をスキップ
    │
    ├─ skipTrailingComment (skip-trailing-comment.js)
    │      └─ // ...をスキップ
    │
    ├─ skipNewline (skip-newline.js)
    │      └─ 改行文字をスキップ
    │
    └─ hasNewline (has-newline.js)
           └─ 改行の存在をチェック

isPreviousLineEmpty (is-previous-line-empty.js)
    │
    ├─ skipSpaces (skip.js) [backwards]
    │
    └─ skipNewline (skip-newline.js) [backwards]
```

### データフロー図

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

isNextLineEmpty:
text ─────────────────▶ skipToLineEnd ─────────────────▶
startIndex                    │
                              ▼
                         skipInlineComment
                              │
                              ▼
                         skipSpaces
                              │
                              ▼
                         skipTrailingComment
                              │
                              ▼
                         skipNewline
                              │
                              ▼
                         hasNewline ─────────────────────▶ boolean

isPreviousLineEmpty:
text ─────────────────▶ skipSpaces (backwards) ─────────▶
startIndex                    │
                              ▼
                         skipNewline (backwards)
                              │
                              ▼
                         skipSpaces (backwards)
                              │
                              ▼
                         skipNewline (backwards)
                              │
                              ▼
                         idx !== idx2 ? ──────────────────▶ boolean
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| is-next-line-empty.js | `src/utilities/is-next-line-empty.js` | ソース | 次行空行検出 |
| is-previous-line-empty.js | `src/utilities/is-previous-line-empty.js` | ソース | 前行空行検出 |
| public.js | `src/utilities/public.js` | ソース | 公開エクスポート、レガシー互換 |
| skip.js | `src/utilities/skip.js` | ソース | スキップユーティリティ |
| skip-inline-comment.js | `src/utilities/skip-inline-comment.js` | ソース | インラインコメントスキップ |
| skip-trailing-comment.js | `src/utilities/skip-trailing-comment.js` | ソース | トレイリングコメントスキップ |
| skip-newline.js | `src/utilities/skip-newline.js` | ソース | 改行スキップ |
| has-newline.js | `src/utilities/has-newline.js` | ソース | 改行存在チェック |
| index.js | `src/index.js` | ソース | API公開（util経由） |
