pub struct ModeRegistry { /* private fields */ }Expand description
Mode registry. Owns the catalogue of registered modes
(Arc<dyn DynMode>) and drives activation / deactivation.
H.2 (2026-05-31): kind_index maps each [BufferKind] to the
major mode that declared target_buffer_kind() == Some(kind).
Populated at register-time; first registration wins (subsequent
claims log a warning rather than failing, so foundation
registration order and feature-crate registration order can
interleave deterministically).
Registration (&mut self) happens at boot and on plugin load, through
the copy-on-write ModeRegistryHandle; every other method is &self
and is read from the editor actor. The activation methods do not own
per-buffer state: the caller passes the buffer’s ActiveModes and the
editor-wide GuardStoreHandle, config, event bus and services.
§Examples
use lattice_core::BufferKind;
use lattice_mode::{
register_foundation_modes, LifecycleFuture, MessagesMode, Mode, ModeContext, ModeId,
ModeKind, ModeRegistry, RegistrationError, TextMode,
};
let mut registry = ModeRegistry::new();
register_foundation_modes(&mut registry);
assert!(registry.is_registered(TextMode::mode_id()));
// Kind-bound majors are found by kind, without a host-side `match`.
assert_eq!(
registry.find_major_for_kind(BufferKind::Messages),
Some(MessagesMode::mode_id()),
);
// The image major PRESENTS its files rather than loading them as text.
assert_eq!(
registry.presenting_major_for_path(std::path::Path::new("diagram.PNG")),
Some(ModeId::new("image-mode")),
);
struct Bare(&'static str);
impl Mode for Bare {
type Guard = ();
fn id(&self) -> ModeId { ModeId::new(self.0) }
fn kind(&self) -> ModeKind { ModeKind::Minor }
fn on_activate(&self, _ctx: ModeContext) -> LifecycleFuture<'_, ()> {
Box::pin(async { Ok(()) })
}
}
// The `-mode` suffix is enforced, and ids are unique.
assert!(matches!(registry.register(Bare("zen")), Err(RegistrationError::MissingModeSuffix(_))));
let id = registry.register(Bare("zen-mode")).unwrap();
assert!(matches!(registry.register(Bare("zen-mode")), Err(RegistrationError::Duplicate(_))));
assert!(registry.is_minor_enabled(&id)); // native registration enables it
assert!(registry.unregister(id));Implementations§
Source§impl ModeRegistry
impl ModeRegistry
Sourcepub fn new() -> Self
pub fn new() -> Self
An empty registry. The host then calls
register_foundation_modes and
each feature crate’s register_<x>_modes.
Sourcepub fn register<M: Mode>(
&mut self,
mode: M,
) -> Result<ModeId, RegistrationError>
pub fn register<M: Mode>( &mut self, mode: M, ) -> Result<ModeId, RegistrationError>
Register a mode. Same id twice is a Duplicate error.
H.2: if mode.target_buffer_kind() is Some(kind) and no
major has claimed kind yet, the mode is recorded as the
default major for that kind (queryable via
Self::find_major_for_kind). Subsequent claims for the
same kind log a tracing::warn! and leave the existing
binding in place — clobbering is treated as a developer bug,
not a hot-swap mechanism.
Sourcepub fn register_available<M: Mode>(
&mut self,
mode: M,
) -> Result<ModeId, RegistrationError>
pub fn register_available<M: Mode>( &mut self, mode: M, ) -> Result<ModeId, RegistrationError>
Register a mode WITHOUT enabling it (CI.3) — the plugin path. The mode is
available (in the registry, its keymap layer exists, :describe-mode
and :ls-style introspection see it) but does NOT auto-activate until the
user enables it (enable-mode / an init.rs on-plugin-loaded handler).
Used by register_plugin_mode; native modes use register
(auto-enabled). Declaration ≠ enablement (config-and-init.md §6).
Sourcepub fn is_registered(&self, id: ModeId) -> bool
pub fn is_registered(&self, id: ModeId) -> bool
True iff this id is registered (any kind).
Sourcepub fn set_minor_enabled(&mut self, id: ModeId, enabled: bool)
pub fn set_minor_enabled(&mut self, id: ModeId, enabled: bool)
Enable or disable a registered minor mode globally (CI.4). Enabling makes
it eligible for auto-activation per its ActivationPolicy
(auto_activatable_minors); disabling
removes it from the enabled set. A no-op for an unregistered id (the
caller logs). The host re-activates open buffers after enabling
(config-and-init.md §6); the registry only holds the flag.
Sourcepub fn is_minor_enabled(&self, id: &ModeId) -> bool
pub fn is_minor_enabled(&self, id: &ModeId) -> bool
True iff this minor mode is enabled (CI.3) — the auto-activation gate.
Sourcepub fn unregister(&mut self, id: ModeId) -> bool
pub fn unregister(&mut self, id: ModeId) -> bool
Remove a registered mode, the teardown seam for a plugin reload / unload
(PH7.12b). The registry is otherwise register-only, so without this a
plugin reload would hit RegistrationError::Duplicate on the second
register-mode, and the modes map would grow across reloads. The
caller drives this with the ModeIds spawn_mode_plugin returned, so
removal is by id (the registry keeps no plugin-id provenance — the host
owns the id↔plugin mapping in its teardown bundle). Also drops any
kind_index entry pointing at this mode so a re-register can re-claim
the kind. Returns true if the mode was present (idempotent no-op on a
second unload).
Sourcepub fn find_major_for_kind(&self, kind: BufferKind) -> Option<ModeId>
pub fn find_major_for_kind(&self, kind: BufferKind) -> Option<ModeId>
Look up the major mode declared as the default for a
given [BufferKind] (via Mode::target_buffer_kind).
Returns None for kinds with no declared major
(e.g. [BufferKind::Document], which dispatches through
language detection rather than a kind-bound major) (H.2).
Index built at register-time, so lookup is HashMap-cheap.
Sourcepub fn find_major_for_lang(&self, lang: &str) -> Option<ModeId>
pub fn find_major_for_lang(&self, lang: &str) -> Option<ModeId>
Look up the major mode declared as the default for a
language, by canonical name (Lang::name() — "rust",
"org"). The peer of find_major_for_kind
for [BufferKind::Document], which dispatches by language
rather than by kind (OM.1).
Returns None for a language no registered major claims —
including every built-in language today, since the built-in
majors still resolve through
lattice_syntax::major_mode_id_for_lang’s table and the host
consults that first. The index exists so a plugin
language, which can have no arm in a hand-written table, gets
a major at all.
Index built at register-time, so lookup is HashMap-cheap.
Sourcepub fn presenting_major_for_path(&self, path: &Path) -> Option<ModeId>
pub fn presenting_major_for_path(&self, path: &Path) -> Option<ModeId>
The major that PRESENTS path’s file type without loading it as text,
if one claims that extension.
The open path asks this BEFORE reading: a match means the bytes are never read into a rope, which for a PNG is the difference between a picture and a UTF-8 error.
Sourcepub fn iter_meta(&self) -> impl Iterator<Item = (ModeId, ModeKind)> + '_
pub fn iter_meta(&self) -> impl Iterator<Item = (ModeId, ModeKind)> + '_
Iterate every registered mode’s (id, kind).
Sourcepub fn auto_activatable_minors(
&self,
major: &str,
buffer_kind: BufferKind,
) -> Vec<ModeId>
pub fn auto_activatable_minors( &self, major: &str, buffer_kind: BufferKind, ) -> Vec<ModeId>
MA.1/MA.2: the minor modes whose declared
ActivationPolicy auto-activates
when a buffer of kind buffer_kind enters the major mode named
major. This is the core of the (B) host resolver
(mode-architecture.md §7.4): the host subscribes once to
[lattice_protocol::Event::MajorEntered] and activates each
minor this returns. O(registered minors) on a rare event
(buffer open / major switch), never per-keystroke.
buffer_kind gates Global minors to real document buffers
(see ActivationPolicy::admits).
Reads each minor’s declared default policy. The config fold
(<mode>.activation) is layered by the host before this is
consulted (SN.3); this method does not see config.
Order is HashMap-undefined; callers that need determinism
sort the result.
Sourcepub fn iter(&self) -> impl Iterator<Item = (ModeId, Arc<dyn DynMode>)> + '_
pub fn iter(&self) -> impl Iterator<Item = (ModeId, Arc<dyn DynMode>)> + '_
Iterate every registered mode as (id, Arc<dyn DynMode>).
K.2.4: the keymap-substrate translation pass walks the
registry to call Mode::keymap() on each mode and merge
the contributed bindings into the host’s KeymapHandle.
iter_meta is enough when only (id, kind) matters;
this is the variant that hands back the live mode trait
object so consumers can dispatch trait methods.
Order is HashMap-undefined; callers that care about
determinism sort the iterator themselves.
Sourcepub fn activate_major(
&self,
active: &mut ActiveModes,
guards: &GuardStoreHandle,
config: &Arc<ConfigRegistry>,
events: &Arc<EventBus>,
services: &Arc<ServiceRegistry>,
buffer: BufferId,
mode: ModeId,
caps: CapabilitySet,
) -> Result<(), ModeActivationError>
pub fn activate_major( &self, active: &mut ActiveModes, guards: &GuardStoreHandle, config: &Arc<ConfigRegistry>, events: &Arc<EventBus>, services: &Arc<ServiceRegistry>, buffer: BufferId, mode: ModeId, caps: CapabilitySet, ) -> Result<(), ModeActivationError>
Activate a major mode on buffer. Synchronous prefix:
validate the major + its implies() tree, mutate
active_modes for the whole tree, bump epochs + build a
cascade plan. Then one task is spawned that walks the
plan in DFS order, awaiting each step’s
on_activate.await before the next.
If a different major is currently active, it is
deactivated synchronously first (Drop runs, MajorExiting
publishes). Idempotent: reactivating the current major
triggers a reload (deactivate then re-activate).
Sourcepub fn activate_minor(
&self,
active: &mut ActiveModes,
guards: &GuardStoreHandle,
config: &Arc<ConfigRegistry>,
events: &Arc<EventBus>,
services: &Arc<ServiceRegistry>,
buffer: BufferId,
mode: ModeId,
caps: CapabilitySet,
) -> Result<(), ModeActivationError>
pub fn activate_minor( &self, active: &mut ActiveModes, guards: &GuardStoreHandle, config: &Arc<ConfigRegistry>, events: &Arc<EventBus>, services: &Arc<ServiceRegistry>, buffer: BufferId, mode: ModeId, caps: CapabilitySet, ) -> Result<(), ModeActivationError>
Activate a minor mode on buffer. Same sync-prefix-then-
spawn shape as activate_major.
Sourcepub fn deactivate_minor(
&self,
active: &mut ActiveModes,
guards: &GuardStoreHandle,
events: &Arc<EventBus>,
buffer: BufferId,
mode: ModeId,
) -> Result<(), ModeActivationError>
pub fn deactivate_minor( &self, active: &mut ActiveModes, guards: &GuardStoreHandle, events: &Arc<EventBus>, buffer: BufferId, mode: ModeId, ) -> Result<(), ModeActivationError>
Deactivate a minor mode. Synchronous: locks guards,
removes + drops the Guard, publishes
MinorDeactivated. Idempotent.
MinorDeactivated publishes BEFORE the Guard drops so
subscribers can inspect the state about to be torn down.
Sourcepub fn deactivate_major(
&self,
active: &mut ActiveModes,
guards: &GuardStoreHandle,
events: &Arc<EventBus>,
buffer: BufferId,
) -> Result<(), ModeActivationError>
pub fn deactivate_major( &self, active: &mut ActiveModes, guards: &GuardStoreHandle, events: &Arc<EventBus>, buffer: BufferId, ) -> Result<(), ModeActivationError>
Deactivate the active major mode (if any). Synchronous.
Trait Implementations§
Source§impl Clone for ModeRegistry
impl Clone for ModeRegistry
Source§fn clone(&self) -> ModeRegistry
fn clone(&self) -> ModeRegistry
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more