# 機能設計書 37-_omz_async_callback

## 概要

本ドキュメントは、Oh My Zshの`_omz_async_callback`関数の機能設計について記載する。この関数は、非同期処理が完了した際にファイルディスクリプタから出力を受け取り、プロンプトを再描画する機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：非同期プロンプトシステムでは、サブシェルで実行されたハンドラの結果を受け取り、プロンプトに反映する必要がある。この関数は、`zle -F`によってファイルディスクリプタが読み取り可能になった際に呼び出されるコールバックであり、結果を`_OMZ_ASYNC_OUTPUT`に格納し、プロンプトを更新する。

**機能の利用シーン**：`_omz_async_request`で起動されたサブシェルが処理を完了し、パイプに結果を出力した際に、ZSHのzleシステムによって自動的に呼び出される。

**主要な処理内容**：
1. ファイルディスクリプタからハンドラ名を逆引き
2. ファイルディスクリプタから出力を読み取り
3. 出力が変更された場合にプロンプトを再描画
4. ファイルディスクリプタをクローズ
5. zle -Fハンドラを削除
6. グローバル変数をクリーンアップ

**関連システム・外部連携**：ZSHのzleモジュール（`zle .reset-prompt`、`zle -R`）を使用してプロンプトを再描画する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | ターミナルプロンプト | 結果表示画面 | 非同期処理の結果でプロンプトが更新される |

## 機能種別

コールバック処理・画面更新

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $1 (fd) | int | Yes | 読み取り可能なファイルディスクリプタ | zle -Fによって渡される |
| $2 (err) | string | No | エラー時に渡される文字列（"hup"等） | - |

### 入力データソース

- **ファイルディスクリプタ**: サブシェルからの出力パイプ
- **グローバル連想配列**: `_OMZ_ASYNC_FDS` - FDからハンドラ名を逆引き
- **グローバル連想配列**: `_OMZ_ASYNC_OUTPUT` - 既存の出力を比較用に参照

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| _OMZ_ASYNC_OUTPUT[$handler] | string | ハンドラの出力結果 |

### 出力先

- グローバル連想配列 `_OMZ_ASYNC_OUTPUT`
- プロンプト再描画（`zle .reset-prompt`）

## 処理フロー

### 処理シーケンス

