Skip to main content

Picker

Struct Picker 

Source
pub struct Picker {
Show 16 fields pub title: String, pub root_label: Option<String>, pub query: String, pub query_cursor: usize, pub candidates: Vec<RenderedCandidate>, pub selected: usize, pub source: PickerSource, pub on_accept: PickerAction, pub source_id: Option<String>, pub loading: bool, pub revision: u64, pub transient: Option<Arc<TransientSpec>>, pub transient_state: TransientState, pub transient_stack: Vec<(Arc<TransientSpec>, TransientState, usize)>, pub transient_selected: usize, pub transient_prefix: String, /* private fields */
}
Expand description

One open vertico-style picker. Lives on App.picker while active; the input and render layers route to / from it via the Action::Picker* family.

Fields§

§title: String§root_label: Option<String>

PP.2: the root these results are scoped to, ready to display — home-contracted (~/src/lattice) by whoever set it, because the renderers have one line and the home prefix is the least informative part of a path.

None for every picker whose results are not root-scoped, which is most of them: buffers spans every project you have open, commands is registry-wide, lines is one buffer. A root on those is noise on the one line the user reads to know what they are looking at.

Set at seat time, from the seating source’s PickerSourceSpec::rooted — or directly, for the LSP pickers, which are seated by hand and never had a spec.

§query: String§query_cursor: usize

Byte offset within query where the cursor sits. Today the picker only appends / backspaces at end-of-query so this equals query.len(); reserved for future left/right editing.

§candidates: Vec<RenderedCandidate>

Candidates that pass the current query filter. Re-built on every refilter call.

§selected: usize

Index into candidates. Clamped to 0..candidates.len() on every refilter.

§source: PickerSource§on_accept: PickerAction§source_id: Option<String>

Picker-registry source id that seated this picker. Some when the picker was seated via the trait-driven path (:picker <source>); None for legacy imperative pickers (:b, :lsp-log, multi-result LSP locations). do_picker_accept reads this to decide whether to delegate accept to the source’s PickerSourceGenerator or fall back to the legacy per-routing dispatch.

§loading: bool

True while an async fetch for this picker is in flight (the initial grep on :picker grep <pat>, or a live re-query after a keystroke). Both renderers surface it as a searching… indicator in the prompt so a slow grep reads as “working”, not “nothing happened”. The host sets it when it spawns a fetch future and clears it when results seat (a fresh Picker defaults to false) or the fetch errors. See seat_picker_from_pairs / open_picker / fire_live_picker_query_changed.

§revision: u64

Globally monotonic stamp, re-taken by Self::refilter — i.e. by every change to candidates, because refilter is the only thing that writes them and every raw assignment calls it.

Exists for the host’s paint gate. compute_paint_revision used to fold in picker.is_some() and nothing else, on a comment that read “async result GROWTH inside an open picker still rides a keystroke today; if that changes, fold a content count here.” PC.10’s <C-l> / <Tab> descend is when that changed: it re-queries the source off-keystroke, so the new listing replaced candidates while the gate reported nothing moved, paint_request never fired, and the rows on screen stayed the ones from before the descend until the user typed.

