Skip to main content

lattice_mode/
operator_chord.rs

1//! Wiring a plugin-registered operator's chord into the grammar (CM.2).
2//!
3//! An operator is only half a contribution. `register-operator` puts the spec
4//! and its `apply` in the command registry; what makes it *reachable* is the
5//! operator-pending composition the host builds around it — motion targets,
6//! the doubled linewise form, `i_` / `a_` text-object pendings, and the
7//! `f` / `F` / `t` / `T` find-char pendings.
8//!
9//! That composition needs host-resolved builtin ids, so it lives in
10//! `lattice-host` (`keymap_normal::register_operator_bindings`, `pub` since
11//! N.1.3 for precisely this split — narrow's `zn` is a native provider doing
12//! the same thing). The plugin loader cannot call it: `lattice-plugin-loader`
13//! does not depend on `lattice-host`, and should not.
14//!
15//! So the host publishes this trait as a service and the loader calls it, the
16//! same shape every other cross-crate seam here uses. **An absent handle is a
17//! `NotWired` load failure, not a silent skip** — a plugin whose operator
18//! registered correctly and has no keys is indistinguishable from one that
19//! never loaded, which is the failure mode this crate keeps growing rules
20//! about.
21
22use std::sync::Arc;
23
24/// Wires a plugin operator's chord into the universal operator-pending layer.
25pub trait OperatorChordWirer: Send + Sync {
26    /// Bind `chord` to `op`, with the full operator-pending surface.
27    ///
28    /// `doubled` is the TRAILING key of the linewise form (`c` for `gcc`),
29    /// not the whole chord; `None` binds no doubled form.
30    ///
31    /// `mode` scopes the bindings to that minor mode's keymap layer rather
32    /// than `Builtin`. A plugin's chord must not outlive the plugin: bound at
33    /// `Builtin`, `gc` would survive `:set comment.enabled=false` pointing at
34    /// a handler that is gone.
35    fn wire(
36        &self,
37        op: lattice_grammar::registry::OperatorId,
38        chord: &str,
39        doubled: Option<char>,
40        mode: crate::ModeId,
41        // CM.4: the contributing plugin, so the bindings are stamped
42        // `SourceLayer::Plugin(id)`. Without it `:describe-key gc` names the
43        // host file that happened to create the binding, which is a
44        // provenance lie about a plugin's own chord.
45        plugin_id: u32,
46        // CM.4: the manifest name, so the stamp reads `plugin:comment` rather
47        // than `plugin:1` — a number the reader then has to resolve by hand.
48        plugin_name: &str,
49        post_motion_char: bool,
50    ) -> Result<(), String>;
51}
52
53/// The shared handle. Registered by the host under THIS alias, and looked up
54/// under it — see the `ServiceRegistry` `TypeId` rule.
55pub type OperatorChordWirerHandle = Arc<dyn OperatorChordWirer>;