# 画面設計書 10-Pager（ページャ）

## 概要

長いテキスト出力をページ単位でスクロール表示するUIコンポーネントの設計書。現在位置とパーセンテージをヘッダに表示する。

### 本画面の処理概要

本画面は、長いテキストをページ単位でスクロール表示するためのターミナルUIコンポーネントである。`REPL.TerminalMenus` サブモジュールの `Pager` 型として実装されている。

**業務上の目的・背景**：Juliaのオブジェクトやテキストが端末画面に収まりきらない場合に、ページ単位でスクロールしながら内容を確認するためのUIコンポーネントを提供する。従来のUnixの `less` コマンドに類似した機能をJulia内蔵のコンポーネントとして提供する。オブジェクトの `show()` 出力を自動的にテキスト化し、行分割してページング表示する。ヘッダには現在の表示位置（行番号/総行数）とパーセンテージが表示される。

**画面へのアクセス方法**：プログラムから `pager(terminal, object)` または `pager(object)` として呼び出す。テキスト文字列から直接ページャを作成する場合は `Pager(text; pagesize=10)` でオブジェクトを作成し `request(pager)` で表示する。

**主要な操作・処理内容**：
1. `Pager(text; pagesize=10)` でページャオブジェクトを作成するか、`pager(object)` でオブジェクトを直接ページングする
2. `pager(object)` の場合は `show(ctx, "text/plain", object)` でテキスト表現を取得する
3. テキストは `readlines(IOBuffer(text))` で行分割される
4. `request(pager)` でページング表示を開始する
5. 上下矢印キー/PageUp/PageDownでスクロールする
6. ヘッダに `(現在行 / 総行数) パーセンテージ%` が表示される
7. Enterキーまたは `q` キーで終了する

**画面遷移**：プログラムから呼び出され、終了後にプログラムに制御が戻る。独立したUIコンポーネントであり、REPLモード遷移には含まれない。

**権限による表示制御**：権限による表示制御は存在しない。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 102 | REPL | 主機能 | REPL.TerminalMenusサブモジュール内のPager型として実装。pager()で呼び出し |
| 36 | テキスト出力 | 主機能 | printによるテキストのページ単位表示。現在位置・パーセンテージをヘッダに表示 |
| 39 | マルチメディアI/O | 補助機能 | show(ctx, "text/plain", object)によるオブジェクトのテキスト表現取得 |
| 35 | IOストリーム | 補助機能 | IOBuffer/IOContextを使用したページ描画バッファの構築 |
| 31 | 文字列分割・結合 | 補助機能 | readlines(IOBuffer(text))によるテキストの行分割処理 |

## 画面種別

ページング表示（テキストビューア）

## URL/ルーティング

該当なし（ターミナルベースのUIコンポーネント）

## 入出力項目

| 項目名 | 入出力 | 型 | 説明 |
|--------|--------|-----|------|
| text | 入力 | AbstractString | ページング表示するテキスト（Pagerコンストラクタ用） |
| object | 入力 | Any | ページング表示するオブジェクト（pager()関数用） |
| pagesize | 入力 | Int | 一度に表示する行数（デフォルト: 10。pager()では端末高さの半分） |
| 戻り値 | 出力 | Nothing | Pagerはnothingを返す |

## 表示項目

| 項目名 | 表示内容 | 条件 |
|--------|----------|------|
| ヘッダー | `(現在行 / 総行数) パーセンテージ%` | 常に表示 |
| テキスト行 | テキストの各行 | 現在のページ範囲の行 |
| カーソルインジケータ | スクロール方向の矢印 | ConfiguredMenuの標準表示 |

## イベント仕様

### 1-スクロール（上下矢印キー）

上下矢印キーで1行単位のスクロールを行う。`move_up!()` / `move_down!()` で `pageoffset` が更新される。

### 2-ページスクロール（PageUp/PageDown）

PageUp/PageDownでページ単位（pagesize行）のスクロールを行う。

### 3-終了（Enter / q）

Enterキーで `pick()` が `true` を返すため、メニューループを終了する。`q` キーでも `cancel()` 経由で終了する。Pagerの `pick()` は常に `true` を返し、`cancel()` は `nothing` を返す。

## データベース更新仕様

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

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

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| - | 情報 | ヘッダー文字列（位置/総行数 パーセンテージ%） | 常に表示 |

## 例外処理

| 例外 | 発生条件 | 対応 |
|------|----------|------|
| InterruptException | Ctrl+C押下（ctrl_c_interrupt=true） | InterruptExceptionをスロー |
| show()エラー | オブジェクトのshow()が失敗した場合 | エラーが上位に伝播する |

## 備考

