# 機能設計書 70-グリッド表示

## 概要

本ドキュメントは、Nushell におけるグリッド表示機能（nu-term-grid クレート）の設計を記述する。複数のテキストアイテムをターミナル幅に最適化されたグリッドレイアウトで表示する。

### 本機能の処理概要

グリッド表示機能は、テキストデータのリストを固定幅フォントに適したグリッドフォーマットに配置するアルゴリズムを提供する。スペースの最小化アルゴリズムにより、ターミナル幅を効率的に活用した表示を実現する。

**業務上の目的・背景**：ファイル一覧やデータ項目など、多数の短いテキストアイテムを一覧表示する際に、縦に並べると長くなりすぎる問題がある。グリッドレイアウトにより、画面スペースを効率的に利用できる。

**機能の利用シーン**：
- `ls` コマンド出力のグリッド形式表示
- 短いテキストアイテムの一覧表示
- ファイル名、コマンド名などの列挙表示
- タグ、ラベルなどのコンパクト表示

**主要な処理内容**：
1. `Cell` - セルの内容、幅、アライメント管理
2. `Grid` - グリッド構造とセルの管理
3. `fit_into_width()` - 幅指定によるレイアウト最適化
4. `fit_into_columns()` - 列数指定によるレイアウト
5. `Display` - グリッドの文字列レンダリング

**関連システム・外部連携**：unicode-width クレートによる Unicode 幅計算。

**権限による制御**：特別な権限制御はなく、すべてのユーザーが使用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 3 | テーブル表示 | 補助機能 | グリッド形式でのデータ表示 |

## 機能種別

データ表示 / レイアウト

## 入力仕様

### 入力パラメータ

#### GridOptions

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| direction | Direction | Yes | セル配置方向（LeftToRight/TopToBottom） | - |
| filling | Filling | Yes | セル間の区切り（スペースまたはテキスト） | - |

#### Cell

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| contents | String | Yes | セルの表示内容 | - |
| width | Width (usize) | Yes | 事前計算された幅 | - |
| alignment | Alignment | Yes | 配置（Left/Right） | - |

### 入力データソース

- **文字列リスト**: `&str` または `String` から `Cell` へ変換
- **幅指定**: `fit_into_width()` の `maximum_width` パラメータ
- **列数指定**: `fit_into_columns()` の `num_columns` パラメータ

## 出力仕様

### 出力データ

| 出力 | 型 | 説明 |
|------|-----|------|
| fit_into_width() | Option<Display> | 幅に収まる場合のみ Display を返す |
| fit_into_columns() | Display | 指定列数の Display を返す |
| Display::width() | usize | 表示の総幅 |
| Display::row_count() | usize | 行数 |
| Display::to_string() | String | レンダリングされたグリッド文字列 |

### 出力先

ターミナル出力（標準出力）。

## 処理フロー

### 処理シーケンス

