# 機能設計書 66-スキップ関数

## 概要

本ドキュメントは、Prettierのコードフォーマッタにおける「スキップ関数」機能の設計を記述する。この機能は、テキスト内の特定文字をスキップするためのユーティリティ関数群を提供し、パース処理やコメント検出などで広く使用される。

### 本機能の処理概要

**業務上の目的・背景**：コードフォーマッタの内部処理では、空白、改行、コメントなど特定の文字パターンをスキップする必要が頻繁に発生する。skip関数群は、これらのスキップ処理を統一的なAPIで提供し、コードの可読性と再利用性を高める。また、順方向・逆方向の両方のスキップをサポートし、さまざまなユースケースに対応する。

**機能の利用シーン**：
- パース処理での空白スキップ
- コメント検出時の行末までのスキップ
- 空行検出での改行スキップ
- インデント計算でのスペース/タブスキップ
- トークン間の空白処理

**主要な処理内容**：
1. **skip**: 汎用スキップ関数ジェネレータ（文字列または正規表現でマッチ）
2. **skipSpaces**: スペースとタブをスキップ
3. **skipWhitespace**: すべての空白文字をスキップ
4. **skipToLineEnd**: 行末文字（,; \t）までスキップ
5. **skipEverythingButNewLine**: 改行以外のすべてをスキップ
6. **skipInlineComment**: インラインコメント（/* ... */）をスキップ
7. **skipTrailingComment**: トレイリングコメント（// ...）をスキップ
8. **skipNewline**: 改行文字をスキップ

**関連システム・外部連携**：
- 空行検出: isNextLineEmpty、isPreviousLineEmptyで使用
- コメント処理: コメント位置の特定に使用
- パーサー: トークン解析で使用

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | Misc - Technical Details | 補助機能 | スキップ関数の技術的詳細を説明 |

## 機能種別

テキスト処理 / ユーティリティ

## 入力仕様

### 入力パラメータ

#### skip（ジェネレータ関数）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| characters | string \| RegExp | Yes | スキップ対象の文字（列）または正規表現 | - |

#### 生成されたskip関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| text | string | Yes | 検査対象テキスト | - |
| startIndex | number \| false | Yes | 開始位置 | falseの場合はfalseを返す |
| options | object | No | オプション | backwards: boolean |

### 入力データソース

- フォーマット処理中のソースコード
- パース処理中のテキスト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| position | number \| false | スキップ後の位置、またはfalse（エラー時） |

### 出力先

- 呼び出し元への戻り値

## 処理フロー

### 処理シーケンス

#### skip（汎用スキップ）

```
1. startIndexがfalseの場合、falseを返す
2. カーソル位置を初期化
3. テキスト範囲内でループ
   └─ 現在位置の文字を取得
   └─ 文字がマッチしない場合、現在位置を返す
   └─ backwardsならカーソル--、そうでなければカーソル++
4. テキスト境界に達した場合
   └─ -1または文字列長を返す（有効なスキップがあった場合）
   └─ falseを返す（スキップがなかった場合）
```

#### skipNewline

```
1. startIndexがfalseの場合、falseを返す
2. 現在位置の文字を取得
3. backwards（逆方向）の場合
   └─ \r\nの場合、startIndex - 2を返す
   └─ 改行文字の場合、startIndex - 1を返す
4. 順方向の場合
   └─ \r\nの場合、startIndex + 2を返す
   └─ 改行文字の場合、startIndex + 1を返す
5. 改行でない場合、startIndexをそのまま返す
```

### フローチャート

