# 帳票設計書 1-プレーンテキスト出力

## 概要

本ドキュメントは、Windows Terminal のテキストバッファから選択範囲のプレーンテキストを取得する処理の設計を記述する。クリップボードへのコピー操作やバッファエクスポート処理において使用される基盤的な帳票出力機能である。

### 本帳票の処理概要

テキストバッファ（TextBuffer）上でユーザーが選択した範囲のテキストデータを、装飾情報（色・スタイル）を一切含まない純粋なプレーンテキストとして抽出・出力する。

**業務上の目的・背景**：ターミナル上で表示されたコマンド出力やテキスト情報を、他のアプリケーション（テキストエディタ・メール・チャットツール等）に貼り付けるための最も基本的なテキスト取得手段を提供する。ANSIエスケープコードやリッチフォーマット情報を含まないため、あらゆるテキスト受付先で問題なく利用できる汎用性の高い出力形式である。

**帳票の利用シーン**：ユーザーがターミナル画面上でマウスドラッグやキーボードショートカット（Ctrl+Shift+C等）によりテキストを選択し、クリップボードにコピーする際に使用される。また、バッファ全体をテキストファイルとしてエクスポートする際の基盤処理としても利用される。

**主要な出力内容**：
1. 選択範囲内の各行のテキスト文字列（Unicode wstring）
2. 行間の改行コード（\r\n）
3. 末尾空白のトリミング処理済みテキスト

**帳票の出力タイミング**：ユーザーがテキスト選択後にコピー操作（Ctrl+Shift+C、右クリックメニュー、Enterキー）を行った際、またはバッファエクスポート操作時に出力される。

**帳票の利用者**：Windows Terminal を使用するすべてのユーザー（開発者・システム管理者・一般利用者）。

## 帳票種別

テキストデータ出力（クリップボードコピー / メモリ上文字列）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | ターミナルペイン | - | テキスト選択 + Ctrl+Shift+C / Enterキー / 右クリックメニュー「コピー」 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | プレーンテキスト（メモリ上 std::wstring） |
| 用紙サイズ | 該当なし |
| 向き | 該当なし |
| ファイル名 | 該当なし（クリップボード経由） |
| 出力方法 | クリップボードへのコピー（CF_UNICODETEXT 形式） |
| 文字コード | UTF-16LE（wstring） |

### PDF固有設定

該当なし

### Excel固有設定

該当なし

## 帳票レイアウト

### レイアウト概要

プレーンテキスト出力は構造化されたレイアウトを持たない。選択範囲の各行のテキストが改行コード（\r\n）で区切られた単一の文字列として出力される。

```
┌─────────────────────────────────────┐
│  行1テキスト\r\n                      │
│  行2テキスト\r\n                      │
│  ...                                │
│  行Nテキスト                         │
└─────────────────────────────────────┘
```

### ヘッダー部

該当なし

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |  列幅 |
|----|-------|------|-------------|---------|------|
| 1 | 行テキスト | 各行の選択範囲内テキスト | TextBuffer::ROW::GetText(rowBeg, rowEnd) | プレーンテキスト | 可変 |
| 2 | 改行コード | 行間の区切り文字 | 固定値 \r\n | 制御文字 | 2バイト |

### フッター部

該当なし

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 選択開始位置（beg） | テキスト選択の開始座標（x, y） | Yes |
| 選択終了位置（end） | テキスト選択の終了座標（x, y） | Yes |
| ブロック選択モード（blockSelection） | 矩形選択か通常選択か | No（デフォルト: false） |
| 末尾空白トリミング（trimTrailingWhitespace） | 行末の空白文字を除去するか | No（デフォルト: true） |
| 改行含む（includeLineBreak） | 行間に改行を含めるか | No（デフォルト: true） |
| 折り返し行フォーマット（formatWrappedRows） | 折り返し行にもフォーマットを適用するか | No（デフォルト: false） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | 行番号（y座標） | 昇順 |
| 2 | 列番号（x座標） | 昇順 |

### 改ページ条件

該当なし（単一文字列として出力）

## データベース参照仕様

### 参照テーブル一覧

該当なし（データベースは使用しない。メモリ上のTextBufferから直接データを取得する）

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