```
1. emacsモードでローカルオプション設定
2. 引数からFDとエラーを取得
3. エラーチェック
   └─ エラーがないか、"hup"（正常終了）の場合のみ処理継続
4. FDからハンドラ名を逆引き
   └─ _OMZ_ASYNC_FDS連想配列で逆引き
5. 現在の出力を保存
6. FDから出力を読み取り
   └─ IFS= read -r -u $fd -d '' で全内容を読み取り
7. 出力が変更された場合にプロンプト再描画
   └─ zle .reset-prompt && zle -R
8. FDをクローズ
   └─ exec {fd}<&-
9. zle -Fハンドラを削除
10. グローバル変数をリセット
    └─ FD/PIDを-1に設定
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[emacsモード設定]
    B --> C[引数取得: fd, err]
    C --> D{errが空 または hup?}
    D -->|No| K[zle -Fハンドラ削除のみ]
    D -->|Yes| E[FDからハンドラ名を逆引き]
    E --> F[現在の出力を保存]
    F --> G[FDから出力を読み取り]
    G --> H{出力が変更?}
    H -->|Yes| I[プロンプト再描画]
    H -->|No| J[FDクローズ]
    I --> J
    J --> K
    K --> L[FD/PIDを-1にリセット]
    L --> M[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-37-1 | 正常終了時のみ処理 | エラーがないか"hup"の場合のみ出力を読み取る | 常時 |
| BR-37-2 | 変更時のみ再描画 | 出力が変更された場合のみプロンプトを再描画する | 常時 |
| BR-37-3 | クリーンアップ | 常にFDハンドラを削除し、グローバル変数をリセットする | 常時 |

### 計算ロジック

FDからハンドラ名を逆引きする計算：
```zsh
local handler="${(k)_OMZ_ASYNC_FDS[(r)$fd]}"
# (k): キーを取得
# [(r)$fd]: 値が$fdに一致するエントリを検索
```

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | FDエラー | ファイルディスクリプタの読み取りエラー | errパラメータで通知、クリーンアップのみ実行 |
| - | hup | パイプのHangup（正常終了を含む） | 正常処理として出力を読み取る |

### リトライ仕様

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

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

該当なし

## パフォーマンス要件

- プロンプト再描画は即座に実行される必要がある
- `zle .reset-prompt`と`zle -R`の組み合わせで即時再描画を実現

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

- ファイルディスクリプタは処理後に必ずクローズされる
- グローバル変数は-1にリセットされ、再利用を防止

## 備考

- `emulate -L zsh`でZSHモードを明示的に設定
- "hup"はパイプのHangupシグナルを意味するが、正常終了の場合も含む

---

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

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

### 推奨読解順序

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

非同期プロンプトシステムで使用されるグローバル変数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | async_prompt.zsh | `lib/async_prompt.zsh` | グローバル連想配列の役割を確認 |

**読解のコツ**:
- `_OMZ_ASYNC_FDS[$handler]`: ハンドラに対応するファイルディスクリプタ
- `_OMZ_ASYNC_PIDS[$handler]`: ハンドラに対応するプロセスID
- `_OMZ_ASYNC_OUTPUT[$handler]`: ハンドラの最新出力

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

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

**主要処理フロー**:
1. **111行目**: `emulate -L zsh` - ZSHモード設定
2. **113-114行目**: `local fd=$1`, `local err=$2` - 引数取得
3. **116行目**: `[[ -z "$err" || "$err" == "hup" ]]` - エラーチェック
4. **118行目**: `local handler="${(k)_OMZ_ASYNC_FDS[(r)$fd]}"` - ハンドラ名逆引き
5. **121行目**: `local old_output="${_OMZ_ASYNC_OUTPUT[$handler]}"` - 現在出力保存
6. **124行目**: `IFS= read -r -u $fd -d '' "_OMZ_ASYNC_OUTPUT[$handler]"` - 出力読み取り
7. **127-130行目**: 出力変更時にプロンプト再描画
8. **133行目**: `exec {fd}<&-` - FDクローズ
9. **137行目**: `zle -F "$fd"` - ハンドラ削除
10. **140-141行目**: FD/PIDを-1にリセット

#### Step 3: 呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | async_prompt.zsh | `lib/async_prompt.zsh` | `_omz_async_request`関数（105行目）でzle -F登録 |

**主要処理フロー**:
- **105行目**: `zle -F "$fd" _omz_async_callback` - コールバック登録

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

```
zle -F イベント（FD読み取り可能）
    │
    └─ _omz_async_callback($fd, $err)
           │
           ├─ ${(k)_OMZ_ASYNC_FDS[(r)$fd]} - ハンドラ名逆引き
           │
           ├─ IFS= read -r -u $fd -d '' - 出力読み取り
           │
           ├─ _OMZ_ASYNC_OUTPUT[$handler] - 結果格納
           │
           ├─ 出力変更時:
           │      ├─ zle .reset-prompt - プロンプト再計算
           │      └─ zle -R - 画面再描画
           │
           ├─ exec {fd}<&- - FDクローズ
           │
           └─ zle -F "$fd" - ハンドラ削除
```

### データフロー図

```
[サブシェル出力]         [コールバック]              [プロンプト]

パイプからの出力 ───▶ _omz_async_callback
      │                      │
      │                      ▼
      │              FDから出力読み取り
      │                      │
      │                      ▼
      │              _OMZ_ASYNC_OUTPUT更新
      │                      │
      │                      ▼
      │              出力変更チェック
      │                      │
      │                 ┌────┴────┐
      │              変更あり    変更なし
      │                 │           │
      │                 ▼           │
      │         zle .reset-prompt   │
      │         zle -R              │
      │                 │           │
      │                 └────┬──────┘
      │                      │
      │                      ▼
      └────────────────▶ プロンプト表示更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| async_prompt.zsh | `lib/async_prompt.zsh` | ソース | _omz_async_callback関数の定義元 |
| git.zsh | `lib/git.zsh` | ソース | git_prompt_info等のスタブ関数が_OMZ_ASYNC_OUTPUTを参照 |
| oh-my-zsh.sh | `oh-my-zsh.sh` | ソース | lib/async_prompt.zshを読み込むメインスクリプト |
