Skip to main content

ModeRegistry

Struct ModeRegistry 

Source
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

Source

pub fn new() -> Self

An empty registry. The host then calls register_foundation_modes and each feature crate’s register_<x>_modes.

Source

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.

Source

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).

Source

pub fn is_registered(&self, id: ModeId) -> bool

True iff this id is registered (any kind).

Source

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.

Source

pub fn is_minor_enabled(&self, id: &ModeId) -> bool

True iff this minor mode is enabled (CI.3) — the auto-activation gate.

Source

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).

Source

pub fn get(&self, id: ModeId) -> Option<Arc<dyn DynMode>>

Look up a registered mode by id.

Source

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.

Source

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.

Source

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.

Source

pub fn iter_meta(&self) -> impl Iterator<Item = (ModeId, ModeKind)> + '_

Iterate every registered mode’s (id, kind).

Source

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.

Source

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.

Source

pub fn len(&self) -> usize

Number of registered modes, majors and minors, enabled or not.

Source

pub fn is_empty(&self) -> bool

True when no mode is registered.

Source

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).

Source

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.

Source

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.

Source

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

Source§

fn clone(&self) -> ModeRegistry

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ModeRegistry

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ModeRegistry

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more