Maps source UTF-8 byte boundaries to composed UTF-16 boundaries and back.
The map stores one span per composition edit plus the unchanged source spans between edits. It does not allocate an entry per grapheme, so an unmodified long line has one span.
Summary
Functions
Maps a composed UTF-16 boundary to a source UTF-8 byte boundary.
Resolves nondecreasing composed boundaries in one forward scan.
Creates a map for composition without tab expansion.
Creates a map for one composed logical line.
Maps a source byte boundary to a composed UTF-16 boundary.
Types
@type affinity() :: :start | :end
@type insertion() :: {non_neg_integer(), non_neg_integer()}
@type replacement() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}
@type span() :: {span_kind(), non_neg_integer(), non_neg_integer(), non_neg_integer(), non_neg_integer()}
@type span_kind() :: :source | :insertion | :replacement
@type t() :: %MingaEditor.RenderModel.Window.SourceOffsetMap{ composed_end_utf16: non_neg_integer(), composed_start_utf16: non_neg_integer(), insertions: [insertion()], replacements: [replacement()], source_end_byte: non_neg_integer(), source_start_byte: non_neg_integer(), source_text: String.t(), spans: [span()] }
Functions
@spec composed_utf16_to_source_byte(t(), non_neg_integer(), affinity()) :: non_neg_integer()
Maps a composed UTF-16 boundary to a source UTF-8 byte boundary.
Inserted text resolves to its source anchor. Inside replacement text,
:start resolves to the replaced range start and :end resolves to its end.
Inside a UTF-16 surrogate pair, affinity chooses the preceding or following
source boundary. Offsets outside the line are clamped.
@spec composed_utf16_to_source_bytes(t(), [{non_neg_integer(), affinity()}]) :: [ non_neg_integer() ]
Resolves nondecreasing composed boundaries in one forward scan.
This is the wrapping path for long lines. It preserves the semantics of
composed_utf16_to_source_byte/3 without rescanning an unchanged source
prefix for every visual row boundary.
@spec new(String.t(), String.t(), Minga.Core.Decorations.t(), non_neg_integer()) :: t()
Creates a map for composition without tab expansion.
@spec new( String.t(), String.t(), Minga.Core.Decorations.t(), non_neg_integer(), keyword() ) :: t()
Creates a map for one composed logical line.
:tab_width records source tabs as replacements expanded to the next tab
stop. Omit it when composed_text retains raw tabs.
@spec slice( t(), non_neg_integer(), non_neg_integer(), non_neg_integer(), non_neg_integer() ) :: t()
@spec source_to_composed_utf16(t(), non_neg_integer(), affinity()) :: non_neg_integer()
Maps a source byte boundary to a composed UTF-16 boundary.
At an insertion anchor, :start returns the boundary after inserted text and
:end returns the boundary before it. This keeps inserted presentation out
of source-backed accessibility ranges.