Skip to main content

Module events

Module events 

Source
Expand description

In-process event bus (DESIGN.md §5.10).

Vim’s autocmd and emacs’s hooks both desugar to the same primitive: subscribe a sink to a filtered stream of typed events; the bus calls the sink whenever a matching event is published. v1 ships the observation-only baseline; the Before-event veto / mutation seam (§5.10.2) layers on later.

§v1 scope

  • Filters by [lattice_protocol::EventKind] for bucketing, then AND-combines the reserved EventFilter fields (path_glob, major_modes, predicate) at publish time on the candidates the kind bucket already selected (EF.1). Mode activation is the caller that needed them (mode-architecture.md §7.4); the checks are per-candidate constants, so dispatch stays O(subscribers-of-kind) – the filter fields never widen the publish scan.
  • Sinks are SubscriptionTarget::Channel (an mpsc::Sender) or SubscriptionTarget::Invocation (a CommandInvocation the bus runs through the document actor’s dispatch when the App wires that path).
  • Plugin handler target (SubscriptionTarget::Plugin in §5.10) is omitted – WASM hosting isn’t online in v1.
  • Indexed dispatch: subscriptions live in a HashMap<EventKind, Vec<Subscription>>. Publish iterates the bucket for one kind, never the global list.
  • Bus is Send + Sync; the inner state is one Mutex. The publish path takes the lock only to snapshot the matching channel senders (and to queue Invocation targets onto the shared pending_invocations); the actual tx.send calls run with the lock dropped. Two publish calls from different threads can therefore dispatch in parallel, and a future bounded subscriber cannot stall the publisher under the bus mutex (audit M1).

§What’s NOT here

  • BeforeSave / BeforeQuit veto. v1 publishes the event so observers see it; mutating the payload or aborting the transition is out of scope until the actor runs the bus inside its task and respects handler errors.
  • Backpressure. Channel sinks use unbounded mpsc. If a subscriber leaks senders the bus grows. Bounded channels + slow-consumer policy follow when LSP / plugin subscribers can actually generate the volume that needs governance.
  • Per-handler fuel (§5.10.4). Plugin / Invocation handlers will eventually run with a fuel budget; v1 calls them inline or punts to caller for Invocation targets.

Structs§

EventBus
Process-shared event bus. Cheap to construct (one Mutex holding empty maps). Cheap to clone via Arc<EventBus> – callers wrap externally; the type itself is Send + Sync and can be shared by reference.
EventFilter
Filter applied at publish time. kinds buckets the subscription; the remaining fields (EF.1) AND-combine on top – every Some field must match for the event to be delivered, a None field is unconstrained. Mode activation (mode-architecture.md §7.4) is the caller these reserved fields were declared for.
SubscriptionId
Opaque handle returned by EventBus::subscribe. Pass to EventBus::unsubscribe to remove the subscription.

Enums§

SubscriptionTarget
What the bus does when a matching event arrives.

Type Aliases§

EventAck
The completion signal an awaited publish threads through a plugin sink (OA.14d). The bus mints one per matched plugin subscription and EventBus::publish_awaited returns the matching receivers; the plugin’s actor drops it once the guest handler has returned.
EventPredicate
A caller-supplied escape-hatch predicate (the predicate field of EventFilter). Returns true to keep the event.
PluginEventSink
A host-owned sink that delivers a matched [Event] to a plugin’s on-event handler (PH7.8). Returns false when the plugin’s receiver has closed (its actor task ended), so the bus prunes the subscription lazily — the same closed-Channel / ForwardFn discipline the rest of this module uses.