fix(terminal): keep a solo glyph inside its cell when the next one is taken

A single-cell segment was always given two cells of budget, so `fit_scale`
never shrank it and the clip never cut it. That is fine when the cell beside
it is blank — the lean is what keeps a pile of symbols from being shrunk for
no reason — but it is not a lean when the neighbour has a glyph of its own.
Segments are painted left to right, so the neighbour goes down after the
overshoot and lands on top of it.

Nerd Font stacks hit this on every prompt: the icons come from a fallback
face whose natural advance has nothing to do with the primary font's cell.
Measured here on Windows, Maple Mono NF CN supplying U+F059 to a 15px
Cascadia Mono grid inks 13.85px across an 8.79px cell — 58% of the next
column, which is where the count badge beside it is drawn.

`seg_budget` now asks `has_room_after` for solo segments too, the same
question the wider ones already ask. A free neighbour still lends its cell
and nothing about today's rendering changes; an occupied one does not, and
the glyph is scaled down into its own cell through the `fit_scale` path
that already exists for wide clusters. kitty and ghostty fit fallback
glyphs to the cell unconditionally; this only does it where the alternative
is drawing over something.
This commit is contained in:
l0ng-ai
2026-09-07 17:43:59 +08:00
parent 893172f57d
commit f7fe37d224
+37 -5
View File
@@ -977,10 +977,15 @@ fn native_cell_residue(style: &GlyphStyle) -> Option<char> {
/// when the neighbouring cell is blank and has nothing to lose.
fn seg_budget(solo: bool, cells: usize, room: bool, cell_width: Pixels) -> Pixels {
if solo {
// Single-cell glyphs have always been allowed to lean into the next
// cell. Narrowing that here would shrink a pile of symbols that look
// fine today, so it stays a separate decision.
cell_width * 2.
// Single-cell glyphs are allowed to lean into the next cell — that is
// what keeps the pile of symbols that look fine today from shrinking —
// but only while that cell is empty. A Nerd Font icon pulled from a
// fallback face inks well past a narrow primary's cell (1.6 cells is
// typical), and where the next cell has a glyph of its own the lean is
// not a lean, it is an overlap: the neighbour is painted afterwards
// and lands on top of the overshoot. Hand those their own cell and let
// `fit_scale` bring them down into it, the way kitty and ghostty do.
if room { cell_width * 2. } else { cell_width }
} else if room {
cell_width * (cells as f32 + 1.)
} else {
@@ -2707,7 +2712,16 @@ mod tests {
#[test]
fn seg_budget_frees_solo_symbols_and_lends_a_cell_only_when_one_is_free() {
let cell = px(10.);
assert_eq!(seg_budget(true, 1, false, cell), px(20.), "solo keeps two");
assert_eq!(
seg_budget(true, 1, true, cell),
px(20.),
"a solo glyph leans into a free cell"
);
assert_eq!(
seg_budget(true, 1, false, cell),
px(10.),
"but keeps to its own once the next cell is taken"
);
assert_eq!(
seg_budget(false, 2, false, cell),
px(22.5),
@@ -2717,6 +2731,24 @@ mod tests {
assert_eq!(seg_budget(false, 1, false, cell), px(12.5));
}
#[test]
fn a_fallback_icon_is_fitted_to_its_cell_only_when_the_next_one_is_taken() {
// Measured on Windows: Maple Mono NF CN supplying U+F059 to a
// 15px Cascadia Mono grid inks 13.85px across an 8.79px cell.
let cell = px(8.789);
let ink = px(13.845);
let leaning = fit_scale(ink, seg_budget(true, 1, true, cell));
assert_eq!(leaning, 1., "a blank neighbour still lends its cell");
let crowded = fit_scale(ink, seg_budget(true, 1, false, cell));
assert!(crowded < 1., "an occupied neighbour does not");
assert!(
ink * crowded <= cell,
"and the icon has to end inside its own cell"
);
}
#[test]
fn build_font_disables_ligatures_unless_features_are_configured() {
let font = build_font(&gpui::font("Test"), false, false);