lattice_mode/lib.rs
1//! The mode system's foundation: the `Mode` trait, the mode registry, the
2//! per-buffer set of active modes, and the typed lifecycle events — plus the
3//! generic host seams a mode uses to own its whole surface (action handlers,
4//! services, inbound wakes, buffer creation) and the foundation modes that
5//! have no other owning crate.
6//!
7//! The major / minor mode system is the primary customization mechanism
8//! (DESIGN.md §5.8, `docs/dev/architecture/mode-architecture.md`). A buffer
9//! has exactly one **major** mode (content-type identity: `rust-mode`,
10//! `help-mode`'s markdown major, `messages-mode`) and any number of **minor**
11//! modes layered over it (`line-numbers-mode`, `table-mode`,
12//! `emacs-keys-mode`). A mode contributes declaratively — option overrides,
13//! a keymap layer, completion sources, gutter signs, action handlers — and
14//! imperatively through one lifecycle hook whose returned Guard is the only
15//! cleanup path.
16//!
17//! ## What this crate owns
18//!
19//! - **The contract.** [`Mode`] (and its object-safe adapter [`DynMode`]),
20//! [`ModeId`], [`ModeKind`], [`ActivationPolicy`], [`EditableTail`],
21//! [`CapabilitySet`], [`ModeContext`], [`LifecycleFuture`] and
22//! [`ModeActivationError`].
23//! - **Activation.** [`ModeRegistry`] registers modes and drives activation /
24//! deactivation against a buffer's [`ActiveModes`], stashing each
25//! activation's Guard in a [`GuardStoreHandle`]. Observable transitions
26//! (`MajorEntered` / `MinorActivated` / …) ride the protocol `Event` enum;
27//! internal failures ride [`ModeEvent`].
28//! - **The host seams a mode needs to own its surface without depending on
29//! the host.** [`ServiceRegistry`] (typed services),
30//! [`ActionHandlerRegistry`] (chord bodies), [`ModeActivator`] and
31//! [`BufferStore`] (buffer creation / lookup), [`SubsystemBoot`] (a
32//! subsystem's one-line `install`), [`inbound`] and
33//! [`TickCallbackRegistry`] (off-keystroke results that wake the editor),
34//! [`idle_gate`] (armed deadlines), [`ProviderViewRegistry`] (open a
35//! provider's view), [`ForegroundCancel`], and the producer registries
36//! for plugin-backed content ([`MediaSourceRegistry`],
37//! [`ContextSourceRegistry`], [`GutterDecorationSourceRegistry`],
38//! [`ScannedExcerptSourceRegistry`], [`BufferScopeSourceRegistry`]).
39//! - **Shared render-facing vocabularies** a mode writes without seeing a
40//! renderer: gutter signs ([`SignRegistry`], [`GutterDecoration`]), the
41//! modeline element model ([`ModelineService`],
42//! [`ModelineElementUpdate`]), async highlight / inlay hand-off
43//! ([`PendingSyntheticHighlights`], [`PendingInlays`]), buffer-locals
44//! ([`BufferLocals`]).
45//! - **Foundation modes** ([`modes`], registered by
46//! [`register_foundation_modes`]): `text-mode`, `help-mode`,
47//! `hover-mode`, `messages-mode`, `image-mode`, the completion and display
48//! minors, `table-mode`, `surround-mode`, `which-key-mode`, and the shared
49//! minors that own one chord for a whole class of view
50//! ([`RefreshableViewMode`] `gr`, [`FoldableViewMode`] `<Tab>`,
51//! [`ReplMode`], [`EmacsKeysMode`]). Feature-crate modes live with their
52//! feature (`lattice-lsp`, `lattice-listing`, `lattice-magit`, …).
53//!
54//! ## What it must not depend on, and why
55//!
56//! Nothing above it: not `lattice-host`, no renderer (`lattice-ui-tui`,
57//! `lattice-ui-gpui`), no feature crate. Every feature crate depends on this
58//! one to declare its modes, and the host depends on every feature crate, so
59//! a dependency upward is a cycle — and, more to the point, it is the
60//! structural guarantee that a mode can own its keymap, handler bodies,
61//! buffers and async wakes **without an `Editor::` method or a host `Action`
62//! variant** (the mode-ownership acid test). Where a mode needs the host, the
63//! host implements a trait defined here ([`ModeActivator`], [`BufferStore`],
64//! [`SubsystemBoot`]) or registers a service. Its own dependencies are the
65//! substrate below: protocol, core, grammar, keymap, config, completion,
66//! runtime, cells.
67//!
68//! ## Example: a minimal minor mode
69//!
70//! ```
71//! use lattice_core::BufferKind;
72//! use lattice_mode::{
73//! ActivationPolicy, LifecycleFuture, Mode, ModeContext, ModeId, ModeKind, ModeRegistry,
74//! OptionOverrideSet,
75//! };
76//!
77//! /// Wraps long lines in prose buffers.
78//! struct ProseMode;
79//!
80//! impl Mode for ProseMode {
81//! type Guard = (); // nothing to clean up
82//! fn id(&self) -> ModeId {
83//! ModeId::new("prose-mode")
84//! }
85//! fn kind(&self) -> ModeKind {
86//! ModeKind::Minor
87//! }
88//! fn options(&self) -> OptionOverrideSet {
89//! lattice_config::overrides! { lattice_config::Wrap = true, }
90//! }
91//! fn activation_policy(&self) -> ActivationPolicy {
92//! ActivationPolicy::Majors(vec![ModeId::new("markdown-mode")])
93//! }
94//! fn on_activate(&self, _ctx: ModeContext) -> LifecycleFuture<'_, ()> {
95//! Box::pin(async { Ok(()) })
96//! }
97//! }
98//!
99//! let mut registry = ModeRegistry::new();
100//! let id = registry.register(ProseMode).unwrap();
101//! // The host's minor resolver asks this when a buffer enters a major.
102//! assert_eq!(registry.auto_activatable_minors("markdown-mode", BufferKind::Document), vec![id]);
103//! assert!(registry.auto_activatable_minors("rust-mode", BufferKind::Document).is_empty());
104//! ```
105//!
106//! The [`Mode`] docs carry the full lifecycle (registration → activation →
107//! deactivation) as a runnable example; [`SubsystemBoot`] shows a whole
108//! subsystem install with an off-thread producer.
109//!
110//! ## Design documents
111//!
112//! - `docs/dev/architecture/mode-architecture.md` — the mode model,
113//! activation, Guards, option layering (§5–§9).
114//! - `docs/dev/architecture/boot-composition.md` — `SubsystemBoot`, the
115//! inbound primitive and why the wake lives in the sender (§3).
116//! - `docs/dev/architecture/keymap-architecture.md` — keymap layers.
117//! - `docs/dev/architecture/modeline.md` — the modeline element model.
118//! - `docs/dev/architecture/cancellation.md` — [`ForegroundCancel`].
119
120#![warn(missing_docs)]
121
122// M.10.1 (2026-06-02): action-handler registry — mode-
123// contributed closures per `CommandId`. Required so modes own
124// BOTH chord choice (already done via `keymap()`) AND handler
125// body (this substrate), per `feedback_mode_owns_its_surface`
126// + `mode-architecture.md` §5.3. Host's chord-resolved-action
127// dispatcher consults via `lookup`; mode's `Guard` carries
128// `ActionHandlerRegistration` tokens whose `Drop` unregisters.
129pub mod action_handler_registry;
130pub mod activator;
131pub mod active;
132pub mod binding_mode;
133// OM.A1: the native seam a WASM agenda-row producer implements. Sibling of
134// `media_source` — async, host-driven off the keystroke path, once per file of
135// a project walk. The source declares which extensions it wants offered, which
136// is what keeps a filetype out of the host's walk.
137pub mod buffer_store;
138pub mod capability;
139pub mod context;
140pub mod scanned_excerpt_source;
141// TC.2: the native seam a WASM sticky-context producer implements. Sibling of
142// `decoration_source` — async, host-driven off the render path, result cached.
143pub mod context_source;
144pub mod contributions;
145pub mod decoration_source;
146pub mod media_source;
147pub mod operator_chord;
148// BC.5: `emacs-keys-mode` — a default-on universal builtin minor mode (the
149// `<C-x>` leader tribute). Moved here from `lattice-host`; registered with the
150// foundation modes. The host keeps only the keymap-layer push (config + the
151// live `KeymapHandle`).
152pub mod emacs_keys_mode;
153// CG.2 (2026-08-08): foreground cancellation as a registered service,
154// so a provider can enrol work from any `&self` context (action
155// handlers, event subscriptions) and not just where `&mut Editor` is
156// reachable. See `docs/dev/architecture/cancellation.md`.
157pub mod error;
158pub mod event;
159pub mod foldable_view_mode;
160pub mod foreground_cancel;
161pub mod guards;
162pub mod refreshable_view_mode;
163pub mod repl_mode;
164// Boot-composition BC.1: the generic *inbound* primitive — a channel whose
165// `send` wakes the editor (`async_landed`) and whose items drain per-tick
166// through a handler. Pairs with `tick_callback`; generalizes the I3
167// `ClaudeCodeInboundBus` + LSP's hand-rolled inbound buses. The wake is baked
168// into the sender so it cannot be forgotten (`boot-composition.md` §3).
169pub mod idle_gate;
170pub mod inbound;
171// K.3 (2026-06-07): `KeymapEntry` + `keymap_entry!` live in
172// `lattice-keymap::keymap_entry`. lattice-mode re-exports the MODULE and
173// the `#[macro_export]` macro with a single `pub use` — the name
174// `keymap_entry` resolves in both the type namespace (the module) and the
175// macro namespace, so `lattice_mode::keymap_entry! { … }` AND
176// `lattice_mode::keymap_entry::{KeymapEntry, default_keymap, …}` keep
177// working for callers in `lattice-multibuffer`, `lattice-host`, and
178// `lattice-ui-tui` WITHOUT duplicating the macro body. The macro's
179// `$crate` resolves to `lattice_keymap` regardless of the re-export path
180// (so callers need no direct `lattice-keymap` dep). See
181// `project_keymap_entry_macro_dual_copy` — the former duplicate is gone.
182pub use lattice_keymap::keymap_entry;
183pub mod locals;
184pub mod mode;
185pub mod modeline;
186// MG.2: pending synthetic-buffer highlights service, shared between
187// lattice-host (drain) and lattice-magit (async refresh tasks).
188pub mod modes;
189pub mod pending_inlays;
190pub mod pending_synthetic_highlights;
191pub mod plugin_meta_sink;
192// PV.1 (2026-08-12): the generic provider-view seam — one host primitive
193// for "open the multibuffer view a provider owns", replacing the
194// per-provider `AppEffect` variant + host arm + plugin-boundary arm.
195pub mod provider_view;
196pub mod registry;
197pub mod services;
198// DB.5 (design.md §9.1): the generic `Startup` boot-completion typed event.
199// Declared here (alongside `ModeEvent`) so subsystem `install(&mut boot)`
200// fns can subscribe without a `lattice-host` dependency.
201pub mod startup;
202// IDE-protocol I1.1: the one generic host primitive — a per-tick drain
203// closure registry. Generalizes the host's hardcoded `drain_<x>` methods
204// so a mode owns its channel + drain body (`feedback_mode_owns_its_surface`).
205pub mod tick_callback;
206// Boot-composition BC.3b: the capability surface a subsystem's `install(boot)`
207// wires against. Lives here (below every subsystem crate) so subsystems name
208// the capability, not the host's concrete `BootContext` (which would cycle).
209pub mod subsystem_boot;
210
211pub use crate::action_handler_registry::{
212 ActionContext, ActionHandler, ActionHandlerContribution, ActionHandlerRegistration,
213 ActionHandlerRegistry, ActionHandlerRegistryHandle,
214};
215pub use crate::activator::{ModeActivator, VirtualRowRegistrar};
216pub use crate::active::ActiveModes;
217pub use crate::binding_mode::BindingMode;
218pub use crate::buffer_store::{BufferStore, BufferStoreHandle};
219pub use crate::capability::CapabilitySet;
220pub use crate::context::ModeContext;
221pub use crate::context_source::{
222 AsyncContextSource, ContextFuture, ContextSourceRegistry, ContextSourceRegistryHandle,
223};
224pub use crate::contributions::{
225 BUILTIN_SIGN_COLUMNS,
226 BuiltinSignIds,
227 CompilationSeverityData,
228 DIAGNOSTIC_ERROR_PRIORITY,
229 DIAGNOSTIC_HINT_PRIORITY,
230 DIAGNOSTIC_INFO_PRIORITY,
231 DIAGNOSTIC_WARNING_PRIORITY,
232 DIFF_SIGN_PRIORITY,
233 DecorationCtx,
234 DecorationProvider,
235 DiagnosticGlyphs,
236 GutterDecoration,
237 GutterDiffKind,
238 GutterSeverityLevel,
239 Keymap,
240 KeymapBinding,
241 SIGN_COLUMN_DIFF,
242 SIGN_COLUMN_MARK,
243 SignDefinition,
244 SignId,
245 SignRegistry,
246 SignRegistryHandle,
247 Subscription, // MO.4.c: real RAII type; use in mode Guards
248 register_builtin_signs,
249 winning_sign,
250};
251pub use crate::decoration_source::{
252 AsyncGutterDecorationSource, DecorationEpoch, DecorationEpochHandle, DecorationFuture,
253 GutterDecorationSourceRegistry, GutterDecorationSourceRegistryHandle,
254};
255pub use crate::error::ModeActivationError;
256pub use crate::event::ModeEvent;
257pub use crate::guards::{GuardStore, GuardStoreHandle};
258pub use crate::locals::{
259 BufferLocal, BufferLocals, BufferScopeDir, BufferScopeSource, BufferScopeSourceRegistry,
260 BufferScopeSourceRegistryHandle, LocalDescriptor,
261};
262pub use crate::media_source::{
263 AsyncMediaSource, MediaBlockRequest, MediaFuture, MediaSourceRegistry,
264 MediaSourceRegistryHandle,
265};
266pub use crate::mode::{
267 ActivationPolicy, DynMode, EditableTail, LifecycleFuture, Mode, ModeId, ModeKind,
268};
269pub use crate::modes::{
270 ActiveCompletionSources, BufferWordsMode, CompletionMode, CompletionPopupMode, HelpMode,
271 HoverMode, MessagesMode, PathCompletionMode, TextMode, register_foundation_modes,
272 register_help_mode_actions,
273};
274pub use crate::operator_chord::{OperatorChordWirer, OperatorChordWirerHandle};
275// TB.1: `table-mode` — the shared pipe-table minor. Re-exported beside the
276// other shared minors so boot reaches it by the same path.
277pub use crate::modes::table::mode::{TableMode, register_table_actions, register_table_mode};
278pub use crate::plugin_meta_sink::{PluginMetaSink, PluginMetaSinkHandle};
279pub use crate::provider_view::{
280 ProviderViewOpener, ProviderViewOutcome, ProviderViewRegistry, ProviderViewRegistryHandle,
281};
282pub use crate::scanned_excerpt_source::{
283 ClockSpan, RowAnnotation, ScanBeginFuture, ScanDescribeFuture, ScanFuture, ScanResult,
284 ScannedExcerpt, ScannedExcerptSource, ScannedExcerptSourceRegistry,
285 ScannedExcerptSourceRegistryHandle,
286};
287pub use crate::services::ServiceRegistry;
288pub use crate::startup::Startup;
289pub use crate::subsystem_boot::SubsystemBoot;
290pub use crate::tick_callback::{
291 TickCallback, TickCallbackRegistration, TickCallbackRegistry, TickCallbackRegistryHandle,
292};
293pub use lattice_keymap::KeymapEntry;
294// BC.5: the host pushes the `<C-x>` leader layer (it owns the `KeymapHandle` +
295// config), calling `emacs_keys_layer_bindings`; `EmacsKeysMode::mode_id` keys
296// the layer + the K.1.c per-keystroke gate.
297pub use crate::emacs_keys_mode::{EmacsKeysMode, emacs_keys_layer_bindings};
298pub use crate::repl_mode::{ReplMode, register_repl_mode, register_repl_mode_actions};
299// RV.1: the one place `gr` means "refresh this view" — the chord lives
300// here, each view's mode declares its own `refresh_action()` target.
301pub use crate::refreshable_view_mode::{
302 RefreshableViewMode, VIEW_REFRESH_ACTION, register_refreshable_view_actions,
303 register_refreshable_view_mode,
304};
305// OA.4b: the one place `<Tab>` folds the block at point — the chord lives
306// here, each view's mode declares its own `fold_toggle_action()` target.
307pub use crate::foldable_view_mode::{
308 FOLD_TOGGLE_DEFAULT_ACTION, FoldableViewMode, VIEW_FOLD_CYCLE_ACTION, VIEW_FOLD_TOGGLE_ACTION,
309 register_foldable_view_actions, register_foldable_view_mode,
310};
311// ML.0a: configurable-modeline element model + descriptor registry.
312pub use crate::foreground_cancel::{ForegroundCancel, ForegroundCancelHandle};
313pub use crate::modeline::{
314 ElementContent, ElementId, HoverSpec, Interaction, ModelineElement, ModelineElementUpdate,
315 ModelineKey, ModelineRegistry, ModelineRole, ModelineService, ModelineServiceHandle,
316 ModelineSnapshot, Scope, Span, Zone,
317};
318pub use crate::pending_inlays::{InlayRow, PendingInlays, PendingInlaysHandle};
319pub use crate::pending_synthetic_highlights::{
320 HighlightsOp, PendingSyntheticHighlights, PendingSyntheticHighlightsHandle,
321};
322// M.4 dep-inversion: layer-input types live in `lattice-config`
323// now. Re-exported here for compatibility -- callers that
324// imported from `lattice_mode` keep working.
325pub use crate::registry::{ModeRegistry, ModeRegistryHandle, RegistrationError};
326pub use lattice_config::{OptionOverride, OptionOverrideSet, OverridePriority};