Skip to content

Commit ce4ecbe

Browse files
committed
test(engine): ignored repro for split-window :extend fill loss
Same shape as the TUI probe overlay_extend_face_paints_the_rest_of_the_line, at the engine level: a 360px frame split into two 180px windows, an :extend face text property covering the action line and its newline [8, 27). The single-window twin of this test (line_break_extend_fill_reaches_tty_reserved _right_column) passes, so the split is the trigger. Evidence collected while bisecting: - the body walk PLANS the fill (EXTEND-PLAN pen_x=144 right_edge=180) and the ResolvedLineEndPlan mutation APPLIES it (the action row goes 18 -> 20 glyphs, stretch included) for the left window's pass; - the final matrix for that window instead holds uniformly padded rows: every enabled Text row (empty ones included) has ncols - 1 = 21 glyphs and no stretch, while the right window and the minibuffer pass keep their compact rows (14 / 8/20/20 glyphs); - no Text row passes through replace_row / install_finalized_row, so the padding happens after the walk inside the output-grid install path. The test is #[ignore]d with that reason; run it with `cargo nextest run -p neomacs-layout-engine --run-ignored ignored-only` while fixing the matrix install for the selected window of a multi-window frame. All 50 existing extend tests still pass.
1 parent 1fa74b5 commit ce4ecbe

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

‎crates/neomacs-layout-engine/src/engine_test.rs‎

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7582,6 +7582,19 @@ fn line_break_extend_fill_reaches_tty_reserved_right_column() {
75827582
.last_frame_display_state
75837583
.as_ref()
75847584
.expect("display state");
7585+
for entry in &state.window_matrices {
7586+
let text_rows: Vec<usize> = entry
7587+
.matrix
7588+
.rows
7589+
.iter()
7590+
.filter(|row| row.enabled && row.role == GlyphRowRole::Text)
7591+
.map(|row| row.glyphs[GlyphArea::Text.index()].len())
7592+
.collect();
7593+
eprintln!(
7594+
"MATRIX-SINGLE window={:?} ncols={} text_row_glyph_counts={:?}",
7595+
entry.window_id, entry.matrix.ncols, text_rows
7596+
);
7597+
}
75857598
let entry = state
75867599
.window_matrices
75877600
.iter()
@@ -7619,6 +7632,117 @@ fn line_break_extend_fill_reaches_tty_reserved_right_column() {
76197632
);
76207633
}
76217634

7635+
/// The TUI probe `overlay_extend_face_paints_the_rest_of_the_line` fails in
7636+
/// a side-by-side split while single-window layouts fill correctly. Same
7637+
/// shape here at the engine level: an `:extend` face covering a line AND its
7638+
/// newline must produce the stretch fill out to the LEFT window's edge even
7639+
/// though another window shares the frame.
7640+
///
7641+
/// IGNORED: the body walk plans and applies the fill (verified: the
7642+
/// `ResolvedLineEndPlan` mutation takes the action row 18 -> 20 glyphs), but
7643+
/// the FINAL matrix for the split window holds uniformly padded rows
7644+
/// (every enabled Text row, empty ones included, has `ncols - 1` glyphs) and
7645+
/// no stretch glyph. The same buffer/face in a full-width window keeps the
7646+
/// 3-glyph row with its stretch, so something materializes the split window's
7647+
/// rows to the text width after the body walk -- likely in the
7648+
/// output-grid/retained-matrix install path for the selected window of a
7649+
/// multi-window frame.
7650+
#[test]
7651+
#[ignore = "layout engine: split-window rows are padded to ncols-1, dropping the :extend stretch"]
7652+
fn split_window_line_break_extend_fill_reaches_right_column() {
7653+
let mut eval = Context::new();
7654+
convert_current_buffer_text_backend(&mut eval, BufferTextBackendKind::GapBuffer);
7655+
let buf_id = eval
7656+
.buffer_manager()
7657+
.current_buffer()
7658+
.expect("current buffer")
7659+
.id();
7660+
{
7661+
insert_fragmented_current_buffer_text(
7662+
&mut eval,
7663+
"Actions\n[f1] Pydoc Module\n[f2] Second action\n",
7664+
);
7665+
let buffer = eval.buffer_manager_mut().get_mut(buf_id).expect("buffer");
7666+
// "Actions\n" is 8 chars; the action line + newline spans [8, 27).
7667+
assert!(buffer.put_text_property(8, 27, Value::symbol("face"), extend_face_value()));
7668+
}
7669+
let frame_id = eval
7670+
.frame_manager_mut()
7671+
.create_frame("split-extend-fill", 360, 180, buf_id);
7672+
let left_window = eval
7673+
.frame_manager()
7674+
.get(frame_id)
7675+
.expect("frame")
7676+
.selected_window;
7677+
let candidates_buf = eval.buffer_manager_mut().create_buffer(" *candidates*");
7678+
{
7679+
let buffer = eval
7680+
.buffer_manager_mut()
7681+
.get_mut(candidates_buf)
7682+
.expect("candidates buffer");
7683+
buffer.insert("deploymentkit\n");
7684+
}
7685+
let right_window = eval
7686+
.frame_manager_mut()
7687+
.split_window(
7688+
frame_id,
7689+
left_window,
7690+
neovm_core::window::SplitDirection::Horizontal,
7691+
candidates_buf,
7692+
None,
7693+
neovm_core::window::SplitPlacement::AfterTarget,
7694+
)
7695+
.expect("split window beside the action buffer");
7696+
7697+
let mut engine = LayoutEngine::new();
7698+
engine.layout_frame_rust(&mut eval, frame_id);
7699+
let state = engine
7700+
.last_frame_display_state
7701+
.as_ref()
7702+
.expect("display state");
7703+
let entry = state
7704+
.window_matrices
7705+
.iter()
7706+
.find(|entry| entry.window_id.get() == left_window.0 as i64)
7707+
.expect("left window matrix");
7708+
let row = entry
7709+
.matrix
7710+
.rows
7711+
.iter()
7712+
.find(|row| {
7713+
row.enabled
7714+
&& row.role == GlyphRowRole::Text
7715+
&& matches!(
7716+
row.glyphs[GlyphArea::Text.index()]
7717+
.first()
7718+
.map(|glyph| &glyph.glyph_type),
7719+
Some(GlyphType::Char { ch: '[' })
7720+
)
7721+
})
7722+
.expect("action line row in the left window");
7723+
let text_glyphs = &row.glyphs[GlyphArea::Text.index()];
7724+
let stretch_cols = text_glyphs
7725+
.iter()
7726+
.rev()
7727+
.find_map(|glyph| match glyph.glyph_type {
7728+
GlyphType::Stretch { width_cols } => Some(width_cols),
7729+
_ => None,
7730+
});
7731+
let right_cols = entry.matrix.ncols - 1;
7732+
assert!(
7733+
stretch_cols.is_some_and(|cols| usize::from(cols) >= right_cols - 20),
7734+
"the :extend fill must reach the left window's right edge (ncols={}, \
7735+
row glyphs={:?}); got stretch {:?} (right window {:?})",
7736+
entry.matrix.ncols,
7737+
text_glyphs
7738+
.iter()
7739+
.map(|glyph| format!("{:?}", glyph.glyph_type))
7740+
.collect::<Vec<_>>(),
7741+
stretch_cols,
7742+
right_window,
7743+
);
7744+
}
7745+
76227746
#[test]
76237747
fn overlay_string_extend_fill_reaches_tty_reserved_right_column() {
76247748
let mut eval = Context::new();

0 commit comments

Comments
 (0)