|
| 1 | +# ADR-0004: Table View CLI Behavior (Range-Based) |
| 2 | + |
| 3 | +- Status: Proposed |
| 4 | +- Date: 2025-12-08 |
| 5 | + |
| 6 | +## Context |
| 7 | + |
| 8 | +`odsview-cli-python` currently supports loading `.ods` files via |
| 9 | +`load_workbook` and listing sheet names via the `--list-sheets` CLI |
| 10 | +behavior (ADR-0003). The next milestone is to provide a simple, |
| 11 | +read-only **table view** of cell contents that works well over SSH, |
| 12 | +remains predictable, and avoids complex UI features like paging. |
| 13 | + |
| 14 | +We want a small, well-specified contract for table view that: |
| 15 | + |
| 16 | +- Matches spreadsheet users' expectations (ranges like `A1:E10`). |
| 17 | +- Provides a deterministic preview window by default. |
| 18 | +- Can be evolved later (e.g. terminal-size awareness, formatting) |
| 19 | + without breaking the initial contract. |
| 20 | + |
| 21 | +## Decision |
| 22 | + |
| 23 | +Introduce a **range-based table view mode** with the following CLI |
| 24 | +surface: |
| 25 | + |
| 26 | +### Invocation |
| 27 | + |
| 28 | +- `odsview FILE.ods` |
| 29 | + - Render a preview window of the first sheet using a default range |
| 30 | + equivalent to `A1:H10`. |
| 31 | +- Optional modifiers: |
| 32 | + - `--sheet NAME` → select a sheet by name (default: first sheet). |
| 33 | + - `--range A1:E10` → restrict the view to an explicit rectangular |
| 34 | + region. |
| 35 | + |
| 36 | +### Range semantics |
| 37 | + |
| 38 | +- Accept either: |
| 39 | + - Single cell: `A1` |
| 40 | + - Range: `A1:E10` |
| 41 | +- Semantics: |
| 42 | + - Columns: spreadsheet-style columns (A–Z, AA–ZZ, etc.); the initial |
| 43 | + implementation will cover at least A–Z and be written so it can be |
| 44 | + extended. |
| 45 | + - Rows: 1-based indices. |
| 46 | + - A single-cell reference like `A1` is interpreted as "start at A1 and |
| 47 | + use the default window size" (see defaults below). |
| 48 | +- Internally, the range is normalized to an inclusive, 1-based tuple: |
| 49 | + `(start_row, end_row, start_col, end_col)`. |
| 50 | + |
| 51 | +### Defaults when `--range` is omitted |
| 52 | + |
| 53 | +- Default window: **equivalent to `A1:H10`**: |
| 54 | + - Start at A1. |
| 55 | + - Up to 10 rows and 8 columns. |
| 56 | +- This ADR fixes these defaults to keep behavior deterministic and |
| 57 | + simple. A future ADR may introduce terminal-size detection and adjust |
| 58 | + the default window dynamically. |
| 59 | + |
| 60 | +### Sheet selection |
| 61 | + |
| 62 | +- If `--sheet` is omitted: |
| 63 | + - Use the **first** sheet as listed by `Workbook.sheets`. |
| 64 | +- If `--sheet NAME` is provided: |
| 65 | + - Match sheet by **exact name** (case-sensitive). |
| 66 | + - If not found: |
| 67 | + - Print a clear error on `stderr` (e.g. `sheet 'NAME' not found`). |
| 68 | + - Exit with code `1`. |
| 69 | + |
| 70 | +### Data extraction API |
| 71 | + |
| 72 | +Extend `odsview.workbook` with a simple, range-based iterator, e.g.: |
| 73 | + |
| 74 | +- `Workbook.iter_range(sheet: str | None, start_row: int, end_row: int, |
| 75 | + start_col: int, end_col: int) -> Iterable[list[str]]` |
| 76 | + |
| 77 | +Responsibilities: |
| 78 | + |
| 79 | +- Resolve the target sheet (default or by name). |
| 80 | +- For each logical row in the specified range: |
| 81 | + - Collect cell values for columns `start_col..end_col`. |
| 82 | + - Normalize values to strings: |
| 83 | + - Empty cells → empty string. |
| 84 | + - Numbers/dates/booleans → converted via a simple, deterministic |
| 85 | + rule (e.g. `str(value)` for the initial implementation). |
| 86 | +- Stop at the requested end row/column even if the sheet is larger. |
| 87 | + |
| 88 | +### Rendering format |
| 89 | + |
| 90 | +- Plain, whitespace-based table; no box-drawing or color: |
| 91 | + - First, iterate the rows within the chosen range to compute **column |
| 92 | + widths** based on the string lengths. |
| 93 | + - Then print each row as: |
| 94 | + - `cell_1.ljust(width_1) + " " + cell_2.ljust(width_2) + ...` |
| 95 | + - No header separators or borders in this first version. |
| 96 | +- Trailing spaces are acceptable; no horizontal clipping in this ADR. |
| 97 | + |
| 98 | +### Handling out-of-bounds and small sheets |
| 99 | + |
| 100 | +- If the requested range extends past the actual used area: |
| 101 | + - Render the full requested rectangle. |
| 102 | + - Cells beyond the real data appear as empty strings. |
| 103 | + - No special truncation notice is printed. |
| 104 | +- If the sheet is smaller than the default window: |
| 105 | + - Same behavior; the user simply sees an empty margin. |
| 106 | + |
| 107 | +### Exit codes and streams |
| 108 | + |
| 109 | +- `0`: |
| 110 | + - Successful table rendering for |
| 111 | + `odsview FILE.ods [--sheet NAME] [--range ...]`. |
| 112 | +- `2` (usage errors): |
| 113 | + - Missing `FILE` when table view is implied and no other behavior is |
| 114 | + requested: |
| 115 | + - `odsview` without args remains "help + exit 0" (existing |
| 116 | + behavior). |
| 117 | + - `odsview --sheet NAME` without `FILE`. |
| 118 | + - `odsview --range A1:E10` without `FILE`. |
| 119 | + - Malformed range syntax (e.g. `--range foo`). |
| 120 | + - These are surfaced via argparse-style usage plus a clear message. |
| 121 | +- `1` (runtime/data errors): |
| 122 | + - File not found. |
| 123 | + - Invalid or unreadable `.ods` container. |
| 124 | + - Workbook loading exceptions. |
| 125 | + - Sheet not found for `--sheet NAME`. |
| 126 | +- Streams: |
| 127 | + - `stdout`: the rendered table (rows of text) for success. |
| 128 | + - `stderr`: error diagnostics for exit codes `1` and `2`. |
| 129 | + |
| 130 | +### Interaction with `--list-sheets` |
| 131 | + |
| 132 | +- The existing `--list-sheets` behavior (ADR-0003) remains unchanged. |
| 133 | +- `--list-sheets` is treated as mutually exclusive with table view: |
| 134 | + - If `--list-sheets` is present, the CLI performs the list-sheets |
| 135 | + action regardless of `--sheet`/`--range` and ignores any table-view |
| 136 | + flags. |
| 137 | + |
| 138 | +## Consequences |
| 139 | + |
| 140 | +- Establishes a clear **range-centric** mental model (`A1:E10`) for |
| 141 | + table view, matching familiar spreadsheet usage. |
| 142 | +- The default preview (`odsview FILE.ods` → `A1:H10` on the first sheet) |
| 143 | + provides a fast, predictable snapshot without extra flags. |
| 144 | +- Future enhancements (terminal-size detection, pagination, |
| 145 | + column-width limits, richer formatting) can be added via additional |
| 146 | + ADRs without breaking this initial contract. |
0 commit comments