```mermaid
flowchart TD
    subgraph skip
        A1[開始] --> B1{startIndex === false?}
        B1 -->|Yes| C1[return false]
        B1 -->|No| D1[cursor = startIndex]
        D1 --> E1{cursor in range?}
        E1 -->|No| F1{boundary reached?}
        F1 -->|Yes| G1[return cursor]
        F1 -->|No| H1[return false]
        E1 -->|Yes| I1[char = text.charAt cursor]
        I1 --> J1{char matches?}
        J1 -->|No| K1[return cursor]
        J1 -->|Yes| L1{backwards?}
        L1 -->|Yes| M1[cursor--]
        L1 -->|No| N1[cursor++]
        M1 --> E1
        N1 --> E1
    end

    subgraph skipNewline
        A2[開始] --> B2{startIndex === false?}
        B2 -->|Yes| C2[return false]
        B2 -->|No| D2[char = text.charAt startIndex]
        D2 --> E2{backwards?}
        E2 -->|Yes| F2{\\r\\n?}
        F2 -->|Yes| G2[return startIndex - 2]
        F2 -->|No| H2{newline?}
        H2 -->|Yes| I2[return startIndex - 1]
        H2 -->|No| J2[return startIndex]
        E2 -->|No| K2{\\r\\n?}
        K2 -->|Yes| L2[return startIndex + 2]
        K2 -->|No| M2{newline?}
        M2 -->|Yes| N2[return startIndex + 1]
        M2 -->|No| J2
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-66-01 | モナド的連鎖 | startIndex=falseの場合はfalseを返す | 連鎖呼び出し時 |
| BR-66-02 | 境界処理 | テキスト境界に達した場合も-1または文字列長を返す | スキップが成功した場合 |
| BR-66-03 | 改行文字 | \n、\r、\u2028、\u2029を改行として認識 | skipNewline |
| BR-66-04 | CRLFの特殊処理 | \r\nは1つの改行として扱う | skipNewline |

### 計算ロジック

各プリセット関数の定義:
- `skipWhitespace`: `/\s/` にマッチする文字をスキップ
- `skipSpaces`: `" \t"` をスキップ
- `skipToLineEnd`: `",; \t"` をスキップ
- `skipEverythingButNewLine`: `/[^\n\r]/` にマッチする文字をスキップ

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

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

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

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

該当なし

## エラー処理

### エラーケース一覧

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

### リトライ仕様

該当なし

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

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

## パフォーマンス要件

- O(n)の線形時間でスキップを完了（nはスキップ範囲の文字数）
- メモリ割り当てなし（インプレース処理）

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

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

## 備考

- skip関数は高階関数（関数を返す関数）として実装されている
- 逆方向スキップはisPreviousLineEmptyで主に使用される

---

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

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

### 推奨読解順序

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

型定義とオプションを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | skip.js | `src/utilities/skip.js` | SkipOptions型定義 |

**読解のコツ**: `@typedef {{backwards?: boolean}} SkipOptions` で逆方向スキップのオプションが定義されている。

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

公開APIとしてのエクスポート構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | public.js | `src/utilities/public.js` | エクスポート定義 |

**主要処理フロー**:
- **150-156行目**: skip、skipSpaces、skipWhitespace、skipToLineEnd、skipEverythingButNewLineのエクスポート
- **157-159行目**: skipInlineComment、skipNewline、skipTrailingCommentのエクスポート

#### Step 3: skip関数の実装を理解する

汎用スキップロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | skip.js | `src/utilities/skip.js` | skip関数とプリセット定義 |

**主要処理フロー**:
- **9-44行目**: skip関数（高階関数）
- **11行目**: backwardsオプションの取得
- **16-18行目**: startIndex=falseの処理（モナド的）
- **22-30行目**: 文字マッチとカーソル移動
- **35-42行目**: 境界処理
- **49-61行目**: プリセット関数の定義

#### Step 4: 特殊スキップ関数を理解する

コメントと改行のスキップを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | skip-inline-comment.js | `src/utilities/skip-inline-comment.js` | インラインコメントスキップ |
| 4-2 | skip-trailing-comment.js | `src/utilities/skip-trailing-comment.js` | トレイリングコメントスキップ |
| 4-3 | skip-newline.js | `src/utilities/skip-newline.js` | 改行スキップ |

**主要処理フロー（skip-newline.js）**:
- **8-12行目**: 改行文字の判定関数
- **23-51行目**: skipNewline本体（逆方向対応）
- **30-38行目**: backwards=trueの場合の処理
- **39-48行目**: 順方向の場合の処理

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

```
skip (skip.js) [高階関数]
    │
    ├── skipWhitespace = skip(/\s/)
    │
    ├── skipSpaces = skip(" \t")
    │
    ├── skipToLineEnd = skip(",; \t")
    │
    └── skipEverythingButNewLine = skip(/[^\n\r]/)

skipInlineComment (skip-inline-comment.js)
    └── /* ... */ をスキップ

skipTrailingComment (skip-trailing-comment.js)
    │
    └── skipEverythingButNewLine (skip.js)
           └── // ... をスキップ

skipNewline (skip-newline.js)
    └── 改行文字（\n, \r, \r\n, \u2028, \u2029）をスキップ
```

### データフロー図

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

skip(characters):
characters ───────────▶ 関数を生成 ────────────────────▶ skipFunction

skipFunction(text, startIndex, options):
text ─────────────────▶
startIndex                  │
options.backwards           ▼
                       startIndex === false? ───────────▶ false
                            │
                            ▼
                       文字マッチループ
                            │
                            ▼
                       マッチしない文字で停止 ──────────▶ position (number)
                            │
                            ▼
                       境界到達 ──────────────────────▶ -1 or text.length
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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` | ソース | 改行スキップ |
| public.js | `src/utilities/public.js` | ソース | 公開エクスポート |
| is-next-line-empty.js | `src/utilities/is-next-line-empty.js` | ソース | 使用例 |
| is-previous-line-empty.js | `src/utilities/is-previous-line-empty.js` | ソース | 使用例（backwards） |
| index.js | `src/index.js` | ソース | API公開（util経由） |
