# `Minga.Editing.Completion`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/editing/completion.ex#L1)

Pure data structure for managing LSP completion state.

Holds the list of completion items returned by a language server,
tracks the selected index, and filters items as the user continues
typing. All functions are pure transformations with no side effects.

## Lifecycle

1. `new/2` — create from parsed LSP completion items and a trigger position
2. `filter/2` — narrow the visible items as the user types more characters
3. `move_up/1` / `move_down/1` — navigate the selection
4. `selected_item/1` — get the currently highlighted item
5. `accept/1` — returns the text/edit to insert for the selected item

# `item`

```elixir
@type item() :: %{
  label: String.t(),
  kind: item_kind(),
  insert_text: String.t(),
  filter_text: String.t(),
  detail: String.t(),
  documentation: String.t(),
  sort_text: String.t(),
  text_edit: text_edit() | nil,
  raw: map() | nil
}
```

A parsed completion item.

# `item_kind`

```elixir
@type item_kind() ::
  :text
  | :method
  | :function
  | :constructor
  | :field
  | :variable
  | :class
  | :interface
  | :module
  | :property
  | :unit
  | :value
  | :enum
  | :keyword
  | :snippet
  | :color
  | :file
  | :reference
  | :folder
  | :enum_member
  | :constant
  | :struct
  | :event
  | :operator
  | :type_parameter
```

LSP CompletionItemKind as an atom.

# `t`

```elixir
@type t() :: %Minga.Editing.Completion{
  filter_text: String.t(),
  filtered: [item()],
  items: [item()],
  last_resolved_index: integer(),
  max_visible: pos_integer(),
  resolve_timer: reference() | nil,
  selected: non_neg_integer(),
  trigger_position: {non_neg_integer(), non_neg_integer()}
}
```

# `text_edit`

```elixir
@type text_edit() :: %{
  range: %{
    start_line: non_neg_integer(),
    start_col: non_neg_integer(),
    end_line: non_neg_integer(),
    end_col: non_neg_integer()
  },
  new_text: String.t()
}
```

A text edit to apply when accepting a completion.

# `accept`

```elixir
@spec accept(t()) :: {:insert_text, String.t()} | {:text_edit, text_edit()} | nil
```

Returns the insert text and optional text edit for the selected item.

If the item has a `text_edit`, returns `{:text_edit, edit}`.
Otherwise returns `{:insert_text, text}`.

# `active?`

```elixir
@spec active?(t()) :: boolean()
```

Returns true if there are any filtered items to show.

# `count`

```elixir
@spec count(t()) :: non_neg_integer()
```

Returns the count of currently filtered items.

# `filter`

```elixir
@spec filter(t(), String.t()) :: t()
```

Filters completion items by the text typed since the trigger position.

Items whose `filter_text` starts with `prefix` (case-insensitive) are kept.
Resets selection to the top.

# `kind_label`

```elixir
@spec kind_label(item_kind()) :: String.t()
```

Returns a single-character kind indicator for rendering.

# `move_down`

```elixir
@spec move_down(t()) :: t()
```

Moves the selection down one item, wrapping at the bottom.

# `move_up`

```elixir
@spec move_up(t()) :: t()
```

Moves the selection up one item, wrapping at the top.

# `new`

```elixir
@spec new(
  [item()],
  {non_neg_integer(), non_neg_integer()}
) :: t()
```

Creates a new completion state from a list of parsed items and the
cursor position where completion was triggered.

# `parse_item`

```elixir
@spec parse_item(map()) :: item()
```

Parses a single LSP CompletionItem map into an `item()` struct.

# `parse_response`

```elixir
@spec parse_response(map() | [map()] | nil) :: [item()]
```

Parses an LSP completion response into a list of `item()` maps.

Handles both `CompletionList` (`%{"items" => [...]}`) and bare
`CompletionItem[]` response formats.

# `selected_item`

```elixir
@spec selected_item(t()) :: item() | nil
```

Returns the currently selected item, or nil if no items.

# `update_selected_documentation`

```elixir
@spec update_selected_documentation(t(), String.t()) :: t()
```

Updates the documentation for the currently selected item.

Called when a `completionItem/resolve` response arrives with
the full documentation text.

# `visible_items`

```elixir
@spec visible_items(t()) :: {[item()], non_neg_integer()}
```

Returns `{visible_items, selected_offset}` for rendering.

`visible_items` is a window of at most `max_visible` items centered
around the selection. `selected_offset` is the index of the selected
item within that window.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
