# `MingaEditor.UI.Highlight`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/ui/highlight.ex#L1)

Stores and queries tree-sitter highlight state for a buffer.

Holds the current highlight spans (byte ranges + capture IDs),
capture names, version counter, and theme. Provides `styles_for_line/3`
to split a line into styled segments for rendering.

# `style_resolver`

```elixir
@type style_resolver() :: (String.t() -&gt; Minga.Core.Face.t())
```

Style resolver: a function that maps capture names to Face structs.

# `styled_segment`

```elixir
@type styled_segment() :: {text :: String.t(), style :: Minga.Core.Face.t()}
```

A styled text segment for rendering.

# `t`

```elixir
@type t() :: %MingaEditor.UI.Highlight{
  capture_names: tuple(),
  face_registry: MingaEditor.UI.Face.Registry.t(),
  spans: tuple() | [map()],
  theme: MingaEditor.UI.Theme.syntax(),
  version: non_neg_integer()
}
```

Highlight state for a buffer.

# `byte_offset_for_line`

```elixir
@spec byte_offset_for_line([String.t()], non_neg_integer()) :: non_neg_integer()
```

Computes the byte offset for a given line index within a list of lines.

Each line is separated by a newline (1 byte), so the offset is the
cumulative `byte_size` of all preceding lines plus their newlines.

# `from_theme`

```elixir
@spec from_theme(MingaEditor.UI.Theme.t()) :: t()
```

Creates an empty highlight state using the syntax map from a `MingaEditor.UI.Theme.t()` struct.

# `new`

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

Creates an empty highlight state with the default theme.

# `new`

```elixir
@spec new(MingaEditor.UI.Theme.syntax()) :: t()
```

Creates an empty highlight state with a syntax theme map.

Builds a `Face.Registry` from the syntax map so face inheritance
is always available.

# `put_names`

```elixir
@spec put_names(t(), [String.t()]) :: t()
```

Stores capture names from a `highlight_names` event.

# `put_spans`

```elixir
@spec put_spans(t(), non_neg_integer(), [
  MingaEditor.Frontend.Protocol.highlight_span()
]) :: t()
```

Stores highlight spans from a `highlight_spans` event.

Only updates if the incoming version is >= the current version,
preventing stale async results from overwriting newer ones.

# `resolver_for`

```elixir
@spec resolver_for(MingaEditor.UI.Face.Registry.t()) :: style_resolver()
```

Returns a style resolver function for the given face registry.

Use this to pass to `styles_for_line/4` when you want to override
the Highlight struct's built-in face registry (e.g., with buffer-local
face overrides applied).

# `styles_for_line`

```elixir
@spec styles_for_line(t(), String.t(), non_neg_integer(), style_resolver() | nil) :: [
  styled_segment()
]
```

Splits a line into styled segments based on highlight spans.

Given a line's text and its starting byte offset within the buffer,
finds all overlapping spans and produces `[{text_segment, Face.t()}]`.
Unstyled regions get `Face.new()` as their style (anonymous face with
all fields nil).

# `styles_for_visible_lines`

```elixir
@spec styles_for_visible_lines(
  t(),
  [{String.t(), non_neg_integer()}],
  style_resolver() | nil
) :: [
  [styled_segment()]
]
```

Batch-compute styled segments for multiple consecutive lines in a single
pass over the span tuple. Returns a list of `[styled_segment()]` in the
same order as the input lines.

This is O(total_spans + total_overlapping_pairs) regardless of file size,
compared to O(spans × visible_lines) for repeated `styles_for_line/3` calls.
Use this for rendering visible lines.

Each element in `lines` is `{line_text, line_start_byte}`.

---

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