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}