Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions crates/neomacs-layout-engine/src/buffer_source/render_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,15 @@ pub(crate) enum WindowPositionPublication {
/// without entering redisplay_window. It publishes neither the temporary
/// start/end nor a window-scroll-functions effect to the live minibuffer.
InactiveEchoArea,
/// A GNU `window-end`/`vertical-motion`-style logical query. It walks from
/// the exact live start and preserves the accepted chrome partition.
SynchronousQueryEnd,
/// A GNU `pos-visible-in-window-p` / `posn-at-point` query. Unlike
/// `window-end`, this path re-evaluates and measures the chrome formats
/// before laying out the body, exactly as `pos_visible_p` does. A measured
/// height that differs from the reserved one triggers the normal
/// `WindowChrome` relayout so the text-area bounds come from the new row.
SynchronousQueryPosition,
}

impl WindowPositionPublication {
Expand All @@ -84,15 +92,27 @@ impl WindowPositionPublication {
pub(crate) const fn uses_exact_window_start(self) -> bool {
matches!(
self,
Self::Snapshot | Self::InactiveEchoArea | Self::SynchronousQueryEnd
Self::Snapshot
| Self::InactiveEchoArea
| Self::SynchronousQueryEnd
| Self::SynchronousQueryPosition
)
}

/// A synchronous GNU `window-end` query walks only the text area using
/// the chrome dimensions accepted by the last redisplay. It must not
/// evaluate mode/header/tab-line Lisp while answering the query.
/// Both query kinds walk the exact live start without publishing redisplay
/// positions. Only Position queries additionally remeasure chrome.
pub(crate) const fn is_synchronous_query(self) -> bool {
matches!(self, Self::SynchronousQueryEnd)
matches!(
self,
Self::SynchronousQueryEnd | Self::SynchronousQueryPosition
)
}

/// Whether the query must evaluate and measure chrome before trusting the
/// text-area partition (GNU `pos_visible_p`, src/xdisp.c:1732-1770).
/// `window-end` deliberately keeps the accepted chrome metrics instead.
pub(crate) const fn remeasures_chrome(self) -> bool {
matches!(self, Self::SynchronousQueryPosition)
}

pub(crate) const fn keeps_complete_minibuffer_measurement_start(self) -> bool {
Expand Down Expand Up @@ -133,7 +153,10 @@ impl WindowPositionPublication {
window_start_lisp,
);
}
Self::Snapshot | Self::InactiveEchoArea | Self::SynchronousQueryEnd => {}
Self::Snapshot
| Self::InactiveEchoArea
| Self::SynchronousQueryEnd
| Self::SynchronousQueryPosition => {}
}
None
}
Expand All @@ -153,6 +176,13 @@ pub(crate) struct BufferSourceRedisplayPublishRequest {
#[derive(Debug)]
pub(crate) enum BufferSourceRenderAttemptOutcome {
Skipped,
/// A position query had no body under the retained partition. Measure
/// chrome anyway so a shrinking row can restore usable body space.
EmptyBodyChromeMeasured {
measured: crate::window_layout::WindowChromeMetrics,
freshness_before_chrome: neovm_core::window::WindowLayoutAttemptFreshness,
chrome_lisp_admission: crate::attempt_freshness::LayoutLispAdmission,
},
/// Lisp evaluated by a buffer-owned display source changed the window's
/// source projection. The frame transaction must discard this attempt and
/// recollect the live window/buffer pair.
Expand Down Expand Up @@ -440,7 +470,8 @@ impl BufferSourceRedisplayPublishRequest {
// discarded attempt.
WindowPositionPublication::Snapshot
| WindowPositionPublication::InactiveEchoArea
| WindowPositionPublication::SynchronousQueryEnd => {}
| WindowPositionPublication::SynchronousQueryEnd
| WindowPositionPublication::SynchronousQueryPosition => {}
}
}

Expand Down
72 changes: 71 additions & 1 deletion crates/neomacs-layout-engine/src/buffer_source/render_plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ pub(crate) struct BufferSourceOutputSetup {
body_install_context: BufferSourceBodyInstallContext,
retry_bounds: BufferSourceRetryBounds,
position_publication: WindowPositionPublication,
/// A Position query evaluates chrome on its first attempt. Its retry runs
/// with the measured metrics already seeded into `WindowParams`, so chrome
/// must not be evaluated a second time (GNU `pos_visible_p` evaluates once
/// before walking the body).
remeasure_chrome: bool,
}

#[cfg(test)]
Expand Down Expand Up @@ -591,6 +596,7 @@ impl BufferSourceOutputSetup {
(text_y + text_height - walk_setup.window_top).round() as i64,
),
position_publication: WindowPositionPublication::Redisplay,
remeasure_chrome: false,
}
}

Expand All @@ -601,6 +607,57 @@ impl BufferSourceOutputSetup {
self.position_publication = publication;
self
}

pub(crate) fn with_remeasure_chrome(mut self, remeasure: bool) -> Self {
self.remeasure_chrome = remeasure;
self
}

