Skip to main content

Crate lattice_plugin_loader

Crate lattice_plugin_loader 

Source
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 gated MinorMode layer on the KeymapHandle — declarative data, so the guest Store drops after registration (no task, nothing to keep alive);
  • completion wraps its WasmCompletionSource as a native async CompletionSourceContribution carried by a loader-owned universal [PluginCompletionMode], so the aggregator reads it through Mode::completion_sources() like any LSP / snippet source (option A — completion is mode-attached; the async generate runs 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 .wasm component.
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 .source marker — where an installed plugin came from, remembered on disk beside its artifact.
watch
PL8.D.4 — auto-reload the user’s init.rs config when its compiled artifact changes on disk.

Structs§

BulkReport
The outcome of a bulk operation, one leg per plugin.
FailedLoad
WT.4: a plugin the loader tried to load and could not.
LoaderServices
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 is Option because 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 the PluginLoader::with_services signature.
PluginLoader
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 a PluginLoaderHandle service so the user surface reaches it generically.
PluginStatus
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.
ReloadConfigReport
The detailed outcome of a reload_config — enough for the user to see what happened and, on a build failure, the compiler error.
WiredSeams
Which drain-required services the loader captured at [install] time — reported by PluginLoader::wired_seams. Every flag must be true after a real editor boot; a false is a boot-ordering regression (the loader was installed before that service registered) that silently degrades the dependent seam’s drain to a NotWired skip.

Enums§

BuildState
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_bulk should run.
ConfigBuildStatus
How the source → artifact build went during reload_config — a :reload-config or a plugins-view rebuild of the init row. Carried in ReloadConfigReport so 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”.
PluginHealth
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::PluginCrashed fires exactly once per instance on that first trap, flipping health here.
PluginLoaderError
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§

PluginLoaderHandle
The service handle other layers reach the loader through — the ex-command surface (PL8.C), the plugin-manager view (PL8.H). Per the ServiceRegistry Arc/TypeId rule, register and look up with this exact alias (Arc<PluginLoader>), never a bare PluginLoader.