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.

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.

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

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 display title for this picker source.

Functions

Returns the actions for an item, or an empty list if the source doesn't support actions.

Returns whether a source fetches candidates asynchronously.

Returns bulk actions for marked items, or an empty list if unsupported.

Runs bulk select for a source, returning state unchanged if unsupported.

Enriches the bounded display items for a source, returning them unchanged when the source does not implement enrich/1.

Returns whether a source defers display enrichment to enrich/1.

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

Returns whether a source module supports alternative actions (C-o menu).

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 selecting an item.

Returns the preferred layout for a source, defaulting to :bottom.

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

Runs a bulk action for a source, returning state unchanged if unsupported.

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

Returns whether a source module should live-preview the highlighted item. Falls back to false if the callback is not implemented.

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.

Types

action_entry()

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

An alternative action: display name and action identifier.

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.

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

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, state)

(optional)
@callback on_action(term(), MingaEditor.UI.Picker.item(), state :: term()) :: term()

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_state.modal here.

on_bulk_action(term, list, state)

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

Executes an alternative action on a marked item batch.

on_bulk_select(list, state)

(optional)
@callback on_bulk_select([MingaEditor.UI.Picker.item()], state :: term()) :: term()

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(state)

@callback on_cancel(state :: term()) :: term()

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

on_select(item, state)

@callback on_select(MingaEditor.UI.Picker.item(), state :: term()) :: term()

Called when the user selects an item. Returns the new editor state.

Important: this callback runs after the picker has been closed (state.shell_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_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.

title()

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

Returns the display title for this picker source.

Functions

actions(module, item)

@spec actions(module(), MingaEditor.UI.Picker.item()) :: [action_entry()]

Returns the actions for an item, or an empty list if the source doesn't support actions.

async?(module)

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

Returns whether a source fetches candidates asynchronously.

bulk_actions(module, items)

@spec bulk_actions(module(), [MingaEditor.UI.Picker.item()]) :: [action_entry()]

Returns bulk actions for marked items, or an empty list if unsupported.

bulk_select(module, items, state)

@spec bulk_select(module(), [MingaEditor.UI.Picker.item()], term()) :: term()

Runs bulk select for a source, returning state unchanged if unsupported.

enrich(module, items)

Enriches the bounded display items for a source, returning them unchanged when the source does not implement enrich/1.

enriches?(module)

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

Returns whether a source defers display enrichment to enrich/1.

gui_preview?(module)

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

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 (C-o menu).

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)

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

Returns whether the picker should stay open after selecting an item.

layout(module)

@spec layout(module()) :: layout()

Returns the preferred layout for a source, defaulting to :bottom.

live_preview?(module)

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

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

on_bulk_action(module, action, items, state)

@spec on_bulk_action(module(), term(), [MingaEditor.UI.Picker.item()], term()) ::
  term()

Runs a bulk action for a source, returning state unchanged if unsupported.

preview(module, item, context)

@spec preview(module(), MingaEditor.UI.Picker.item(), preview_context()) ::
  [[preview_segment()]] | nil

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

preview?(module)

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

Returns whether a source module should live-preview the highlighted item. Falls back to false if the callback is not implemented.

restore_or_keep(state)

@spec restore_or_keep(term()) :: term()

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.