Expand description
lattice-plugin-loader — the editor-side plugin loader (Phase 8).
Phase 7 shipped the plugin runtime (lattice_plugin_host): the wasmtime
engine, the WIT API package, the capability/fuel/crash model, and every
extension seam, each exercised end-to-end by guest fixtures. That crate is
deliberately substrate-neutral — it owns “engine + seams” and knows
nothing about the editor: no XDG discovery, no ex-commands, no native
registries. A headless test harness or a future non-editor host can drive it
unchanged.
This crate is the subsystem that composes the runtime with the editor’s
native registries. It discovers plugins on disk (discovery), loads
them (compile → spawn each declared seam → drain the contribution into its native registry), owns the loaded-plugin state past boot as a service, and
(PL8.C) will expose the user-facing load/unload/reload surface.
§Where this sits (the loader-home decision)
Three homes were weighed (slice plan, “where the loader lives”): inline in
lattice-host, folded into lattice-plugin-host, or a dedicated crate. This
is the dedicated crate — the genuinely-better long-term fit (heuristic #1):
the runtime crate stays “engine + seams”; inlining in the host would grow
Editor:: methods + a host dispatch arm (the half-migration the
mode-ownership acid test forbids). The loader reaches the native registries
through the same SubsystemBoot seam every
other subsystem installs through, so wiring it into the editor is one line
([install]) and zero host internals.
§Status (PL8.B — picker / config / events / grammar / modes / completion)
On-disk discovery + six seam→registry drains are live: a plugin dropped in
<data>/lattice/plugins/ loads at boot and its contribution is reachable.
- picker RCU-registers its source into the
PickerRegistryHandle; - config registers its typed options into the live
ConfigRegistry; - events subscribes its handlers on the
EventBus; - grammar registers its motions / operators / text-objects / ex-commands
into the runtime-mutable
CommandRegistryHandle(B3a/B3b) — the sync-trampoline seam, so the dispatcher fires it on keystroke off a wait-free.load()snapshot with no actor task; - modes registers its minor modes into the runtime-mutable
ModeRegistryHandle(B2), each mode’s keymap binding landing in its own gatedMinorModelayer on theKeymapHandle— declarative data, so the guestStoredrops after registration (no task, nothing to keep alive); - completion wraps its
WasmCompletionSourceas a native asyncCompletionSourceContributioncarried by a loader-owned universal [PluginCompletionMode], so the aggregator reads it throughMode::completion_sources()like any LSP / snippet source (option A — completion is mode-attached; the asyncgenerateruns on a spawned actor, off the keystroke path).
Each records provenance for :list-plugins via the [PluginMetaSink] seam.
That closes the PL8.B seam drains. PL8.C adds the user-facing lifecycle:
the loader self-registers :plugin-load / :plugin-unload / :plugin-reload
into the runtime-mutable command registry (register_ex_commands,
option A — zero host code), and unload reverses every
registry contribution via PluginTeardown. Decoration caching is the
separate hot-path slice PL8.E; init.rs-as-WASM is PL8.D.
Design: docs/dev/architecture/plugin-host.md,
docs/dev/architecture/boot-composition.md. Slice plan:
docs/dev/operations/slice-plans/plugin-loader.md.
Re-exports§
pub use build::BuildOutcome;pub use build::CargoComponentBuilder;pub use build::ComponentBuilder;pub use build::Stamp;pub use build::artifact_path;pub use build::build_plugin;pub use build::source_stamp;pub use discovery::DiscoveredPlugin;pub use discovery::default_core_plugins_dir;pub use discovery::default_init_dir;pub use discovery::default_plugins_dir;pub use discovery::default_source_cache_dir;pub use discovery::discover;pub use discovery::discover_one;pub use events::LanguagesRegistered;pub use install::autoload_enabled;pub use install::disable_autoload;pub use install::enable_autoload;pub use install::flush_plugin_stores;pub use install::install;pub use pipeline::Install;pub use pipeline::RequiredSpec;pub use pipeline::install_all;pub use pipeline::install_required;pub use pipeline::to_required_spec;pub use resolve::Fetcher;pub use resolve::GitRunner;pub use resolve::HttpFetcher;pub use resolve::PluginSource;pub use resolve::Resolved;pub use resolve::SystemGit;pub use resolve::git_cache_dir;pub use resolve::resolve;pub use source_record::SourceRecord;
Modules§
- build
- PM.5: the build service — a plugin source directory becomes a cached
.wasmcomponent. - discovery
- On-disk plugin discovery (PL8.B).
- events
- LA.1 — typed events the loader publishes about the mode/language catalog, as distinct from the plugin lifecycle.
- install
- PL8.A/B — the crate-owned
install(boot)entry point. - pipeline
- PM.7: the resolve → build → stage pipeline behind a
require. - resolve
- PM.6: the source resolver — a declared source becomes something the build service can consume.
- source_
record - PM.8a: the
.sourcemarker — where an installed plugin came from, remembered on disk beside its artifact. - watch
- PL8.D.4 — auto-reload the user’s
init.rsconfig when its compiled artifact changes on disk.
Structs§
- Bulk
Report - The outcome of a bulk operation, one leg per plugin.
- Failed
Load - WT.4: a plugin the loader tried to load and could not.
- Loader
Services - The editor-side runtime environment the loader drives seams against —
captured once from the boot context in [
install], or built directly by a headless harness / test. Every handle isOptionbecause a given consumer wires only the seams it exercises; a seam drain with an absent handle is a logged skip, never a panic. Grows one field per seam without churning thePluginLoader::with_servicessignature. - Plugin
Loader - The plugin loader subsystem: owns the runtime, the loaded-plugin set, and the
discovery + load orchestration. Stood up at boot by [
install], which captures the editor environment and registers the loader as aPluginLoaderHandleservice so the user surface reaches it generically. - Plugin
Status - A read-only snapshot of one loaded plugin for the manager view. Cloned out of the loader’s loaded-set under its lock (never a live borrow), so the view renders a stable frame while loads/unloads proceed.
- Reload
Config Report - The detailed outcome of a
reload_config— enough for the user to see what happened and, on a build failure, the compiler error. - Wired
Seams - Which drain-required services the loader captured at [
install] time — reported byPluginLoader::wired_seams. Every flag must betrueafter a real editor boot; afalseis a boot-ordering regression (the loader was installed before that service registered) that silently degrades the dependent seam’s drain to aNotWiredskip.
Enums§
- Build
State - PM.8a: how current a plugin’s built artifact is.
- BulkLeg
- What a bulk run did to one plugin.
- BulkOp
- Which bulk verb
PluginLoader::spawn_bulkshould run. - Config
Build Status - How the source → artifact build went during
reload_config— a:reload-configor a plugins-view rebuild of theinitrow. Carried inReloadConfigReportso the surface (the*messages*echo, the plugins view row) can say precisely what happened, and on a failure show the compiler diagnostics rather than a bare “it failed”. - Plugin
Health - A loaded plugin’s health — the quarantine/reload surface the manager view
shows. A component trap taints its instance irrecoverably (wasmtime offers no
rollback), so the instance is dead-until-reload;
Event::PluginCrashedfires exactly once per instance on that first trap, flipping health here. - Plugin
Loader Error - Why a plugin failed to load. Every variant is graceful-degradation input for the caller (discovery logs + skips; the editor never aborts boot on one bad plugin) — the load path returns a value, never panics.
Functions§
- error_
chain - An error and every cause behind it, as
outer: inner: innermost.
Type Aliases§
- Plugin
Loader Handle - The service handle other layers reach the loader through — the ex-command
surface (PL8.C), the plugin-manager view (PL8.H). Per the
ServiceRegistryArc/TypeId rule, register and look up with this exact alias (Arc<PluginLoader>), never a barePluginLoader.