```
1. グリッドの作成
   └─ Grid::new(GridOptions) で空グリッド作成

2. セルの追加
   └─ grid.add(Cell) でセルを追加
   └─ widest_cell_length, width_sum を更新

3. レイアウト計算
   └─ fit_into_width(): 幅に収まる最適な列数を探索
   └─ fit_into_columns(): 指定列数でレイアウト

4. 幅計算（fit_into_width 内部）
   └─ theoretical_max_num_lines() で理論上の最大行数を計算
   └─ width_dimensions() で各列の幅を決定

5. レンダリング
   └─ Display::fmt() で文字列に変換
   └─ アライメントに応じたパディング適用
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[Grid::new]
    B --> C[grid.add でセル追加]
    C --> D{レイアウト方式}
    D -->|幅指定| E[fit_into_width]
    D -->|列数指定| F[fit_into_columns]
    E --> G{最大セル幅 > 最大幅?}
    G -->|Yes| H[None 返却]
    G -->|No| I[theoretical_max_num_lines]
    I --> J[width_dimensions で列幅計算]
    J --> K[Dimensions 構築]
    F --> L[columns_dimensions]
    L --> K
    K --> M[Display 構築]
    M --> N[Display::fmt でレンダリング]
    N --> O{最終列?}
    O -->|No| P[パディング + 区切り出力]
    O -->|Yes| Q[内容のみ出力]
    P --> R[改行]
    Q --> R
    R --> S[String 返却]
    S --> T[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 幅チェック | 最大セル幅が指定幅を超える場合は None を返す | fit_into_width |
| BR-002 | 方向指定 | LeftToRight はタイプライター順、TopToBottom は ls 風 | Direction |
| BR-003 | 区切り | Spaces(n) でn個の空白、Text(s) で任意文字列 | Filling |
| BR-004 | 最終列 | 左寄せの最終列は末尾パディング不要 | Display::fmt |
| BR-005 | Unicode 幅 | ANSI エスケープを除去して Unicode 幅を計算 | Cell::from |

### 計算ロジック

- **総幅計算**: `total_width = 列幅合計 + 区切り幅 * (列数 - 1)`
- **理論最大行数**: セルを幅の大きい順にソートし、幅に収まる最小列数から計算
- **列幅決定**: 各列に配置されるセルの最大幅をその列の幅とする
- **セルインデックス計算**:
  - LeftToRight: `index % num_columns`
  - TopToBottom: `index / num_lines`

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| None 返却 | 表示不可 | 最大セル幅 > 指定幅 | 幅を広げるか1列表示に切り替え |

### リトライ仕様

リトライは不要。

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

本機能はトランザクション管理を行わない。

## パフォーマンス要件

- セル追加時に幅統計（widest_cell_length, width_sum）を維持
- 理論最大行数計算でソートを行う（大規模データで影響あり）
- 列幅計算は O(n) でセルを走査

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

- ANSI エスケープシーケンスは幅計算から除外される
- 特別なセキュリティ考慮事項なし

## 備考

- rust-term-grid クレートをベースに実装
- Unicode 幅の正確な計算（全角文字対応）
- 左右アライメントのサポート
- カスタム区切り文字のサポート

---

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

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

### 推奨読解順序

#### Step 1: ライブラリ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | lib.rs | `crates/nu-term-grid/src/lib.rs` | エクスポート一覧 |

**主要処理フロー**:
- **2行目**: `pub mod grid` でモジュール公開
- **4行目**: `pub use grid::Grid` で Grid 構造体を再エクスポート

#### Step 2: 基本型を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | grid.rs | `crates/nu-term-grid/src/grid.rs` | 基本型定義 |

**主要処理フロー**:
- **99-101行目**: `unicode_width_strip_ansi()` - ANSI エスケープを除去して幅計算
- **105-112行目**: `Alignment` 列挙型（Left, Right）
- **119-129行目**: `Cell` 構造体（contents, width, alignment）
- **131-148行目**: `From<String>` / `From<&str>` でセル作成
- **152-161行目**: `Direction` 列挙型（LeftToRight, TopToBottom）
- **164行目**: `Width` 型エイリアス（usize）
- **168-176行目**: `Filling` 列挙型（Spaces, Text）
- **189-197行目**: `GridOptions` 構造体（direction, filling）

#### Step 3: Grid 構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | grid.rs | `crates/nu-term-grid/src/grid.rs` | Grid 実装 |

**主要処理フロー**:
- **199-207行目**: `Dimensions` 構造体（num_lines, widths）
- **209-218行目**: `Dimensions::total_width()` - 総幅計算
- **224-231行目**: `Grid` 構造体（options, cells, widest_cell_length, width_sum, cell_count）
- **235-244行目**: `Grid::new()` - 空グリッド作成
- **248-250行目**: `Grid::reserve()` - 容量予約
- **253-260行目**: `Grid::add()` - セル追加と統計更新
- **267-272行目**: `Grid::fit_into_width()` - 幅指定レイアウト
- **276-281行目**: `Grid::fit_into_columns()` - 列数指定レイアウト

#### Step 4: レイアウト計算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | grid.rs | `crates/nu-term-grid/src/grid.rs` | レイアウトアルゴリズム |

**主要処理フロー**:
- **283-290行目**: `columns_dimensions()` - 列数から行数と列幅を計算
- **292-303行目**: `column_widths()` - 各列の幅を決定
- **305-330行目**: `theoretical_max_num_lines()` - 理論上の最大行数計算
- **332-402行目**: `width_dimensions()` - 最適な列数を探索

#### Step 5: 表示処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | grid.rs | `crates/nu-term-grid/src/grid.rs` | Display 実装 |

**主要処理フロー**:
- **409-416行目**: `Display` 構造体（grid, dimensions）
- **421-424行目**: `Display::width()` - 表示幅取得
- **427-429行目**: `Display::row_count()` - 行数取得
- **438-440行目**: `Display::is_complete()` - 全列使用確認
- **443-513行目**: `fmt::Display for Display` - 文字列レンダリング
- **516-518行目**: `spaces()` - スペース文字列生成
- **524-529行目**: `pad_string()` - パディング適用

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

```
Grid
    │
    ├─ new(GridOptions) - グリッド作成
    │
    ├─ reserve(n) - 容量予約
    │
    ├─ add(Cell) - セル追加
    │      └─ widest_cell_length 更新
    │      └─ width_sum 更新
    │      └─ cell_count 更新
    │
    ├─ fit_into_width(max_width) - 幅指定レイアウト
    │      └─ width_dimensions()
    │             └─ theoretical_max_num_lines()
    │             └─ column_widths()
    │      └─ Display 構築
    │
    └─ fit_into_columns(num_cols) - 列数指定レイアウト
           └─ columns_dimensions()
                  └─ column_widths()
           └─ Display 構築

Display
    │
    ├─ width() - 総幅取得
    │      └─ Dimensions::total_width()
    │
    ├─ row_count() - 行数取得
    │
    ├─ is_complete() - 完全性確認
    │
    └─ fmt::Display::fmt() - レンダリング
           └─ pad_string()
           └─ spaces()
```

### データフロー図

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

Vec<&str/String> ──────▶ Cell::from() ─────────────────────▶ Cell
                              │
                              └─ unicode_width_strip_ansi()
                                     └─ ANSI除去 + 幅計算

Vec<Cell> ────────────▶ Grid ─────────────────────────────▶ Display
                              │
                              ├─ fit_into_width()
                              │      └─ width_dimensions()
                              │             └─ 最適列数探索
                              │             └─ column_widths()
                              │
                              └─ fit_into_columns()
                                     └─ columns_dimensions()
                                            └─ column_widths()

Display ──────────────▶ fmt::Display ─────────────────────▶ String
                              │
                              ├─ 行ループ
                              │      └─ 列ループ
                              │             └─ pad_string()
                              │             └─ filling 適用
                              │
                              └─ 改行挿入
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| lib.rs | `crates/nu-term-grid/src/lib.rs` | ソース | ライブラリエントリ |
| grid.rs | `crates/nu-term-grid/src/grid.rs` | ソース | Grid, Cell, Display 実装 |
