Skip to content
All Engineering Notes
Engineering NoteTermstead

When a Terminal Cell Width Is Not a Text-Run Width

A focused Termstead debugging note about the difference between terminal-cell geometry and shaped text-run width at the GPUI renderer boundary.

Question: Why did Termstead need a per-cell forced width instead of the total shaped-run width?

rendererterminalRustGPUI

The bug was a geometry assumption#

Termstead’s renderer sits between two useful but different coordinate systems. A terminal grid describes cells: ordinary text occupies one column at a time, while combining and wide characters need different treatment. A shaped text run describes glyphs after the text has been prepared for rendering.

The bug came from treating those descriptions as interchangeable at the GPUI boundary. TextRun.force_width is the advance assigned to each glyph. It is not the total width of the shaped run.

The incorrect mental model#

The diagnosed error was:

total run width = per-glyph forced advance

Those values are only interchangeable in a very narrow ordinary-cell case. Passing a run’s total width as each glyph’s forced advance can multiply the spacing across the run and produce scattered glyph placement.

The correction#

The renderer now returns one terminal-cell width for compatible runs and leaves other runs alone:

fn forced_cell_width(text: &str, columns: u16, cell_width: gpui::Pixels) -> Option<gpui::Pixels> {
    // GPUI's `force_width` is the advance assigned to each glyph, not the
    // total width of the shaped run.
    should_force_cell_width(text, columns).then_some(cell_width)
}

The important correction is semantic rather than visual: the value passed to force_width represents one glyph advance, so the ordinary one-column case receives one cell width instead of the whole run’s width.

Why wide and combining cells are different#

The shortcut is guarded by should_force_cell_width(text, columns). A run such as abc spread across three ordinary terminal columns can use one cell width per glyph. A combining sequence cannot be treated as three independent columns, and a double-width character cannot be treated as one ordinary glyph advance. Those cases are excluded from the simple rule rather than forced into it.

What the regression test proves#

The focused test forced_width_is_one_cell_per_glyph_not_the_full_run_width establishes that an ordinary compatible run receives one cell advance per glyph, not the full shaped-run width. The neighboring focused cases preserve the exclusions for combining and wide content, along with fractional-width and resize behavior.

That is geometry and shaping evidence at the renderer boundary. It is a narrower claim than proving how every font, compositor, or device will display the result.

What this does not prove#

This correction is not pixel-golden validation, cross-device proof, physical keyboard or IME validation, complete Unicode correctness, terminal-emulator completeness, or a performance benchmark. It also does not establish frame-rate or input-latency improvement, production-scale reliability, or complete GPUI product-shell functionality.

Source