# 機能設計書 48-スニペット管理

## 概要

本ドキュメントは、Doom Emacsにおけるスニペット管理機能の設計仕様を記述する。yasnippetを基盤として、コードスニペットの挿入・作成・編集・管理を行い、定型コードの入力効率を大幅に向上させる機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：ソフトウェア開発において、同じパターンのコード（関数定義、クラス定義、ループ構文、条件分岐等）を繰り返し入力する作業は頻繁に発生する。これらの定型コードをスニペットとして登録し、短いキーワードで展開することで、タイピング量を削減し、入力ミスを防ぎ、開発者の生産性を向上させる。また、プロジェクト固有のスニペットライブラリを管理することで、チーム内での一貫したコーディングスタイルを実現する。

**機能の利用シーン**：
- 短いキーワードからのスニペット展開（TABキー）
- スニペット選択UI経由での挿入
- プライベートスニペットの新規作成
- 既存スニペットの編集・上書き
- プロジェクト固有スニペットの有効化
- 一時的なスニペット（auto-yasnippet）の作成・展開

**主要な処理内容**：
1. スニペットキーの入力とTABによる展開
2. プライベートスニペットの作成（+snippets/new）
3. 既存スニペットの編集（+snippets/edit）
4. スニペットフィールド間の移動
5. プロジェクトモード連動のスニペット有効化

**関連システム・外部連携**：
- yasnippet: スニペット展開エンジン
- auto-yasnippet: 一時スニペット作成
- doom-snippets: Doom標準スニペットライブラリ
- company-yasnippet: 補完候補としてのスニペット表示
- smartparens: 括弧補完との連携

**権限による制御**：特になし（全ユーザーが利用可能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面関連なし（エディタ機能） |

## 機能種別

テキスト編集 / コード自動生成

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| snippet-key | string | Yes | スニペット展開キー | yas-snippet-dirsに存在するスニペット |
| mode | symbol | No | スニペットを作成するモード | 有効なメジャーモード |
| template-uuid | string | No | 編集対象のスニペットUUID | 登録済みUUID |

### 入力データソース

- キーボード入力（スニペットキー + TAB）
- コマンド呼び出し（M-x yas-insert-snippet等）
- スニペットファイル（~/.doom.d/snippets/以下）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 展開済みコード | string | スニペットテンプレートを展開したコード |
| スニペットファイル | file | 新規作成・編集されたスニペット定義ファイル |

### 出力先

- バッファ（展開結果）
- ファイルシステム（+snippets-dir配下のスニペットファイル）

## 処理フロー

### 処理シーケンス

```
1. スニペット展開
   ├─ キー入力 + TAB: yas-expand
   └─ コマンド呼び出し: yas-insert-snippet
2. スニペット検索
   ├─ メジャーモード対応スニペット検索
   └─ プライベート/ビルトインの優先順位判定
3. テンプレート展開
   ├─ フィールド配置
   ├─ ミラー設定
   └─ トランスフォーム実行
4. フィールドナビゲーション
   ├─ TAB: 次のフィールド
   └─ S-TAB: 前のフィールド
5. 確定
   └─ ESC または 最終フィールド後のTAB
```

### フローチャート