#### TextBuffer（メモリ上バッファ）

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| ROW::GetText() | 行テキスト | rowBeg <= x < rowEnd | グリフ境界に調整済み |
| ROW::WasWrapForced() | 改行判定 | - | 折り返し行かどうかの判定に使用 |
| ROW::GetLastNonSpaceColumn() | 末尾空白トリミング位置 | trimTrailingWhitespace=true時 | 空白でない最後の列位置 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| rowBeg | blockSelection ? minX : (iRow != beg.y ? 0 : beg.x) | 該当なし | AdjustToGlyphStartで調整 |
| rowEnd | blockSelection ? maxX : (iRow != end.y ? ReadableColumnCount : end.x) | 該当なし | AdjustToGlyphEndで調整 |
| トリミング後rowEnd | min(rowEnd, GetLastNonSpaceColumn()) | 該当なし | trimTrailingWhitespace=true時のみ適用 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[コピー操作要求] --> B[CopySelectionToClipboard]
    B --> C[RetrieveSelectedTextFromBuffer]
    C --> D[CopyRequest::FromConfig でリクエスト構築]
    D --> E[TextBuffer::GetPlainText 呼び出し]
    E --> F{beg > end?}
    F -->|Yes| G[空文字列を返却]
    F -->|No| H[行ループ開始 iRow = beg.y to end.y]
    H --> I[GetRowByOffset で行取得]
    I --> J[_RowCopyHelper で行範囲計算]
    J --> K[ROW::GetText で行テキスト取得]
    K --> L{改行を追加する?}
    L -->|Yes| M[\\r\\n を追加]
    L -->|No| N[次の行へ]
    M --> N
    N --> O{最終行?}
    O -->|No| H
    O -->|Yes| P[selectedText を返却]
    P --> Q[WriteToClipboard イベント発火]
    Q --> R[終了]
    G --> R
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 選択なし | IsSelectionActive() が false | なし（コピー操作を無視） | CopySelectionToClipboard が false を返す |
| 無効な範囲 | beg > end | なし | 空文字列を返却 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 最大9001行（デフォルトスクロールバックバッファ） |
| 目標出力時間 | 即座（同期処理） |
| 同時出力数上限 | 1（ターミナルロック取得による排他制御） |

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

- テキストバッファへのアクセスは `_terminal->LockForWriting()` によるロックで排他制御される
- クリップボードへの書き込みは OS のクリップボード API を経由する
- コピーされたテキストにはパスワードやセンシティブ情報が含まれる可能性があるが、ターミナルの標準的な動作としてユーザーの操作に委ねられる

## 備考

- GetPlainText には2つのオーバーロードが存在する: `GetPlainText(point start, point end)` と `GetPlainText(const CopyRequest& req)`。前者は後者のラッパーであり、内部で CopyRequest を構築して呼び出す
- ブロック選択（矩形選択）モードでは、各行の選択範囲が minX～maxX の固定幅となる
- グリフ境界の調整（AdjustToGlyphStart / AdjustToGlyphEnd）により、マルチバイト文字や絵文字が部分的に選択されることを防止する

---

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

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

### 推奨読解順序

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

まず、テキストバッファのデータ構造と、コピーリクエストの構造体を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | textBuffer.hpp | `src/buffer/out/textBuffer.hpp` | CopyRequest 構造体（204-271行目）の各フィールド（beg, end, blockSelection, trimTrailingWhitespace, includeLineBreak, formatWrappedRows）を理解する |
| 1-2 | textBuffer.hpp | `src/buffer/out/textBuffer.hpp` | TextBuffer クラスのメモリレイアウト（356-416行目）。ROWの配列構造と仮想メモリ管理の仕組みを理解する |

**読解のコツ**: CopyRequest::FromConfig（235-270行目）は singleLine や blockSelection などのフラグから、includeLineBreak / trimTrailingWhitespace / formatWrappedRows を計算するファクトリメソッドである。各フラグの組み合わせによる出力の変化を把握すること。

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

処理の起点となるコピー操作のトリガーを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | CopySelectionToClipboard メソッド（1343-1372行目）がエントリーポイント。選択の有無チェック、フォーマット判定、RetrieveSelectedTextFromBuffer 呼び出しを行う |

