MingaEditor.UI.Picker.Source behaviour (Minga v0.1.0)

Copy Markdown View Source

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 context
  • on_select/2 — called when the user selects an item; returns new editor state
  • on_bulk_select/2 — optionally called when the user confirms explicitly marked items
  • on_cancel/1 — called when the user cancels; returns new editor state
  • preview?/0 — legacy live-navigation preview flag (default: false)
  • live_preview?/0 — whether navigating the picker should temporarily run on_select/2 for the highlighted item (default: preview?/0 for 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 context
  • title/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

action_entry()

@type action_entry() :: {name :: String.t(), action_id :: term()}

An alternative action: display name and action identifier.

fetch_meta()

@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.

layout()

@type layout() :: :bottom | :centered

Picker layout: bottom-anchored (default) or centered floating window.

preview_context()

@type preview_context() :: MingaEditor.Frontend.Emit.Context.t()

Render context passed to preview/2 from the GUI emit pipeline.

preview_segment()

@type preview_segment() :: {String.t(), non_neg_integer(), boolean()}

A styled preview segment: display text, 24-bit foreground color, bold flag.

shortcut()

@type shortcut() :: {:ctrl, non_neg_integer()}

A normalized picker keyboard shortcut.

Callbacks

actions(item)

(optional)
@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.

async?()

(optional)
@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).

async_fetch(t)

(optional)
@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}.

bulk_actions(list)

(optional)
@callback bulk_actions([MingaEditor.UI.Picker.item()]) :: [action_entry()]

Returns the list of alternative actions available for a marked item batch.

candidates(t)

Returns the list of candidates to display in the picker.

enrich(list)

(optional)

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.

gui_preview?()

(optional)
@callback gui_preview?() :: boolean()

Whether the GUI preview pane should be shown for this source.

keep_open_on_select?()

(optional)
@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).

layout()

(optional)
@callback layout() :: layout()

Returns the preferred layout for this picker source. Defaults to :bottom (Emacs-style minibuffer overlay). :centered renders inside a FloatingWindow overlay.

live_preview?()

(optional)
@callback live_preview?() :: boolean()

Whether navigating the picker should live-preview the selection.

on_action(term, item, t)

(optional)

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.

on_bulk_action(term, list, t)

(optional)
@callback on_bulk_action(term(), [MingaEditor.UI.Picker.item()], MingaEditor.State.t()) ::
  MingaEditor.State.t()

Executes an alternative action on a marked item batch.

on_bulk_select(list, t)

(optional)

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.

on_cancel(t)

@callback on_cancel(MingaEditor.State.t()) :: MingaEditor.State.t()

Called when the user cancels the picker. Returns the new editor state.

on_select(item, 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.

preview(item, context)

(optional)
@callback preview(MingaEditor.UI.Picker.item(), context :: preview_context()) ::
  [[preview_segment()]] | nil

Returns source-provided GUI preview content for an item.

preview?()

(optional)
@callback preview?() :: boolean()

Legacy live-navigation preview flag.

shortcut_action(item, shortcut)

(optional)
@callback shortcut_action(MingaEditor.UI.Picker.item(), shortcut()) :: term() | nil

Returns the action identifier for a source-owned keyboard shortcut, or nil.

title()

@callback title() :: String.t()

Returns the display title for this picker source.

Functions

actions(module, item, source \\ nil)

Returns a validated action collection, or an empty list when unsupported.

async?(module, source \\ nil)

Returns whether a source fetches candidates asynchronously.

bulk_actions(module, items, source \\ nil)

Returns validated bulk actions, or an empty list when unsupported.

bulk_select(module, items, state, source \\ nil)

Runs bulk select, returning state unchanged when unsupported.

candidates(module, context, source \\ nil)

Returns a validated picker candidate collection.

enrich(module, items, source \\ nil)

Returns a validated enriched candidate collection.

enriches?(module)

@spec enriches?(module()) :: boolean()

Returns whether a source defers display enrichment.

fetch(module, context, source \\ nil)

Runs and validates an asynchronous candidate fetch.

gui_preview?(module, source \\ nil)

Whether the GUI preview pane should be shown for the source.

has_actions?(module)

@spec has_actions?(module()) :: boolean()

Returns whether a source module supports alternative actions.

has_bulk_actions?(module)

@spec has_bulk_actions?(module()) :: boolean()

Returns whether a source module supports bulk alternative actions.

has_bulk_select?(module)

@spec has_bulk_select?(module()) :: boolean()

Returns whether a source module supports bulk select.

keep_open_on_select?(module, source \\ nil)

@spec keep_open_on_select?(
  module(),
  Minga.Extension.ContributionCleanup.contribution_source() | nil
) ::
  boolean()

Returns whether the picker should stay open after selection.

layout(module, source \\ nil)

Returns the validated preferred picker layout.

live_preview?(module, source \\ nil)

Returns whether navigating the picker should live-preview the selection.

on_action(module, action, item, state, source \\ nil)

Runs a validated alternative action.

on_bulk_action(module, action, items, state, source \\ nil)

Runs a validated bulk action, returning state unchanged when unsupported.

on_cancel(module, state, source \\ nil)

Runs a validated picker cancellation callback.

on_select(module, item, state, source \\ nil)

Runs a validated picker selection callback.

preview(module, item, context, source \\ nil)

Returns source-provided preview content, or nil when no preview callback exists.

preview?(module, source \\ nil)

Returns whether a source module should live-preview the highlighted item.

restore_or_keep(state)

@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.

shortcut_action(module, item, shortcut, source \\ nil)

Returns the source action for a keyboard shortcut, with a common delete fallback.

source_identity(module)

Returns the authoritative contribution source installed at callback invocation.

title(module, source \\ nil)

Returns a validated picker title.