| 1 | // Adapting canonical row snapshots into the flat, column-indexed shape the |
| 2 | // prediction engine reconciles against. |
| 3 | // |
| 4 | // This exists because `RowSnapshot.cells` is not column-indexed: spacer tails |
| 5 | // are omitted, so a wide glyph advances two columns from one array entry, and |
| 6 | // the canonical default tail is trimmed away entirely. Predictions are stated |
| 7 | // in columns, so the translation has to happen somewhere — and it belongs here, |
| 8 | // tested once, rather than inline at the reconciliation site where an off-by-one |
| 9 | // would silently read the wrong cell and retract a correct prediction. |
| 10 | |
| 11 | import type { CellSnapshot } from "../ghostty/terminal.ts"; |
| 12 | |
| 13 | /** |
| 14 | * Column-indexed text for one row. Empty and trimmed columns read as `""`, |
| 15 | * which is also what a prediction compares against when its glyph has not |
| 16 | * arrived yet. |
| 17 | */ |
| 18 | export function columnTexts(cells: readonly CellSnapshot[], cols: number): string[] { |
| 19 | const texts = Array.from<string>({ length: Math.max(0, cols) }).fill(""); |
| 20 | let column = 0; |
| 21 | for (const cell of cells) { |
| 22 | if (column >= texts.length) break; |
| 23 | // A spacer head owns its column but carries no grapheme of its own. |
| 24 | texts[column] = cell.spacer === "head" ? "" : cell.text; |
| 25 | column += Math.max(1, cell.width); |
| 26 | } |
| 27 | return texts; |
| 28 | } |