MingaEditor.CompletionHandling (Minga v0.1.0)

Copy Markdown View Source

Completion accept, filter, trigger, and dismiss logic.

Handles both LSP completions (async, debounced) and config file completions (synchronous, from the Options registry). Extracted from MingaEditor to keep the GenServer module focused on orchestration. All functions are pure state transforms.

Summary

Types

Config contexts that produce completion items (excludes :none).

Config completion context detected from cursor position.

Functions

Accepts the currently selected completion item.

Applies a completion result that was processed off the Editor in a Task.

Detects the config DSL context from a line of text and cursor position.

Dismisses the active completion popup and resets trigger state.

Sends the actual completionItem/resolve request after debounce.

Handles a completionItem/resolve response.

Handles an LSP completion response off the Editor hot path.

Handles a textDocument/signatureHelp response.

Updates or dismisses completion after a key press.

Triggers a completionItem/resolve request for the selected item.

Types

active_config_context()

@type active_config_context() :: :option_name | {:option_value, atom()} | :filetype

Config contexts that produce completion items (excludes :none).

config_context()

@type config_context() :: :option_name | {:option_value, atom()} | :filetype | :none

Config completion context detected from cursor position.

Functions

accept(state, completion)

Accepts the currently selected completion item.

Routes to insert-text or text-edit depending on the completion type, then dismisses the completion popup.

apply_processed(state, gen, mode, payload, trigger_pos)

Applies a completion result that was processed off the Editor in a Task.

Discards the result unless a completion modal is still open and it carries the current request generation (gen). This latest-wins guard drops a stale Task result from a request batch that was superseded by newer typing, so an out-of-order or slow Task can never overwrite a fresher menu.

For a :primary result the Task already produced a fully sorted+filtered Completion, so the common single-server path is a cheap assignment. If a secondary server's :merge result happened to land first, the primary result is unioned into it (rather than replacing it) so no server's items are lost.

A :failed payload (the Task crashed or could not start) clears a still-pending menu so it is not left stuck, while leaving an already-populated menu intact.

detect_config_context(line_text, cursor_col)

@spec detect_config_context(String.t(), non_neg_integer()) :: config_context()

Detects the config DSL context from a line of text and cursor position.

Returns :option_name, {:option_value, atom()}, :filetype, or :none. Used internally by config_completion_context/1 after determining the buffer is a config file. Exposed for testing.

dismiss(state)

Dismisses the active completion popup and resets trigger state.

No-op when the active modal is something other than :completion — the caller may invoke this on every non-insert keypress, so we mustn't displace the picker / prompt / etc.

flush_resolve(state, index)

Sends the actual completionItem/resolve request after debounce.

Called from MingaEditor.handle_info({:completion_resolve, index}).

handle_resolve_response(state, arg)

@spec handle_resolve_response(MingaEditor.State.t(), {:ok, term()} | {:error, term()}) ::
  MingaEditor.State.t()

Handles a completionItem/resolve response.

Updates the selected completion item's documentation with the resolved content.

handle_response(state, ref, result)

@spec handle_response(MingaEditor.State.t(), reference(), term()) ::
  MingaEditor.State.t()

Handles an LSP completion response off the Editor hot path.

Only the cheap bookkeeping runs on the Editor: CompletionTrigger.classify_response/4 matches the response ref against the pending request and decides whether it is the primary response, a secondary one to merge, or stale. The expensive work (parsing every item, sorting, and prefix-filtering) is then handed to a Task under Minga.Eval.TaskSupervisor, which sends the processed result back as {:completion_processed, gen, mode, payload, trigger_pos}. The Editor applies that via apply_processed/5 with a cheap state assignment.

This keeps the Editor mailbox responsive even when a server returns 1000+ completion items.

handle_signature_help_response(state, arg2)

@spec handle_signature_help_response(
  MingaEditor.State.t(),
  {:ok, term()} | {:error, term()}
) ::
  MingaEditor.State.t()

Handles a textDocument/signatureHelp response.

Creates a SignatureHelp struct from the response and stores it in editor state. Returns state unchanged on error or empty response.

maybe_handle(state, was_inserting, codepoint, modifiers)

Updates or dismisses completion after a key press.

Called after every key in insert mode. If the mode changed away from insert, dismisses completion. Otherwise updates the filter prefix and possibly triggers new completion.

maybe_resolve_selected(state)

@spec maybe_resolve_selected(MingaEditor.State.t()) :: MingaEditor.State.t()

Triggers a completionItem/resolve request for the selected item.

Called when C-n/C-p moves the selection. Debounces to avoid flooding the server when navigating rapidly. Only triggers if the selected item doesn't already have documentation and the server supports resolve.