# 通知設計書 58-TerminalMenus rawモード失敗警告

## 概要

本ドキュメントは、Julia REPLの `TerminalMenus` モジュールにおいて、ターミナルのrawモードへの切り替えに失敗した場合に発行される警告通知の設計を記述する。

### 本通知の処理概要

`TerminalMenus.request` 関数内でインタラクティブメニューを表示する際に、ターミナルのrawモード（キー入力を即座に処理するモード）への切り替えが失敗した場合に、警告を出力しメニュー操作をrawモードなしで続行する処理である。

**業務上の目的・背景**：`TerminalMenus` は、ユーザーに選択肢を提示しキーボード操作で選択を行うインタラクティブメニュー機能を提供する。この機能は `Pkg` のパッケージ選択や一般的なCLIツールで広く使用される。rawモードはキー入力をバッファリングせず即座にプログラムに渡すモードであり、矢印キーなどの特殊キー検出に必須である。一部のターミナル（非対話型端末、特定のリモート環境など）ではrawモードへの切り替えが失敗する場合がある。この警告は、rawモード失敗をユーザーに通知しつつ、可能な範囲でメニュー操作を継続するために存在する。

**通知の送信タイミング**：`request` 関数内で `REPL.Terminals.raw!(term, true)` が例外をスローした場合にトリガーされる。ただし `suppress_output` が `true` の場合は警告出力が抑制される。

**通知の受信者**：TerminalMenusを使用しているユーザー本人。ターミナルの標準エラー出力を通じて表示される。

**通知内容の概要**：「TerminalMenus: Unable to enter raw mode:」というメッセージと、発生した例外およびスタックトレース情報が含まれる。

**期待されるアクション**：ユーザーは、ターミナル環境を確認する。非対話型端末を使用している場合は、対話型ターミナルでの実行を検討する。rawモードが無効な場合、メニューの操作性は制限される可能性がある。

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（メニュー初期化時に即座に出力） |
| 優先度 | 中（Warnレベルだが、メニュー操作性に影響） |
| リトライ | 無し |

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

TerminalMenusを使用しているユーザー本人が受信者。`@warn` マクロによりグローバルロガーの設定に従いstderrへ出力される。`suppress_output` が `true` の場合は出力が抑制される。

## 通知テンプレート

### メール通知の場合

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

### 本文テンプレート

```
Warning: TerminalMenus: Unable to enter raw mode:
  exception = {例外オブジェクトとスタックトレース}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| exception | 発生した例外とスタックトレース | `(err, catch_backtrace())` タプル | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| メニュー表示 | rawモードへの切り替え失敗 | `REPL.Terminals.raw!(term, true)` が例外をスロー、かつ `suppress_output == false` | `request` 関数内の try-catch |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `suppress_output == true` | 出力抑制モードの場合は警告が出力されない |
| rawモード切り替え成功 | 正常にrawモードに入れた場合は通知されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[request 関数呼び出し] --> B[メニュー初期表示]
    B --> C[REPL.Terminals.raw! true 呼び出し]
    C --> D{例外発生?}
    D -->|No| E[raw_mode_enabled = true]
    E --> F[カーソル非表示]
    F --> G[キー入力ループ]
    D -->|Yes| H{suppress_output?}
    H -->|Yes| I[raw_mode_enabled = false]
    H -->|No| J[@warn TerminalMenus Unable to enter raw mode]
    J --> I
    I --> G
    G --> K[finally: rawモード解除]
```

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

### 参照テーブル一覧

該当なし

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

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| rawモード切り替え失敗 | `REPL.Terminals.raw!(term, true)` が例外をスロー | `@warn` で通知し、`raw_mode_enabled = false` に設定。rawモードなしでメニュー操作を続行 |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（各 request 呼び出しごとに発生し得る） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

例外情報にスタックトレースが含まれるが、ローカル環境で表示されるのみであり、セキュリティ上の問題はない。

## 備考

- `raw_mode_enabled` フラグは `finally` ブロックでの rawモード解除処理の制御に使用される（238-243行目）
- rawモードが有効な場合のみ、カーソル非表示（`\x1b[?25l`）とカーソル表示（`\x1b[?25h`）のESCAPEシーケンスが送信される
- `suppress_output` パラメータはテスト環境やバッチ処理で使用される

---

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

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

### 推奨読解順序

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

TerminalMenusの基本構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AbstractMenu.jl | `stdlib/REPL/src/TerminalMenus/AbstractMenu.jl` | 1-49行目: `AbstractMenu` の説明と必要なインターフェース |

**読解のコツ**: `AbstractMenu` のサブタイプは `pagesize` と `pageoffset` フィールドを持ち、`pick`、`cancel`、`options`、`writeline` の実装が必要である。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AbstractMenu.jl | `stdlib/REPL/src/TerminalMenus/AbstractMenu.jl` | 181-247行目: `request` 関数 |

**主要処理フロー**:
1. **188行目**: `printmenu` でメニュー初期表示
2. **191-197行目**: `REPL.Terminals.raw!(term, true)` でrawモード切り替え（try-catch付き）
3. **195行目**: 失敗時に `@warn` 出力
4. **199行目**: rawモード有効時にカーソル非表示
5. **201-237行目**: キー入力ループ（矢印キー、Enter、q、Ctrl-C の処理）
6. **238-243行目**: `finally` ブロックでrawモード解除とカーソル表示

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

```
request(term, m; cursor, suppress_output)  [AbstractMenu.jl:181]
    |
    +-- printmenu(term.out_stream, m, cursor[], init=true)  [AbstractMenu.jl:188]
    |
    +-- REPL.Terminals.raw!(term, true)  [AbstractMenu.jl:192]
    |       |
    |       +-- [例外発生時]
    |              +-- @warn "TerminalMenus: Unable to enter raw mode: "  [AbstractMenu.jl:195]
    |              +-- raw_mode_enabled = false
    |
    +-- [キー入力ループ]  [AbstractMenu.jl:201-237]
    |       |
    |       +-- readkey(term.in_stream)
    |       +-- move_up!/move_down!/page_up!/page_down!
    |       +-- pick(m, cursor[])
    |       +-- cancel(m)
    |       +-- keypress(m, c)
    |
    +-- [finally]  [AbstractMenu.jl:238-243]
           +-- raw!(term, false)  (rawモード有効時のみ)
           +-- カーソル表示復帰
```

### データフロー図

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

TTYTerminal ----------------> raw!(term, true)
                                    |
                                    +-> 成功: raw_mode_enabled = true
                                    |       +-> キー入力ループ -> selected(m) --> 選択結果
                                    |
                                    +-> 失敗: @warn ------------------> stderr
                                            +-> raw_mode_enabled = false
                                            +-> キー入力ループ(制限付き) -> selected(m)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AbstractMenu.jl | `stdlib/REPL/src/TerminalMenus/AbstractMenu.jl` | ソース | request 関数とrawモード処理（181-247行目） |
| REPL.jl | `stdlib/REPL/src/REPL.jl` | ソース | TerminalMenus のインクルード（99行目） |
