# 機能設計書 35-_omz_register_handler

## 概要

本ドキュメントは、Oh My Zshの`_omz_register_handler`関数の機能設計について記載する。この関数は、非同期プロンプト処理のためのハンドラ関数を登録し、precmdフックで自動実行されるようにする機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：シェルプロンプトにGit情報などの動的な情報を表示する場合、情報取得に時間がかかるとプロンプト表示が遅延し、ユーザー体験が低下する。非同期処理を導入することで、プロンプト表示を妨げずにバックグラウンドで情報を取得し、取得完了後にプロンプトを更新できる。この関数は、そのような非同期処理のハンドラを登録するAPIを提供する。

**機能の利用シーン**：Git情報表示などの重い処理をプロンプトに含める際、非同期で実行するためにハンドラ関数を登録するケースで利用される。Oh My Zshの内部APIとして、主にlib/git.zshから呼び出される。

**主要な処理内容**：
1. 引数として渡された関数名のバリデーション
2. 重複登録の防止チェック
3. `_omz_async_functions`配列へのハンドラ追加
4. 必要に応じてprecmdフックに`_omz_async_request`を登録

**関連システム・外部連携**：ZSHのadd-zsh-hookモジュールを使用してprecmdフックに登録する。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | ターミナルプロンプト | 結果表示画面 | 登録されたハンドラの出力がプロンプトに反映される |

## 機能種別

設定・登録処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $1 | string | Yes | 登録する非同期ハンドラ関数名 | 空でないこと、関数として定義されていること、未登録であること |

### 入力データソース

- **グローバル配列**: `_omz_async_functions` - 登録済みハンドラの一覧
- **ZSH組み込み変数**: `functions` - 定義済み関数の連想配列
- **ZSH組み込み変数**: `precmd_functions` - precmdフック関数の配列

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 0（成功）または暗黙の非0（無効な入力時の早期リターン） |

### 出力先

- グローバル配列 `_omz_async_functions` にハンドラ名を追加
- `precmd_functions` に `_omz_async_request` を追加（初回のみ）

## 処理フロー

### 処理シーケンス

```
1. シェルオプションの一時設定
   └─ noksharrays、unsetを有効化
2. グローバル配列の宣言
   └─ _omz_async_functions配列を型指定付きで宣言
3. バリデーションチェック
   └─ $1が空、関数未定義、または登録済みの場合はreturn
4. ハンドラ配列への追加
   └─ _omz_async_functions+=("$1")
5. precmdフックの登録
   └─ _omz_async_requestがprecmdに未登録かつ定義済みの場合、add-zsh-hookで登録
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[シェルオプション設定]
    B --> C[グローバル配列宣言]
    C --> D{$1が空?}
    D -->|Yes| K[return]
    D -->|No| E{関数が定義済み?}
    E -->|No| K
    E -->|Yes| F{既に登録済み?}
    F -->|Yes| K
    F -->|No| G[配列にハンドラを追加]
    G --> H{precmdに_omz_async_request未登録?}
    H -->|Yes| I{_omz_async_requestが定義済み?}
    I -->|Yes| J[add-zsh-hookでprecmdに登録]
    I -->|No| K
    H -->|No| K
    J --> K[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-35-1 | 関数存在チェック | 登録対象の関数が定義されている必要がある | 常時 |
| BR-35-2 | 重複防止 | 同じハンドラの二重登録を防止する | 常時 |
| BR-35-3 | 自動フック登録 | precmdフックに_omz_async_requestを自動登録する | 初回ハンドラ登録時 |
| BR-35-4 | 空引数拒否 | 空の引数は無視される | 常時 |

### 計算ロジック

重複チェックにはZSHの配列検索機能を使用：
```zsh
(( ${_omz_async_functions[(Ie)$1]} ))  # $1がインデックスに存在するかチェック
```

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

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

該当なし（データベースを使用しない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 暗黙のエラー | 引数が空 | 何もせずreturn |
| - | 暗黙のエラー | 関数が未定義 | 何もせずreturn |
| - | 暗黙のエラー | 既に登録済み | 何もせずreturn |

### リトライ仕様

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

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

該当なし

## パフォーマンス要件

配列操作のみで実行されるため、非常に高速。シェル起動時に1回実行されることを想定。

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

- 登録できるのは既に定義されている関数のみ
- 任意のコードを登録することはできない

## 備考

- このAPIはOh My Zsh内部用であり、変更される可能性がある
- 主な使用元: lib/git.zsh の `_defer_async_git_register` 関数

---

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

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

### 推奨読解順序

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

非同期プロンプトシステム全体のデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | async_prompt.zsh | `lib/async_prompt.zsh` | グローバル変数の宣言と用途を確認 |

**読解のコツ**:
- `_omz_async_functions`: 登録されたハンドラ関数名の配列
- `_OMZ_ASYNC_FDS`: ファイルディスクリプタの連想配列
- `_OMZ_ASYNC_PIDS`: プロセスIDの連想配列
- `_OMZ_ASYNC_OUTPUT`: 非同期処理結果の連想配列

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | async_prompt.zsh | `lib/async_prompt.zsh` | `_omz_register_handler`関数の定義（28-43行目） |

**主要処理フロー**:
1. **29行目**: `setopt localoptions noksharrays unset` - シェルオプション設定
2. **30行目**: `typeset -ga _omz_async_functions` - グローバル配列宣言
3. **32-35行目**: バリデーション（空チェック、関数存在チェック、重複チェック）
4. **36行目**: `_omz_async_functions+=("$1")` - 配列への追加
5. **38-42行目**: precmdフックへの登録処理

#### Step 3: 関連する非同期処理関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | async_prompt.zsh | `lib/async_prompt.zsh` | `_omz_async_request`関数（46-107行目） |
| 3-2 | async_prompt.zsh | `lib/async_prompt.zsh` | `_omz_async_callback`関数（110-142行目） |

**主要処理フロー**:
- `_omz_async_request`: 登録されたハンドラをサブシェルで実行
- `_omz_async_callback`: 非同期処理完了時にプロンプトを再描画

#### Step 4: 使用例を確認する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | git.zsh | `lib/git.zsh` | `_defer_async_git_register`関数（163-179行目） |

**主要処理フロー**:
- **167行目**: `_omz_register_handler _omz_git_prompt_info`
- **173行目**: `_omz_register_handler _omz_git_prompt_status`

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

```
_omz_register_handler($1)
    │
    ├─ 内部: バリデーションチェック
    │      ├─ [[ -z "$1" ]] - 空チェック
    │      ├─ (( ! ${+functions[$1]} )) - 関数存在チェック
    │      └─ (( ${_omz_async_functions[(Ie)$1]} )) - 重複チェック
    │
    ├─ _omz_async_functions+=("$1") - 配列への追加
    │
    └─ add-zsh-hook precmd _omz_async_request - フック登録（初回のみ）
```

### データフロー図

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

関数名($1) ─────▶ バリデーション ─────▶ _omz_async_functions配列
                        │                       │
                        ▼                       ▼
                 add-zsh-hook ─────────▶ precmd_functions
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| async_prompt.zsh | `lib/async_prompt.zsh` | ソース | _omz_register_handler関数の定義元 |
| git.zsh | `lib/git.zsh` | ソース | 主な呼び出し元（git_prompt_info/status登録） |
| oh-my-zsh.sh | `oh-my-zsh.sh` | ソース | lib/async_prompt.zshを読み込むメインスクリプト |