- `Pager` は `_ConfiguredMenu{C}` を継承するが、`selected` フィールドは `Nothing` 型（選択概念がない）
- `pager(terminal, object)` は端末サイズを取得し、`pagesize = div(lines, 2)` で表示行数を決定する
- `columns -= 3` で描画バッファの幅を端末幅より3文字分狭く設定する（インジケータ用スペース）
- `show(ctx, "text/plain", object)` でカラー対応のテキスト表現を取得する
- `header()` は `lpad` を使用して行番号とパーセンテージを右揃えにフォーマットする
- `pick()` が常にtrueを返すため、Enterキーは即終了動作となる
- `cancel()` が `nothing` を返すため、`selected()` も `nothing` を返す

---

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

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

### 推奨読解順序

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

Pagerの中核データ構造はPager構造体であり、RadioMenu/MultiSelectMenuと同じConfiguredMenuパターンを使用する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Pager.jl | `stdlib/REPL/src/TerminalMenus/Pager.jl` | Pager構造体（3-9行）: lines(Vector{String}), pagesize, pageoffset, selected(Nothing), config |

**読解のコツ**: Pagerは `_ConfiguredMenu{C}` を継承しているが、`selected` が `Nothing` 型であることが重要。これによりAbstractMenuインタフェースに準拠しつつ、「選択」概念を持たない表示専用コンポーネントとして動作する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Pager.jl | `stdlib/REPL/src/TerminalMenus/Pager.jl` | Pagerコンストラクタ（11-14行）: テキストの行分割とConfig構築 |
| 2-2 | Pager.jl | `stdlib/REPL/src/TerminalMenus/Pager.jl` | pager()関数（33-42行）: オブジェクトのshow→テキスト化→Pager作成→request |

**主要処理フロー**:
1. **34行**: `displaysize(terminal)` で端末サイズを取得
2. **35行**: `columns -= 3` で描画幅を調整
3. **37行**: `IOContext` でカラー対応・サイズ制限付きバッファを構築
4. **38行**: `show(ctx, "text/plain", object)` でテキスト表現を取得
5. **39行**: `Pager(text; pagesize=div(lines, 2))` でPagerを作成
6. **40行**: `request(terminal, pager)` でページング表示を開始

#### Step 3: 表示処理の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Pager.jl | `stdlib/REPL/src/TerminalMenus/Pager.jl` | header()（16-21行）: 位置情報とパーセンテージの計算 |
| 3-2 | Pager.jl | `stdlib/REPL/src/TerminalMenus/Pager.jl` | writeline()（29-31行）: 各行の表示 |
| 3-3 | AbstractMenu.jl | `stdlib/REPL/src/TerminalMenus/AbstractMenu.jl` | request()（181-247行）: メニューループ（スクロール、終了処理） |

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

```
ユーザープログラム
    |
    +-- pager(terminal, object)               [33行]
    |       |
    |       +-- displaysize(terminal)         [34行]
    |       +-- IOContext(buffer, :color, :displaysize)  [37行]
    |       +-- show(ctx, "text/plain", object)          [38行]
    |       +-- Pager(text; pagesize=div(lines,2))       [39行]
    |       |       +-- readlines(IOBuffer(text))        [12行]
    |       |
    |       +-- request(terminal, pager)                 [40行]
    |               |
    |               +-- printmenu() 初期描画
    |               |       +-- header() → "(行番号 / 総行数) パーセンテージ%"
    |               |       +-- writeline() → テキスト行表示
    |               |
    |               +-- メインループ
    |               |   +-- readkey()
    |               |   +-- 上下矢印 → move_up!/move_down!
    |               |   +-- Enter → pick() returns true → 終了
    |               |   +-- q → cancel() → 終了
    |               |
    |               +-- nothing (戻り値)
    |
    +-- pager(object)                         [42行]
            +-- default_terminal()
            +-- pager(terminal, object)
```

### データフロー図

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

object           -----> show(ctx, "text/plain", object) -----> テキスト文字列
                        |
                  readlines(IOBuffer(text))
                        |
                  Pager(lines, pagesize, ...)
                        |
request() 呼出  -----> printmenu()
                        |
                  header()                      -----> "(行 / 総行数) %"
                  writeline()                   -----> テキスト行表示
                        |
上下キー         -----> move_up!/move_down!
                  printmenu() 再描画             -----> スクロール表示
                        |
Enter/q          -----> pick()/cancel()          -----> nothing
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Pager.jl | `stdlib/REPL/src/TerminalMenus/Pager.jl` | ソース | Pager型定義。header(), writeline(), pick(), cancel(), pager() |
| AbstractMenu.jl | `stdlib/REPL/src/TerminalMenus/AbstractMenu.jl` | ソース | request(), move_up!(), move_down!(), printmenu() |
| config.jl | `stdlib/REPL/src/TerminalMenus/config.jl` | ソース | Config構造体（Pagerで使用） |
| TerminalMenus.jl | `stdlib/REPL/src/TerminalMenus/TerminalMenus.jl` | ソース | モジュール定義、export |
