# `MingaEditor.DisplayMap`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/display_map.ex#L1)

Unified buffer-line-to-display-row mapping.

Merges per-window folds (`FoldMap`), per-buffer decoration folds
(`Decorations.FoldRegion`), virtual lines (`Decorations.VirtualText`
with `:above`/`:below` placement), and block decorations into a single
authoritative mapping.

The DisplayMap is the single source of truth for translating between
buffer line numbers and screen row positions. It replaces
`FoldMap.VisibleLines.compute/4` in the render pipeline.

## Design

The DisplayMap is a pure data structure built once per frame from:
1. Per-window `FoldMap` folds (code folds, per-window)
2. Per-buffer decoration folds (closed fold regions from `Decorations`)
3. Virtual lines from decorations (`:above`/`:below`)
4. Block decorations (custom-rendered lines between buffer lines)

The map produces a list of `entry()` tuples describing what to render
at each display row, compatible with the existing content rendering
pipeline.

# `entry`

```elixir
@type entry() ::
  {non_neg_integer(), :normal}
  | {non_neg_integer(), {:fold_start, pos_integer()}}
  | {non_neg_integer(),
     {:decoration_fold, Minga.Core.Decorations.FoldRegion.t()}}
  | {non_neg_integer(), {:virtual_line, Minga.Core.Decorations.VirtualText.t()}}
  | {non_neg_integer(),
     {:block, Minga.Core.Decorations.BlockDecoration.t(), non_neg_integer()}}
```

What to render at a display row.

- `{buf_line, :normal}` — render the buffer line normally
- `{buf_line, {:fold_start, hidden_count}}` — render with fold summary
- `{buf_line, {:decoration_fold, fold_region}}` — render with custom placeholder
- `{buf_line, {:virtual_line, virtual_text}}` — render a virtual line (no buffer line)
- `{buf_line, {:block, block_decoration, line_index}}` — render a block decoration row

# `t`

```elixir
@type t() :: %MingaEditor.DisplayMap{
  entries: [entry()],
  total_display_lines: non_neg_integer()
}
```

The computed display map for a viewport.

# `buf_line_for_display_row`

```elixir
@spec buf_line_for_display_row(t(), non_neg_integer()) :: non_neg_integer() | nil
```

Returns the buffer line at the given display row.

# `buffer_range`

```elixir
@spec buffer_range(t()) :: {non_neg_integer(), non_neg_integer()} | nil
```

Returns the buffer line range needed to fetch all visible lines.

# `compute`

```elixir
@spec compute(
  MingaEditor.FoldMap.t(),
  Minga.Core.Decorations.t(),
  non_neg_integer(),
  pos_integer(),
  non_neg_integer(),
  pos_integer()
) :: t() | nil
```

Computes the display map for a viewport.

Returns `nil` when there are no folds, decoration folds, virtual lines,
or block decorations, signaling the caller to use the faster sequential path.

## Arguments

- `fold_map` — per-window `FoldMap` (code folds)
- `decorations` — per-buffer `Decorations`
- `first_buf_line` — first buffer line to display (from viewport scroll)
- `visible_rows` — number of screen rows available
- `total_lines` — total lines in the buffer
- `content_width` — available width for block decoration render callbacks (default 80)

# `display_row_for_buf_line`

```elixir
@spec display_row_for_buf_line(t(), non_neg_integer()) :: non_neg_integer() | nil
```

Returns the display row for a buffer line, or nil if hidden.

# `next_visible_line`

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

Returns the next visible buffer line after the given line.

# `prev_visible_line`

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

Returns the previous visible buffer line before the given line.

# `required?`

```elixir
@spec required?(MingaEditor.FoldMap.t(), Minga.Core.Decorations.t()) :: boolean()
```

Returns true when folds, virtual lines, or block decorations require a display map.

# `to_visible_line_map`

```elixir
@spec to_visible_line_map(t()) :: [entry()]
```

Returns the entry list for the content renderer.

# `total_display_lines`

```elixir
@spec total_display_lines(
  MingaEditor.FoldMap.t(),
  Minga.Core.Decorations.t(),
  non_neg_integer(),
  pos_integer()
) :: non_neg_integer()
```

Total display lines for the entire buffer (scrollbar calculations).

---

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