Skip to main content

lattice_host/
pane_render.rs

1//! Mode-keyed pane render resolution (renderer-agnostic algorithm).
2//!
3//! Phase 5.6: the *algorithm* that walks a buffer's active modes
4//! (minors most-recently-activated first, then the major) lives
5//! host-side here; the per-renderer *storage* of provider fn-pointers
6//! stays renderer-specific (TUI uses `ratatui::Frame`-typed fns; a
7//! future GPUI renderer will use its own native shapes). Renderers
8//! implement [`ProviderLookup`] over their own registry and call
9//! [`resolve_pane_render_mode`] to find the `ModeId` whose provider
10//! should drive a pane.
11//!
12//! See `docs/dev/architecture/phase-5-extraction.md` "Hard Case ยง2"
13//! โ†’ "Post-Option-E revision (slice 5.6)". The trait-object plan
14//! from the original draft is superseded โ€” composition made the
15//! registry naturally renderer-specific, so the only shared piece
16//! is the mode-walking lookup.
17
18use lattice_core::BufferId;
19use lattice_mode::ModeId;
20
21use crate::editor::Editor;
22
23/// Renderer-supplied "is this mode registered?" probe. Each renderer
24/// implements this trait on its own typed pane-render registry; the
25/// host walks modes via [`resolve_pane_render_mode`] and asks the
26/// probe whether the candidate `ModeId` has a provider.
27///
28/// Kept deliberately minimal (one boolean method) so the host's
29/// algorithm carries no renderer-specific shape and the renderer's
30/// registry stays free to hold native-typed fn-pointers without
31/// trait-object overhead on the paint hot path.
32pub trait ProviderLookup {
33    /// True when `mode` has a registered pane-render provider.
34    fn has_provider(&self, mode: ModeId) -> bool;
35}
36
37/// Resolve the `ModeId` whose pane-render provider should drive the
38/// pane displaying `buffer_id`. Walks active minors in reverse
39/// activation order (most-recently activated wins โ€” same priority
40/// the option resolver uses) before falling back to the major.
41/// Returns `None` when no active mode has a provider registered, in
42/// which case the renderer uses its default document path.
43///
44/// The probe is `&L` rather than `&impl Fn(ModeId) -> bool` so the
45/// trait acts as a documented seam between host and renderer; the
46/// monomorphisation cost is the same.
47pub fn resolve_pane_render_mode<L: ProviderLookup>(
48    editor: &Editor,
49    buffer_id: BufferId,
50    lookup: &L,
51) -> Option<ModeId> {
52    let modes = editor.active_modes.get(&buffer_id)?;
53    for &minor_id in modes.minors().iter().rev() {
54        if lookup.has_provider(minor_id) {
55            return Some(minor_id);
56        }
57    }
58    modes.major().filter(|id| lookup.has_provider(*id))
59}