```mermaid
flowchart TD
    A[スニペット展開要求] --> B{展開方法}
    B -->|キー+TAB| C[yas-expand]
    B -->|コマンド| D[yas-insert-snippet]

    C --> E{スニペット存在?}
    D --> E

    E -->|複数候補| F[+snippets-prompt-private]
    E -->|単一| G[テンプレート展開]
    E -->|なし| H[何もしない]

    F -->|プライベート優先| G

    G --> I[フィールド配置]
    I --> J{編集中?}
    J -->|TAB| K[次のフィールド]
    J -->|S-TAB| L[前のフィールド]
    J -->|ESC| M[スニペット終了]

    K --> J
    L --> J
    M --> N[最終コード確定]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | プライベート優先 | 同じキーのスニペットがある場合、プライベートを優先 | yas-expand実行時 |
| BR-02 | ESCで終了 | doom-escape-hookでスニペット編集を終了 | スニペット展開中 |
| BR-03 | Evilリージョン対応 | ビジュアル選択時のスニペット展開を正しく処理 | evil-visual-state時 |
| BR-04 | smartparens一時無効化 | hippie-expand経由の展開時にsmartparensを一時無効化 | hippie-exp使用時 |

### 計算ロジック

プライベートスニペット優先ロジック:
```elisp
(defun +snippets-prompt-private (prompt choices &optional display-fn)
  (when (eq this-command 'yas-expand)
    (let* ((choices (cl-remove-duplicates choices :test #'+snippets--remove-p)))
      (if (cdr choices)
          (cl-loop for fn in (cdr (memq '+snippets-prompt-private yas-prompt-functions))
                   if (funcall fn prompt choices display-fn)
                   return it)
        (car choices)))))
```

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | user-error | スニペットファイルが読み取り不可 | エラーメッセージ表示 |
| - | user-error | UUIDに対応するスニペットが見つからない | エラーメッセージ表示 |
| - | error | スニペットディレクトリが存在しない | ディレクトリ作成を確認 |

### リトライ仕様

リトライ処理は実装されていない。

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

トランザクション処理は不要。スニペット編集は通常のファイル保存で確定。

## パフォーマンス要件

- スニペット展開: 即座
- スニペット検索: yas-snippet-dirsの数に比例
- 重複排除: yas--all-templatesにadviceで適用

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

- スニペットはElispコードを含む可能性があるため、信頼できるソースからのみ取得
- ビルトインスニペットは読み取り専用で表示

## 備考

- `+snippets-dir`はデフォルトで`~/.doom.d/snippets/`
- `yas-verbosity`は2に設定（エラーのみ表示）
- プロジェクトモード（`def-project-mode!`定義）のスニペットは自動有効化
- `auto-yasnippet`で一時スニペットを作成・展開可能

---

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

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

### 推奨読解順序

#### Step 1: 設定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | config.el | `modules/editor/snippets/config.el` | yasnippet設定 |

**読解のコツ**:
- **3-4行目**: `+snippets-dir`変数定義
- **10-92行目**: yasnippet設定（use-package!ブロック）
- **47-51行目**: プロンプト関数設定（プライベート優先）
- **57-58行目**: ESCフック・smartparens連携
- **73-86行目**: キーバインド設定

#### Step 2: コマンドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | snippets.el | `modules/editor/snippets/autoload/snippets.el` | コマンド定義 |

**主要処理フロー**:
- **7-30行目**: `+snippets-prompt-private`（プライベート優先ロジック）
- **96-120行目**: フィールドナビゲーション関数
- **163-186行目**: スニペット検索コマンド
- **206-230行目**: `+snippets/new`（新規作成）
- **264-289行目**: `+snippets/edit`（編集）

#### Step 3: auto-yasnippetを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | config.el | `modules/editor/snippets/config.el` | auto-yasnippet設定 |

**主要処理フロー**:
- **123-134行目**: auto-yasnippet設定
- **126行目**: `aya-persist-snippets-dir`設定
- **127-134行目**: yas-global-mode回避advice

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

```
yas-expand (TABキー)
    │
    ├─ yas--templates-for-key-at-point
    │      └─ スニペットキー検索
    │
    ├─ +snippets-prompt-private
    │      └─ プライベート/ビルトイン優先順位決定
    │
    └─ yas-expand-snippet
           ├─ フィールド配置
           ├─ ミラー設定
           └─ evil-insert-state（Evil有効時）

+snippets/new (新規作成)
    │
    ├─ +snippets--snippet-mode-name-completing-read
    │      └─ モード選択
    │
    ├─ +snippet--ensure-dir
    │      └─ ディレクトリ作成確認
    │
    └─ yas-expand-snippet
           └─ スニペットテンプレート挿入

+snippets/edit (編集)
    │
    ├─ +snippet--completing-read-uuid
    │      └─ スニペット選択
    │
    ├─ +snippet--get-template-by-uuid
    │      └─ テンプレート検索
    │
    └─ find-file / switch-to-buffer
           └─ スニペットファイル表示
```

### データフロー図

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

スニペットキー         ┌──────────────────────┐
+ TAB    ───────────▶│ yas-expand           │
                      │ スニペット検索        │
                      └──────────────────────┘
                               │
                               ▼
                      ┌──────────────────────┐
                      │ テンプレート選択      │
                      │ (+snippets-prompt)   │
                      └──────────────────────┘
                               │
                               ▼
                      ┌──────────────────────┐
展開済みコード ◀───── │ yas-expand-snippet   │
                      │ フィールド配置       │
                      └──────────────────────┘
                               │
                               ▼
                      ┌──────────────────────┐
フィールド    ◀─────── │ フィールドナビゲーション│
編集              ▶── │ TAB/S-TAB           │
                      └──────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| config.el | `modules/editor/snippets/config.el` | ソース | yasnippet/auto-yasnippet設定 |
| snippets.el | `modules/editor/snippets/autoload/snippets.el` | ソース | コマンド定義、プロンプト関数 |
| packages.el | `modules/editor/snippets/packages.el` | 設定 | 依存パッケージ（yasnippet, auto-yasnippet, doom-snippets） |