A stamp rather than hashing the rows: the gate runs on every publish and a picker may hold tens of thousands of candidates (paramount #1). A stamp rather than candidates.len(): a re-query returning the same NUMBER of different rows is exactly what a length cannot see.

GLOBAL rather than per-picker, which is the whole of why the first attempt at this did not work. A live re-query does not mutate the open picker — seat_picker_from_pairs builds a FRESH Picker and swaps it in. A per-instance counter therefore read 1 both before and after the descend, the hash did not move, and the gate was as blind as before. A process-wide counter is unrepeatable by construction, so re-seating cannot alias.

§transient: Option<Arc<TransientSpec>>

Active transient-mode specification + live state. When Some, the renderer switches to grouped section layout with single-key chord dispatch, and the input layer routes keystrokes through transient chord matching rather than the query → filter path. See transient.rs.

§transient_state: TransientState§transient_stack: Vec<(Arc<TransientSpec>, TransientState, usize)>

Stack of parent transient specs for BS/DEL back navigation through nested submenus. The usize is the parent’s transient_selected at the moment its submenu opened — popping restores it instead of leaving the parent’s selection wherever the submenu happened to leave the shared field.

§transient_selected: usize

Which of the transient’s items <C-n> / <C-p> have walked to — an index over TransientSpec::selectable_count, NOT a scroll offset.

The distinction is the whole fix: the host can bound an item index from the spec alone, whereas a scroll offset’s true maximum depends on a viewport height only the renderer knows. The offset version grew unbounded and each renderer clamped it privately at paint time, so the stored value drifted tens of rows past anything renderable and <C-p> did nothing visible until the overshoot had been walked back off. Renderers now derive their scroll from this every frame (TransientSpec::scroll_for), leaving no scroll state to drift.

§transient_prefix: String

Keys typed at this level that begin some row’s key but do not complete one yet — magit’s , k / , r / = f rows.

Empty whenever no multi-key row is part-way typed, which is almost always. The host used to compare a single typed char against each row’s key string, so a multi-key row rendered, could be walked to with <C-n> and fired with <CR>, and did nothing at all when its own keys were pressed.

Implementations§

Source§

impl Picker

Source

pub fn new( title: impl Into<String>, source: PickerSource, on_accept: PickerAction, ) -> Self

Source

pub fn set_create_label(&mut self, label: Option<String>)

OR.5: declare that this picker offers to create what the query names.

Called by the host at seat time from the seating source’s PickerSourceSpec::create_label. %s in label is replaced by the query on every render.

Source

pub fn create_label(&self) -> Option<&str>

OR.5: this picker’s create label, if it has one.

Source

pub fn arm_preview_settle(&mut self, delay: Duration)

MG.54: (re)start this picker’s preview settle window.

Called on every selection move for a source that declares a window. Each call pushes the deadline out, so a burst of moves leaves exactly one due time — the moment the user stopped.

The host still schedules the wake (it owns the runtime), but the policy and the state are the picker’s, so every picker — first party, plugin, one written next year — gets the same behaviour from declaring the window alone.

Source

pub fn take_due_preview_settle(&mut self, now: Instant) -> bool

MG.54: has the settle window elapsed? Consumes the deadline when it has, so the preview runs once per settle no matter how many wakes arrive — a burst of N moves schedules N wakes, and the N-1 superseded ones find the deadline still in the future.

Source

pub fn preview_settle_pending(&self) -> bool

MG.54: whether a deferred preview is waiting on this picker. Read by tests and by the host’s “is anything pending” checks; the deadline itself is deliberately not exposed.

Source

pub fn filtered_routing(&self) -> Vec<&RoutingPayload>

Unwind one level of transient state: a half-typed multi-key row first, then one submenu off the stack.

Returns false when there was nothing to unwind — the caller (<Esc> / BS) then means “close”, because this is the root menu with no pending keys.

The precedence is the point. A part-typed row (, waiting for k) is what the key most likely means to undo, so it goes first; only once nothing is pending does the same key leave the menu you are in. Vim gives <Esc> the same precedence over a partial chord.

Lives here rather than in the host’s dispatch arm because BS and <Esc> both need it, and two copies of a precedence rule drift. LR.5: the routing payloads of the candidates that survived the current query — the FILTERED set, which is what <C-q> means. Sending the unfiltered list would discard the work the user just did typing a query (telescope’s send_to_qflist semantics).

Source

pub fn filtered_entries(&self) -> Vec<(&str, &RoutingPayload)>

PE.2: the filtered candidates paired with their routing payload AND the visible row text (display) — the peer of Self::filtered_routing for a send that wants to carry the matched line as the error-list entry’s message, not just its coordinates.

<C-q>’s result should read the way the rows looked in the picker (the grep match text, the symbol name, the reference preview), so the error list and *problems* are a column of meaningful lines rather than bare file:lines the user must jump to one by one to tell apart.

Source

pub fn transient_unwind(&mut self) -> bool

Source

pub fn transient_select_next(&mut self)

Walk the transient’s selection one item forward, wrapping at the end — the same wrap Self::select_next gives the candidate list, and the reason <C-n> can no longer overshoot: the index is taken modulo the item count, so there is no out-of-range value to represent.

No-op when no transient is open or it has no items.

Source

pub fn transient_select_prev(&mut self)

The peer of Self::transient_select_next, wrapping the other way.

Source

pub fn transient_selected_item(&self) -> Option<&TransientItem>

The item <CR> would fire — the selection resolved against the open transient. None when there is no transient, it is empty, or the selection is somehow past its end.

Source

pub fn set_live_source_mode(&mut self, live: bool)

Toggle live-source mode (spec().live == true). When on, Self::refilter renders raw verbatim instead of running fuzzy matching. The host calls this once at picker-open time after consulting the source’s spec; the flag stays on for the picker’s lifetime.

Source

pub fn is_live_source_mode(&self) -> bool

Query accessor for the host (and tests) – mirrors the other state predicates.

Source

pub fn set_orderless(&mut self, orderless: bool)

Mirror the picker.orderless option onto this picker. The host calls this once at picker-open time, alongside Self::set_live_source_mode; the flag stays put for the picker’s lifetime so the result list cannot change semantics underneath a user who is mid-query.

Source

pub fn is_orderless(&self) -> bool

Whether this picker splits its query into orderless components.

Source

pub fn set_mru_bonuses(&mut self, bonuses: Vec<f64>)

Stamp the parallel mru_bonuses vec the matcher reads during refilter. Must be called after Self::set_raw_candidates_with_routing and must match routing_meta.len() in length; mismatched lengths reset to zero-bonus so the picker stays in a sane state if a caller miscounts (mostly a guardrail for tests).

Callers that already have both pairs + bonuses in hand should prefer Self::set_raw_candidates_with_routing_and_bonuses – it sets all three vecs and refilters once, vs. this path which leaves a wasted refilter behind from the preceding set_raw_candidates_with_routing call.

Source

pub fn set_raw_candidates_with_routing_and_bonuses( &mut self, items: Vec<(RawCandidate, RoutingPayload)>, bonuses: Vec<f64>, )

Single-pass seat: mutate raw + routing_meta + mru_bonuses and refilter exactly once. The fast-path replacement for the set_raw_candidates_with_routing + set_mru_bonuses pair the host’s trait-driven seat path used before this method existed – which refiltered twice, with the first pass entirely wasted because the bonuses were about to replace the same data.

Mismatched-length bonuses zero out (same guardrail as Self::set_mru_bonuses). Legacy callers that don’t have bonuses yet keep using set_raw_candidates_with_routing

  • set_mru_bonuses; the new method is opt-in.
Source

pub fn mru_bonus_for(&self, candidate: &RenderedCandidate) -> f64

Borrow the candidate’s MRU bonus by its routing-payload index. Returns 0.0 for candidates without a registered bonus (slice 12 pickers, legacy LSP pickers, anything pre-MRU-snapshot). Public so the refilter path can read it; not intended for downstream callers.

Source

pub fn set_raw_candidates(&mut self, raw: Vec<RawCandidate>)

Replace the raw candidate list. Host-built (e.g. the TUI host walks BufferRegistry for the buffer switcher); picker just stores + refilters. The single mutation entry point: every other “set the candidates” helper (e.g. Self::set_lsp_instances) routes through this.

Source

pub fn set_raw_candidates_with_routing( &mut self, items: Vec<(RawCandidate, RoutingPayload)>, )

Replace the raw candidate list AND the typed routing sidecar (Phase 4.2.g.7 polish). Each input pair is a (RawCandidate, RoutingPayload) – the picker stores the payload at index i in routing_meta and stamps the candidate’s data with Extension { kind_id: PICKER_ROUTING_KIND_ID, payload: i.to_le_bytes() }. The accept dispatch reads the index back, indexes the sidecar, and matches on the typed enum variant. Replaces the prior text-tab-encoded string-parsing path.

Source

pub fn routing_for( &self, candidate: &RenderedCandidate, ) -> Option<&RoutingPayload>

Look up the routing payload for candidate – returns None for candidates that don’t carry a picker-routing Extension payload (defensive; the picker only ever builds candidates through Self::set_raw_candidates_with_routing in the new world). Used by the accept dispatch.

Source

pub fn set_lsp_locations(&mut self, rows: Vec<LspLocationRow>)

Replace the raw candidate list with externally-built LSP location rows – multi-result navigation, references, diagnostics. Caller builds + sorts + dedups the Vec host-side; the picker just stores + refilters.

Source

pub fn set_lsp_instances(&mut self, rows: Vec<LspInstanceRow>)

Replace the raw candidate list with externally-built LSP instance rows. Caller (App::open_lsp_picker) snapshots the supervisor under its lock and hands the resulting tuples here. Refreshes the filter.

Slice 15: stamps each candidate’s accept_action based on the picker’s on_accept (OpenLspLog vs OpenLspTraceLog) so the typed dispatch (7d.0) fires instead of the legacy PickerAction match.

Source

pub fn set_ai_sessions(&mut self, rows: Vec<AiSessionRow>)

Replace the raw candidate list with externally-built AI session rows. Caller (App::do_open_ai_log) snapshots the AiLogger service’s known_sessions() and hands the rows here. Honors the source’s optional provider prefilter. Mirrors Self::set_lsp_instances, minus the accept_action stamping (the AI picker rides the legacy RoutingPayload::AiSession accept dispatch).

Source

pub fn refilter(&mut self)

Filter raw against the current query and write the matches into candidates. Routes through [lattice_completion::fuzzy_match] – the same 5-tier algorithm Insert-mode completion uses (exact / prefix / word-boundary / substring / subsequence). Picker rows match against display because their text field often carries a routing payload (e.g. "<server_id>\t<workspace>") the user never sees. Empty query yields a uniform score so every candidate passes through; the rust stdlib’s stable sort preserves the host-supplied insertion order on ties (callers like the buffer switcher depend on this – alternate-buffer floats to the top via insertion order).

Source

pub fn append_query(&mut self, c: char)

Source

pub fn paste_query(&mut self, text: &str) -> bool

Append a whole pasted burst to the query.

Newlines are flattened to spaces rather than dropped or honoured. The query is a single line, so a multi-line paste has to become one — and joining with nothing would weld the last word of each line to the first of the next (foo.rs + bar.rs → foo.rsbar.rs), which matches nothing and looks like the paste was corrupted. Other control characters are dropped: they cannot be typed into the query, so they cannot be intended in it, and a stray \t or \r from a terminal round-trip would silently make the filter match nothing.

Returns false when the burst contributes nothing, so the caller can skip the refilter and the preview.

Source

pub fn backspace_query(&mut self)

Source

pub fn delete_word_backward(&mut self) -> bool

PH.1: <C-w> — vim’s c_CTRL-W. Drop trailing whitespace, then the run of characters of the class before it: a word (alphanumerics and _) or a run of punctuation. foo bar → foo ; src/lib → src/.

Returns whether anything was deleted, so the host can skip the re-query tail on an empty query.

Source

pub fn clear_query(&mut self)

Source

pub fn select_next(&mut self)

Source

pub fn select_prev(&mut self)

Source

pub fn selected_candidate(&self) -> Option<&RenderedCandidate>

Trait Implementations§

Source§

impl Clone for Picker

Source§

fn clone(&self) -> Picker

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Picker

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more