lattice_picker/events.rs
1// `linkme`'s distributed-slice expansion uses `#[link_section]`
2// declarations which the workspace's `unsafe_code = "deny"`
3// lint flags. Same shape `lattice-lsp::events` and
4// `lattice-config::core_options` use.
5#![allow(unsafe_code)]
6
7//! Picker-owned editor-bus events (§5.10).
8//!
9//! `PickerAccepted` is the canonical signal a candidate was
10//! chosen. The MRU index subscribes to record on accept;
11//! plugins (Phase 7+) can subscribe for telemetry / heatmap
12//! views / behavioral learning. The publish path stays on
13//! the orchestration thread so the bus delivers the event to
14//! every subscriber synchronously -- the MRU subscriber
15//! finishes its record-and-persist before the App applies
16//! the accept outcome.
17//!
18//! Why typed events here instead of direct calls: the App's
19//! `do_picker_accept` used to call `picker_mru.record`
20//! inline. Decoupling lets plugin sources (Phase 7) react
21//! without lattice-ui-tui growing per-plugin hooks, mirrors
22//! the §5.10 hooks / autocmds unification, and makes the
23//! "what fires on accept?" surface introspectable via
24//! `:describe-events`.
25
26use std::path::PathBuf;
27use std::time::SystemTime;
28
29/// Fired when the user accepts a candidate from a picker
30/// (either through `Action::PickerAccept` or the single-
31/// match LSP-picker short-circuit). The MRU index
32/// subscribes to record + persist; future plugin
33/// subscribers can read the same signal.
34///
35/// `source_id` is the picker registry id (`"files"`,
36/// `"commands"`, ...). `identity` is the MRU key derived
37/// from the routing payload via
38/// `lattice_picker::routing_identity`; `None` means the
39/// routing payload has no stable identity (line/col drifts,
40/// per-request index, etc.) and the candidate doesn't
41/// participate in MRU. Subscribers that care about
42/// identity-bearing accepts skip the `None` arm.
43///
44/// `ts` is the wall-clock instant the accept fired; the MRU
45/// subscriber stamps it on the `MruEntry`, while telemetry
46/// subscribers can attribute by time window.
47#[derive(Debug, Clone)]
48pub struct PickerAccepted {
49 pub source_id: String,
50 pub identity: Option<String>,
51 pub routing_payload_path: Option<PathBuf>,
52 pub ts: SystemTime,
53}
54
55lattice_protocol::register_event!(
56 PickerAccepted,
57 "picker.accepted",
58 "Fired when the user accepts a candidate from a picker. \
59 The MRU index subscribes to record; plugins can subscribe \
60 for telemetry / behavioral learning.",
61 "lattice-picker",
62);
63
64/// Fired when a picker opens (`:picker <source>`). Carries
65/// the source id so subscribers can react to specific
66/// surfaces (e.g. a plugin that wants to inject extra
67/// candidates into `:picker grep` when it sees the open).
68///
69/// Less load-bearing than `PickerAccepted` -- shipped for
70/// symmetry + the introspection surface (`:describe-events`
71/// shows what an editor can observe). No first-party
72/// subscribers today.
73#[derive(Debug, Clone)]
74pub struct PickerOpened {
75 pub source_id: String,
76 pub ts: SystemTime,
77}
78
79lattice_protocol::register_event!(
80 PickerOpened,
81 "picker.opened",
82 "Fired when a picker opens for a registered source.",
83 "lattice-picker",
84);
85
86/// Fired when a picker dismisses without an accept (Esc,
87/// empty filter, source-side abort). Counterpart to
88/// `PickerAccepted` for subscribers that track open-without-
89/// accept sessions.
90#[derive(Debug, Clone)]
91pub struct PickerDismissed {
92 pub source_id: String,
93 pub ts: SystemTime,
94}
95
96lattice_protocol::register_event!(
97 PickerDismissed,
98 "picker.dismissed",
99 "Fired when a picker dismisses without an accept.",
100 "lattice-picker",
101);