**主要処理フロー**:
1. **1349行目**: ターミナルロック取得
2. **1352行目**: 選択がアクティブかチェック
3. **1362行目**: RetrieveSelectedTextFromBuffer でテキスト抽出
4. **1365-1370行目**: WriteToClipboard イベント発火

#### Step 3: テキスト取得処理を理解する

選択範囲からテキストを取得する中間層の処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TerminalSelection.cpp | `src/cascadia/TerminalCore/TerminalSelection.cpp` | RetrieveSelectedTextFromBuffer（962-1007行目）。CopyRequest 構築と GetPlainText 呼び出しの中継処理 |

**主要処理フロー**:
- **979行目**: CopyRequest::FromConfig で選択情報からリクエスト構築
- **986行目**: textBuffer.GetPlainText(req) で実際のテキスト取得

#### Step 4: コアテキスト抽出処理を理解する

TextBuffer::GetPlainText の実装詳細を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | textBuffer.cpp | `src/buffer/out/textBuffer.cpp` | GetPlainText(CopyRequest&) メソッド（2139-2163行目）。行ループ、_RowCopyHelper による範囲計算、ROW::GetText によるテキスト取得 |
| 4-2 | textBuffer.cpp | `src/buffer/out/textBuffer.cpp` | _RowCopyHelper メソッド（2088-2131行目）。ブロック選択と通常選択での行範囲計算、グリフ境界調整、末尾空白トリミング |

**主要処理フロー**:
- **2141行目**: beg > end の検証
- **2148行目**: 行ループ（beg.y から end.y まで）
- **2151行目**: _RowCopyHelper で行の開始・終了位置と改行フラグを取得
- **2154行目**: row.GetText(rowBeg, rowEnd) でテキスト取得
- **2156-2159行目**: 改行コード追加（最終行以外）

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

```
ControlCore::CopySelectionToClipboard (ControlCore.cpp:1343)
    |
    +-- Terminal::RetrieveSelectedTextFromBuffer (TerminalSelection.cpp:962)
    |       |
    |       +-- CopyRequest::FromConfig (textBuffer.hpp:235)
    |       |
    |       +-- TextBuffer::GetPlainText(req) (textBuffer.cpp:2139)
    |               |
    |               +-- TextBuffer::_RowCopyHelper (textBuffer.cpp:2088)
    |               |       |
    |               |       +-- ROW::AdjustToGlyphStart
    |               |       +-- ROW::AdjustToGlyphEnd
    |               |       +-- ROW::GetLastNonSpaceColumn
    |               |       +-- ROW::WasWrapForced
    |               |
    |               +-- ROW::GetText(rowBeg, rowEnd)
    |
    +-- WriteToClipboard.raise (ControlCore.cpp:1365)
```

### データフロー図

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

TextBuffer (メモリ)  ---> GetPlainText()           ---> std::wstring (selectedText)
  |                         |                              |
  +-- ROW[]               +-- _RowCopyHelper()            +-- WriteToClipboard イベント
  +-- 選択範囲(beg,end)   +-- ROW::GetText()                  |
                          +-- 改行コード付加                   +-- クリップボード (CF_UNICODETEXT)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| textBuffer.hpp | `src/buffer/out/textBuffer.hpp` | ヘッダー | TextBuffer クラス定義、CopyRequest 構造体定義 |
| textBuffer.cpp | `src/buffer/out/textBuffer.cpp` | ソース | GetPlainText、_RowCopyHelper の実装 |
| ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | ソース | CopySelectionToClipboard のエントリーポイント |
| ControlCore.h | `src/cascadia/TerminalControl/ControlCore.h` | ヘッダー | ControlCore クラス定義 |
| TerminalSelection.cpp | `src/cascadia/TerminalCore/TerminalSelection.cpp` | ソース | RetrieveSelectedTextFromBuffer の実装 |
| Terminal.hpp | `src/cascadia/TerminalCore/Terminal.hpp` | ヘッダー | Terminal クラス定義、TextCopyData 構造体 |
| ControlInteractivity.cpp | `src/cascadia/TerminalControl/ControlInteractivity.cpp` | ソース | ユーザー操作からのコピートリガー |
| TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | ソース | UI層からのCopySelectionToClipboard 呼び出し |