/// A retained chrome partition can consume the whole body after a resize.
/// Its current format must still get a chance to shrink before we skip it.
pub(crate) fn measure_chrome_for_empty_body<FS: crate::neovm_bridge::FaceServices>(
self,
state: BufferSourceRenderAttemptContext<'_, '_, FS>,
request: WindowChromeRowsRenderRequest<'_, '_>,
params: &WindowParams,
) -> BufferSourceRenderAttemptOutcome {
let (mut output, font_metrics, face_resolver, mut face_ids, _) = state.into_parts();
if !self.remeasure_chrome
|| !self.position_publication.remeasures_chrome()
|| neovm_core::emacs_core::xdisp::mode_line_display_active(output.evaluator())
{
return BufferSourceRenderAttemptOutcome::Skipped;
}
let freshness_before_chrome = match freshness_before_window_chrome(
output.evaluator(),
self.begin_request.frame_id(),
params,
) {
Ok(freshness) => freshness,
Err(changed) => return changed.into(),
};
let mut emitter = output.begin_text_window_output(self.begin_request);
let (output, evaluator) = output.into_parts();
let services =
ChromeRowRenderServices::new(font_metrics, face_resolver.resolver(), &mut face_ids);
match render_or_retain_window_chrome(
output,
&mut emitter,
evaluator,
request,
services,
WindowChromeRowSource::Recompute,
) {
Ok((measured, chrome_lisp_admission)) => {
BufferSourceRenderAttemptOutcome::EmptyBodyChromeMeasured {
measured,
freshness_before_chrome,
chrome_lisp_admission,
}
}
Err(changed) => changed.into(),
}
}
}

impl BufferSourceDefaultFacePlan {
Expand Down Expand Up @@ -1852,7 +1909,20 @@ impl BufferSourceOutputSetup {
// *measured* chrome heights so the window snapshot reports the real
// (possibly taller, e.g. doom-modeline's bar) height rather than the
// face-only estimate the text-area geometry was reserved from.
let chrome_source = if self.position_publication.is_synchronous_query() {
// A chrome `:eval` may itself ask for a position (GNU guards that
// re-entry, src/xdisp.c:1725-1731). Keep the accepted partition while
// a chrome formatter is already running instead of recursing.
let chrome_source = if self.remeasure_chrome
&& self.position_publication.remeasures_chrome()
&& !neovm_core::emacs_core::xdisp::mode_line_display_active(evaluator)
{
// GNU `pos_visible_p` re-evaluates and measures mode/header/tab
// lines on every call, before walking the body (src/xdisp.c:1732-
// 1770). Measure here too: a mismatch with the reserved metrics
// becomes `WindowLayoutOutcome::NeedsRelayout`, and the retry lays
// the body out under the measured text-area partition.
WindowChromeRowSource::Recompute
} else if self.position_publication.is_synchronous_query() {
// GNU `Fwindow_end` builds a stack-local display iterator for the
// text area. Reuse the already accepted partition instead of
// evaluating status-line Lisp as a side effect of a geometry
Expand Down
32 changes: 30 additions & 2 deletions crates/neomacs-layout-engine/src/buffer_source/window_render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ where
buffer_name: &'a str,
reserve_right_border_col: bool,
position_publication: WindowPositionPublication,
/// Evaluate and measure chrome on this attempt. Set for a Position query's
/// first pass; its measured-metric retry clears it.
remeasure_chrome: bool,
resolved_window_start: ResolvedWindowStart,
forward_viewport_measurement: Option<ForwardViewportMeasurement>,
}
Expand Down Expand Up @@ -164,6 +167,7 @@ where
buffer_name,
reserve_right_border_col,
position_publication: WindowPositionPublication::Redisplay,
remeasure_chrome: false,
resolved_window_start,
forward_viewport_measurement: None,
}
Expand All @@ -177,6 +181,11 @@ where
self
}

pub(crate) fn with_remeasure_chrome(mut self, remeasure: bool) -> Self {
self.remeasure_chrome = remeasure;
self
}

pub(crate) fn with_forward_viewport_measurement(
mut self,
measurement: Option<ForwardViewportMeasurement>,
Expand Down Expand Up @@ -249,6 +258,7 @@ where
buffer_name: self.buffer_name,
reserve_right_border_col: self.reserve_right_border_col,
position_publication: self.position_publication,
remeasure_chrome: self.remeasure_chrome,
resolved_window_start: self.resolved_window_start,
forward_viewport_measurement: self.forward_viewport_measurement.clone(),
};
Expand Down Expand Up @@ -307,6 +317,7 @@ where
buffer_name: self.buffer_name,
reserve_right_border_col: self.reserve_right_border_col,
position_publication: self.position_publication,
remeasure_chrome: self.remeasure_chrome,
resolved_window_start: self.resolved_window_start,
forward_viewport_measurement: self.forward_viewport_measurement.clone(),
};
Expand Down Expand Up @@ -411,6 +422,7 @@ where
buffer_name,
reserve_right_border_col,
position_publication,
remeasure_chrome,
resolved_window_start,
forward_viewport_measurement,
} = self;
Expand Down Expand Up @@ -587,7 +599,7 @@ where
bytes_read
);

if geometry.text_height <= 0.0 || geometry.text_width <= 0.0 {
if geometry.text_width <= 0.0 {
return BufferSourceRenderAttemptOutcome::Skipped;
}

Expand Down Expand Up @@ -630,7 +642,23 @@ where
geometry.max_rows,
&walk_setup,
)
.with_position_publication(position_publication);
.with_position_publication(position_publication)
.with_remeasure_chrome(remeasure_chrome);

if geometry.text_height <= 0.0 {
return output_setup.measure_chrome_for_empty_body(
state,
chrome_plan.render_request(
params,
*layout_box,
geometry.mode_line_display_row,
reserve_right_border_col,
window_metrics,
buffer_name,
),
params,
);
}

output_setup.render_body_attempt(
&mut walk_setup,
Expand Down
Loading