Minga.Core.WrapMap (Minga v0.1.0)

Copy Markdown View Source

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.

Summary

Types

t()

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

A single visual row within a wrapped logical line.

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

Functions

Computes the wrap map for a list of logical lines.

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

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.

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

Types

t()

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

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

visual_row()

@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()

@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.

Functions

compute(lines, content_width, opts \\ [])

@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(vrow)

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

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

logical_to_visual(wrap_map, logical_line)

@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(wrap_map)

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

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