# `Minga.Core.WrapMap`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/core/wrap_map.ex#L1)

Computes visual line breaks for soft word-wrapping.

Given a list of logical lines and a content width, produces a wrap map
that tells the renderer how to split each logical line across multiple
screen rows. All computation is pure and stateless; the wrap map is
recomputed per frame for visible lines only (typically 30-50 lines),
so performance is not a concern.

## Design

- **Break at word boundaries.** Scans graphemes left to right, tracking
  display width. When the next grapheme would exceed the content width,
  breaks at the last whitespace position. If no whitespace exists in
  the line (a single long token), breaks at the content width exactly.
- **Breakindent.** Continuation rows preserve the logical line's leading
  whitespace, keeping indented code visually coherent.
- **No buffer mutations.** Wrapping is a rendering concern. The buffer
  stores logical lines unchanged.

# `t`

```elixir
@type t() :: [wrap_entry()]
```

A wrap map: one entry per logical line in the visible range.

# `visual_row`

```elixir
@type visual_row() :: %{
  text: String.t(),
  source_text: String.t(),
  byte_offset: non_neg_integer(),
  indent_width: non_neg_integer()
}
```

A single visual row within a wrapped logical line.

- `text` — the raw slice of the logical line for this visual row
- `source_text` — the slice of the logical line for this visual row, excluding artificial indent
- `byte_offset` — byte offset from the start of the logical line
- `indent_width` — artificial display columns prefixed before `source_text`

# `wrap_entry`

```elixir
@type wrap_entry() :: [visual_row()]
```

Wrap entry for one logical line: a list of visual rows it expands to.
A non-wrapping line produces a single-element list.

# `compute`

```elixir
@spec compute([String.t()], pos_integer(), keyword()) :: t()
```

Computes the wrap map for a list of logical lines.

`content_width` is the number of display columns available for text
(viewport cols minus gutter width). When `breakindent` is true,
continuation rows are indented to match the logical line's leading
whitespace.

Returns a list with one `wrap_entry` per input line. Each entry is a
list of `visual_row` maps, one per screen row the line occupies.

# `display_text`

```elixir
@spec display_text(visual_row() | map()) :: String.t()
```

Returns the display text for a visual row, including any breakindent prefix.

# `logical_to_visual`

```elixir
@spec logical_to_visual(t(), non_neg_integer()) :: non_neg_integer()
```

Converts a logical line index to the visual row offset from the start
of the wrap map. Returns the screen row where the logical line begins.

# `visual_row_count`

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

Returns the total number of visual rows across all entries in a wrap map.

---

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