Behaviour for picker sources.
A source provides candidates for a picker and handles the select/cancel actions. Implementing this behaviour is all that's needed to add a new picker-powered feature — no changes to the editor core required.
Callbacks
candidates/1— returns the list of picker items given some contexton_select/2— called when the user selects an item; returns new editor stateon_bulk_select/2— optionally called when the user confirms explicitly marked itemson_cancel/1— called when the user cancels; returns new editor statepreview?/0— legacy live-navigation preview flag (default: false)live_preview?/0— whether navigating the picker should temporarily runon_select/2for the highlighted item (default:preview?/0for backwards compatibility)gui_preview?/0— whether the GUI preview pane should be shown for this source (default: false)preview/2— optional source-provided GUI preview content for the selected item, called with the render contexttitle/0— the picker title shown in the separator bar
Example
defmodule MySource do
@behaviour MingaEditor.UI.Picker.Source
@impl true
def title, do: "My picker"
@impl true
def candidates(_context),
do: [%MingaEditor.UI.Picker.Item{id: :a, label: "item a", description: "description"}]
@impl true
def on_select(item, state), do: state
@impl true
def on_cancel(state), do: state
end
Summary
Types
An alternative action: display name and action identifier.
Extra info an async source can report alongside its candidates, surfaced by the
editor once results land (e.g. a "results truncated" status line). Sources that
don't need it omit async_fetch/1 and the editor uses an empty map.
Picker layout: bottom-anchored (default) or centered floating window.
Render context passed to preview/2 from the GUI emit pipeline.
A styled preview segment: display text, 24-bit foreground color, bold flag.
A normalized picker keyboard shortcut.
Callbacks
Returns the list of alternative actions available for a picker item. The first entry is conventionally the default action.
Whether this source fetches candidates asynchronously.
When true, PickerUI.open/3 opens the picker immediately with a
"Searching..." indicator and fetches candidates in a background task.
Defaults to false (synchronous).
Async candidate fetch with optional reporting metadata.
Returns the list of alternative actions available for a marked item batch.
Returns the list of candidates to display in the picker.
Enriches a bounded list of items for display.
Whether the GUI preview pane should be shown for this source.
Whether the picker should stay open after selecting an item.
Defaults to false (picker closes on Enter). When true, the picker
calls on_select, then refreshes items via candidates/1 so the
user can see updated state (e.g., tool install status changes).
Returns the preferred layout for this picker source.
Defaults to :bottom (Emacs-style minibuffer overlay).
:centered renders inside a FloatingWindow overlay.
Whether navigating the picker should live-preview the selection.
Executes an alternative action on a picker item. Called when the user selects an action from the C-o menu.
Executes an alternative action on a marked item batch.
Called when the user confirms explicitly marked items. Returns the new editor state.
Called when the user cancels the picker. Returns the new editor state.
Called when the user selects an item. Returns the new editor state.
Returns source-provided GUI preview content for an item.
Legacy live-navigation preview flag.
Returns the action identifier for a source-owned keyboard shortcut, or nil.
Returns the display title for this picker source.
Functions
Returns a validated action collection, or an empty list when unsupported.
Returns whether a source fetches candidates asynchronously.
Returns validated bulk actions, or an empty list when unsupported.
Runs bulk select, returning state unchanged when unsupported.
Returns a validated picker candidate collection.
Returns a validated enriched candidate collection.
Returns whether a source defers display enrichment.
Runs and validates an asynchronous candidate fetch.
Whether the GUI preview pane should be shown for the source.
Returns whether a source module supports alternative actions.
Returns whether a source module supports bulk alternative actions.
Returns whether a source module supports bulk select.
Returns whether the picker should stay open after selection.
Returns the validated preferred picker layout.
Returns whether navigating the picker should live-preview the selection.
Runs a validated alternative action.
Runs a validated bulk action, returning state unchanged when unsupported.
Runs a validated picker cancellation callback.
Runs a validated picker selection callback.
Returns source-provided preview content, or nil when no preview callback exists.
Returns whether a source module should live-preview the highlighted item.
Default on_cancel implementation: restores the buffer that was active
when the picker opened (stored in the picker payload's restore field),
or returns state unchanged if no restore index was saved.
Returns the source action for a keyboard shortcut, with a common delete fallback.
Returns the authoritative contribution source installed at callback invocation.
Returns a validated picker title.
Types
An alternative action: display name and action identifier.
@type fetch_meta() :: %{optional(:status) => String.t()}
Extra info an async source can report alongside its candidates, surfaced by the
editor once results land (e.g. a "results truncated" status line). Sources that
don't need it omit async_fetch/1 and the editor uses an empty map.
@type layout() :: :bottom | :centered
Picker layout: bottom-anchored (default) or centered floating window.
@type preview_context() :: MingaEditor.Frontend.Emit.Context.t()
Render context passed to preview/2 from the GUI emit pipeline.
@type preview_segment() :: {String.t(), non_neg_integer(), boolean()}
A styled preview segment: display text, 24-bit foreground color, bold flag.
@type shortcut() :: {:ctrl, non_neg_integer()}
A normalized picker keyboard shortcut.
Callbacks
@callback actions(MingaEditor.UI.Picker.item()) :: [action_entry()]
Returns the list of alternative actions available for a picker item. The first entry is conventionally the default action.
@callback async?() :: boolean()
Whether this source fetches candidates asynchronously.
When true, PickerUI.open/3 opens the picker immediately with a
"Searching..." indicator and fetches candidates in a background task.
Defaults to false (synchronous).
@callback async_fetch(MingaEditor.UI.Picker.Context.t()) :: {:ok, [MingaEditor.UI.Picker.item()], fetch_meta()} | {:error, String.t()}
Async candidate fetch with optional reporting metadata.
Runs off the editor input path inside the picker's background task. Sources that
need to report a status (e.g. project search reporting that results were capped)
implement this and return {:ok, items, meta}; everything else falls back to
candidates/1 via fetch/2. Errors are returned as {:error, message}.
@callback bulk_actions([MingaEditor.UI.Picker.item()]) :: [action_entry()]
Returns the list of alternative actions available for a marked item batch.
@callback candidates(MingaEditor.UI.Picker.Context.t()) :: [MingaEditor.UI.Picker.item()]
Returns the list of candidates to display in the picker.
@callback enrich([MingaEditor.UI.Picker.item()]) :: [MingaEditor.UI.Picker.item()]
Enriches a bounded list of items for display.
Filtering keeps only the top results, so a source with expensive per-item
display work (icons, colors, two-line descriptions, status annotations) can
return lean items from candidates/1 and defer that work to this callback,
which runs only on the small set actually shown. The default is identity, so
sources that already return fully-built items need not implement it.
Enrichment must be a pure function of the items themselves (any state a source
needs should be stashed in Item.meta at candidates/1 time), because it runs
on every render of the visible window.
@callback gui_preview?() :: boolean()
Whether the GUI preview pane should be shown for this source.
@callback keep_open_on_select?() :: boolean()
Whether the picker should stay open after selecting an item.
Defaults to false (picker closes on Enter). When true, the picker
calls on_select, then refreshes items via candidates/1 so the
user can see updated state (e.g., tool install status changes).
@callback layout() :: layout()
Returns the preferred layout for this picker source.
Defaults to :bottom (Emacs-style minibuffer overlay).
:centered renders inside a FloatingWindow overlay.
@callback live_preview?() :: boolean()
Whether navigating the picker should live-preview the selection.
@callback on_action(term(), MingaEditor.UI.Picker.item(), MingaEditor.State.t()) :: MingaEditor.State.t()
Executes an alternative action on a picker item. Called when the user selects an action from the C-o menu.
Like on_select/2, this runs after the picker has been closed. Any
context required must travel with the Picker.item(); do not read
state.shell_runtime.state.modal here.
@callback on_bulk_action(term(), [MingaEditor.UI.Picker.item()], MingaEditor.State.t()) :: MingaEditor.State.t()
Executes an alternative action on a marked item batch.
@callback on_bulk_select([MingaEditor.UI.Picker.item()], MingaEditor.State.t()) :: MingaEditor.State.t()
Called when the user confirms explicitly marked items. Returns the new editor state.
Sources that do not implement this callback ignore picker marks and keep the
single-selection behavior from on_select/2.
@callback on_cancel(MingaEditor.State.t()) :: MingaEditor.State.t()
Called when the user cancels the picker. Returns the new editor state.
@callback on_select(MingaEditor.UI.Picker.item(), MingaEditor.State.t()) :: MingaEditor.State.t()
Called when the user selects an item. Returns the new editor state.
Important: this callback runs after the picker has been closed
(state.shell_runtime.state.modal has been reset to :none). Any context the
callback needs must travel with the Picker.item() (typically embedded
in Item.id). Reading state.shell_runtime.state.modal here will see :none.
@callback preview(MingaEditor.UI.Picker.item(), context :: preview_context()) :: [[preview_segment()]] | nil
Returns source-provided GUI preview content for an item.
@callback preview?() :: boolean()
Legacy live-navigation preview flag.
@callback shortcut_action(MingaEditor.UI.Picker.item(), shortcut()) :: term() | nil
Returns the action identifier for a source-owned keyboard shortcut, or nil.
@callback title() :: String.t()
Returns the display title for this picker source.
Functions
@spec actions( module(), MingaEditor.UI.Picker.item(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: [action_entry()]
Returns a validated action collection, or an empty list when unsupported.
@spec async?( module(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: boolean()
Returns whether a source fetches candidates asynchronously.
@spec bulk_actions( module(), [MingaEditor.UI.Picker.item()], Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: [action_entry()]
Returns validated bulk actions, or an empty list when unsupported.
@spec bulk_select( module(), [MingaEditor.UI.Picker.item()], MingaEditor.State.t(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: MingaEditor.State.t()
Runs bulk select, returning state unchanged when unsupported.
@spec candidates( module(), MingaEditor.UI.Picker.Context.t(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: [MingaEditor.UI.Picker.item()]
Returns a validated picker candidate collection.
@spec enrich( module(), [MingaEditor.UI.Picker.item()], Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: [MingaEditor.UI.Picker.item()]
Returns a validated enriched candidate collection.
Returns whether a source defers display enrichment.
@spec fetch( module(), MingaEditor.UI.Picker.Context.t(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: {:ok, [MingaEditor.UI.Picker.item()], fetch_meta()} | {:error, String.t()}
Runs and validates an asynchronous candidate fetch.
@spec gui_preview?( module(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: boolean()
Whether the GUI preview pane should be shown for the source.
Returns whether a source module supports alternative actions.
Returns whether a source module supports bulk alternative actions.
Returns whether a source module supports bulk select.
@spec keep_open_on_select?( module(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: boolean()
Returns whether the picker should stay open after selection.
@spec layout( module(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: layout()
Returns the validated preferred picker layout.
@spec live_preview?( module(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: boolean()
Returns whether navigating the picker should live-preview the selection.
@spec on_action( module(), term(), MingaEditor.UI.Picker.item(), MingaEditor.State.t(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: MingaEditor.State.t()
Runs a validated alternative action.
@spec on_bulk_action( module(), term(), [MingaEditor.UI.Picker.item()], MingaEditor.State.t(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: MingaEditor.State.t()
Runs a validated bulk action, returning state unchanged when unsupported.
@spec on_cancel( module(), MingaEditor.State.t(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: MingaEditor.State.t()
Runs a validated picker cancellation callback.
@spec on_select( module(), MingaEditor.UI.Picker.item(), MingaEditor.State.t(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: MingaEditor.State.t()
Runs a validated picker selection callback.
@spec preview( module(), MingaEditor.UI.Picker.item(), preview_context(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: [[preview_segment()]] | nil
Returns source-provided preview content, or nil when no preview callback exists.
@spec preview?( module(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: boolean()
Returns whether a source module should live-preview the highlighted item.
@spec restore_or_keep(MingaEditor.State.t()) :: MingaEditor.State.t()
Default on_cancel implementation: restores the buffer that was active
when the picker opened (stored in the picker payload's restore field),
or returns state unchanged if no restore index was saved.
@spec shortcut_action( module(), MingaEditor.UI.Picker.item(), shortcut(), Minga.Extension.ContributionCleanup.contribution_source() | nil ) :: term() | nil
Returns the source action for a keyboard shortcut, with a common delete fallback.
@spec source_identity(module()) :: Minga.Extension.ContributionCleanup.contribution_source() | nil
Returns the authoritative contribution source installed at callback invocation.
@spec title(module(), Minga.Extension.ContributionCleanup.contribution_source() | nil) :: String.t()
Returns a validated picker title.