# 通知設計書 53-edit失敗通知

## 概要

本ドキュメントは、Julia REPLの `Ctrl-Q` キーバインドによるエディタ起動操作が失敗した場合に発行される情報通知の設計を記述する。

### 本通知の処理概要

REPLにおいてスタックトレースやメソッドテーブルの行番号を入力し `Ctrl-Q` を押すと、対応するソースファイルをエディタで開く機能がある。この操作が `ProcessFailedException`、`Base.IOError`、または `SystemError` により失敗した場合に、エラー情報を `@info` レベルで通知する。

**業務上の目的・背景**：Julia REPLは、エラー発生時のスタックトレースから直接ソースコードをエディタで開く機能を提供している。この機能はデバッグ効率を大幅に向上させるが、エディタのパス不正、エディタプロセスの異常終了、ファイルアクセスエラーなどの理由で失敗する可能性がある。この通知は、エディタ起動失敗をユーザーに通知しつつ、REPLセッションの継続を妨げないために存在する。

**通知の送信タイミング**：`Ctrl-Q` キーバインドによる `InteractiveUtils.edit` 呼び出しが `ProcessFailedException`、`Base.IOError`、または `SystemError` をスローした場合にトリガーされる。

**通知の受信者**：REPLを使用している開発者本人。ターミナルのログ出力を通じて表示される。

**通知内容の概要**：「edit failed」というメッセージと、発生した例外の詳細情報（`_exception=ex`）が含まれる。

**期待されるアクション**：開発者は、エディタの設定（`EDITOR` 環境変数や `JULIA_EDITOR` 環境変数）を確認し、正しいエディタが指定されているか、対象ファイルが存在するかを確認する。

## 通知種別

ログ（Info） - Julia標準ロギングフレームワーク `@info` マクロによるコンソール出力

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（キーバインドハンドラ内で即座に出力） |
| 優先度 | 低（Infoレベル） |
| リトライ | 無し |

### 送信先決定ロジック

REPLセッション中のコード入力者本人が受信者。`@info` マクロによりグローバルロガーの設定に従い出力される。

## 通知テンプレート

### メール通知の場合

該当なし（コンソールログ出力のみ）

### 本文テンプレート

```
Info: edit failed
  exception = {例外の詳細}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| _exception | 発生した例外オブジェクト | catch ブロックの `ex` 変数 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | `Ctrl-Q` キー押下によるエディタ起動 | `InteractiveUtils.edit` が `ProcessFailedException`、`Base.IOError`、または `SystemError` をスロー | `repl_keymap` の `"^Q"` ハンドラ内で処理 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 正常なedit実行 | エディタが正常に起動・終了した場合は通知されない |
| 上記3種以外の例外 | `ProcessFailedException`、`Base.IOError`、`SystemError` 以外の例外は `rethrow()` される |
| 無効な行番号 | `n === nothing` または範囲外、REPL行の場合は edit 自体が実行されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Ctrl-Q キー押下] --> B[入力バッファから行番号取得]
    B --> C{有効な行番号?}
    C -->|No| D[バッファに文字列書き戻し]
    C -->|Yes| E[InteractiveUtils.edit 呼び出し]
    E --> F{例外発生?}
    F -->|No| G[エディタ正常終了]
    G --> H[LineEdit.refresh_line]
    F -->|Yes| I{例外種別チェック}
    I -->|ProcessFailedException/IOError/SystemError| J[@info edit failed]
    I -->|その他| K[rethrow]
    J --> H
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし

### テーブル別参照項目詳細

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ProcessFailedException | エディタプロセスが異常終了 | `@info` で通知し、REPL行をリフレッシュして続行 |
| Base.IOError | I/O操作エラー（ファイル不在など） | 同上 |
| SystemError | システムレベルのエラー | 同上 |
| その他の例外 | 予期しないエラー | `rethrow()` で上位に伝播 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（REPLセッション中いつでも発生し得る）

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

通知にはファイルパス情報が含まれる可能性があるが、ユーザーのローカル環境情報であり、リモートに送信されることはない。

## 備考

- `repl.last_shown_line_infos` から行情報を取得し、`Base.fixup_stdlib_path` でパスを正規化してからエディタに渡す
- REPL入力行（`"REPL["` で始まるパス）は edit 対象から除外される
- `_exception=ex` パラメータにより、例外の詳細情報がログメッセージに含まれる

---

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

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

### 推奨読解順序

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

REPLの行情報管理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | REPL.jl | `stdlib/REPL/src/REPL.jl` | `repl.last_shown_line_infos` フィールド。スタックトレース表示時に保存される行情報 |

**読解のコツ**: `last_shown_line_infos` は `(ファイルパス, 行番号)` のタプル配列で、直前に表示されたスタックトレースの各フレーム情報を保持する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | REPL.jl | `stdlib/REPL/src/REPL.jl` | 1624-1644行目: `"^Q"` キーバインドハンドラ |

**主要処理フロー**:
1. **1625行目**: `repl.last_shown_line_infos` から行情報リストを取得
2. **1626行目**: `LineEdit.buffer(s)` から入力文字列を取得
3. **1627行目**: `tryparse(Int, str)` で行番号をパース
4. **1630行目**: 有効範囲チェック（`n <= 0 || n > length(linfos)`）
5. **1634行目**: `InteractiveUtils.edit(Base.fixup_stdlib_path(...), ...)` でエディタ起動
6. **1636行目**: 特定例外のみ catch し、それ以外は rethrow
7. **1637行目**: `@info "edit failed" _exception=ex` で通知

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

```
REPL keymap handler "^Q"  [REPL.jl:1624]
    |
    +-- LineEdit.buffer(s) -> String(take!(...))
    +-- tryparse(Int, str)
    +-- [有効性チェック]
    +-- InteractiveUtils.edit(path, line)  [InteractiveUtils]
    |       |
    |       +-- [例外発生時]
    |              +-- @info "edit failed" _exception=ex  [REPL.jl:1637]
    |
    +-- LineEdit.refresh_line(s)  [LineEdit.jl]
```

### データフロー図

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

Ctrl-Q キー入力 +           InteractiveUtils.edit 呼び出し
バッファ内の行番号 -------->         |                        -----> エディタ起動（正常時）
                                    |
repl.last_shown_line_infos          +-> 例外発生時
  (ファイルパス, 行番号)                    |
                                          +-> @info -----------> stderr ("edit failed")
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| REPL.jl | `stdlib/REPL/src/REPL.jl` | ソース | Ctrl-Q キーバインドハンドラ（1624-1644行目） |
| editless.jl | `stdlib/InteractiveUtils/src/editless.jl` | ソース | `edit` 関数の実装（エディタ起動） |
| LineEdit.jl | `stdlib/REPL/src/LineEdit.jl` | ソース | `buffer`, `refresh_line` などのREPL行編集機能 |
