Current section
Files
Jump to
Current section
Files
src/lattice_text@text.erl
-module(lattice_text@text).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lattice_text/text.gleam").
-export([new/1, try_insert_with_delta/3, insert/3, insert_with_delta/3, try_delete_with_delta/2, delete/2, delete_with_delta/2, values/1, value/1, length/1, substring/3, try_substring/3, try_delete_range_with_delta/3, delete_range/3, delete_range_with_delta/3, try_replace_range_with_delta/4, replace_range/4, replace_range_with_delta/4, try_move_with_delta/3, move/3, move_with_delta/3, start_anchor/0, end_anchor/0, try_anchor_at/3, anchor_at/3, try_resolve_anchor/2, resolve_anchor/2, anchor_to_json/1, anchor_from_json/1, append/2, append_with_delta/2, compact/2, remove_forwardings/2, frontier/1, merge/2, to_json/1, from_json/1]).
-export_type([text/0, range_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" A plain-text CRDT backed by `lattice_sequence`.\n"
"\n"
" Text is stored as a sequence of single-grapheme items: every inserted\n"
" string is split into graphemes and each grapheme becomes one sequence\n"
" item, so indices, anchors, and `length` are all grapheme-based. Insert,\n"
" delete, merge, and delta operations delegate to `lattice_sequence`.\n"
" Use `lattice_sequence/sequence` directly when you need a generic list CRDT.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import lattice_core/replica_id\n"
" import lattice_text/text\n"
"\n"
" let doc = text.new(replica_id.new(\"node-a\"))\n"
" text.value(doc) // -> \"\"\n"
" ```\n"
).
-opaque text() :: {text, lattice_sequence@sequence:sequence(binary())}.
-type range_error() :: {range_out_of_bounds, integer(), integer(), integer()}.
-file("src/lattice_text/text.gleam", 41).
?DOC(" Create an empty text CRDT for a replica.\n").
-spec new(lattice_core@replica_id:replica_id()) -> text().
new(Replica_id) ->
{text, lattice_sequence@sequence:new(Replica_id)}.
-file("src/lattice_text/text.gleam", 502).
-spec insert_graphemes_with_delta(
list(binary()),
lattice_sequence@sequence:sequence(binary()),
integer()
) -> {ok,
{lattice_sequence@sequence:sequence(binary()),
lattice_sequence@sequence:sequence(binary())}} |
{error, lattice_sequence@sequence:insert_error()}.
insert_graphemes_with_delta(Graphemes, Seq, Index) ->
lattice_text_core@grapheme:insert_graphemes(
Graphemes,
Seq,
Index,
fun lattice_sequence@sequence:length/1,
fun lattice_sequence@sequence:try_insert_many_with_delta/3,
fun(Field@0, Field@1) -> {index_out_of_bounds, Field@0, Field@1} end
).
-file("src/lattice_text/text.gleam", 68).
?DOC(" Safely insert a value and return both the updated text and insertion delta.\n").
-spec try_insert_with_delta(text(), integer(), binary()) -> {ok,
{text(), text()}} |
{error, lattice_sequence@sequence:insert_error()}.
try_insert_with_delta(Text, Index, Value) ->
{text, Seq} = Text,
_pipe = Value,
_pipe@1 = gleam@string:to_graphemes(_pipe),
_pipe@2 = insert_graphemes_with_delta(_pipe@1, Seq, Index),
gleam@result:map(
_pipe@2,
fun(Pair) ->
{Updated, Delta} = Pair,
{{text, Updated}, {text, Delta}}
end
).
-file("src/lattice_text/text.gleam", 49).
?DOC(
" Insert a value at the visible character index.\n"
"\n"
" Panics with `IndexOutOfBounds` when `index` is outside `[0, length]`. Use\n"
" `try_insert_with_delta` to handle an untrusted index without crashing.\n"
).
-spec insert(text(), integer(), binary()) -> text().
insert(Text, Index, Value) ->
Updated@1 = case try_insert_with_delta(Text, Index, Value) of
{ok, {Updated, _}} -> Updated;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"insert"/utf8>>,
line => 50,
value => _assert_fail,
start => 1577,
'end' => 1654,
pattern_start => 1588,
pattern_end => 1610})
end,
Updated@1.
-file("src/lattice_text/text.gleam", 58).
?DOC(
" Insert a value and return both the updated text and insertion delta.\n"
"\n"
" Panics with `IndexOutOfBounds` when `index` is outside `[0, length]`. Use\n"
" `try_insert_with_delta` to handle an untrusted index without crashing.\n"
).
-spec insert_with_delta(text(), integer(), binary()) -> {text(), text()}.
insert_with_delta(Text, Index, Value) ->
Result@1 = case try_insert_with_delta(Text, Index, Value) of
{ok, Result} -> Result;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"insert_with_delta"/utf8>>,
line => 63,
value => _assert_fail,
start => 1992,
'end' => 2057,
pattern_start => 2003,
pattern_end => 2013})
end,
Result@1.
-file("src/lattice_text/text.gleam", 104).
?DOC(" Safely delete a value and return both the updated text and deletion delta.\n").
-spec try_delete_with_delta(text(), integer()) -> {ok, {text(), text()}} |
{error, lattice_sequence@sequence:delete_error()}.
try_delete_with_delta(Text, Index) ->
{text, Seq} = Text,
case lattice_sequence@sequence:try_delete_with_delta(Seq, Index) of
{ok, {Updated, Delta}} ->
{ok, {{text, Updated}, {text, Delta}}};
{error, Error} ->
{error, Error}
end.
-file("src/lattice_text/text.gleam", 88).
?DOC(
" Delete the value at the visible character index.\n"
"\n"
" Panics with `DeleteIndexOutOfBounds` when `index` is outside\n"
" `[0, length)`. Use `try_delete_with_delta` to handle an untrusted index\n"
" without crashing.\n"
).
-spec delete(text(), integer()) -> text().
delete(Text, Index) ->
Updated@1 = case try_delete_with_delta(Text, Index) of
{ok, {Updated, _}} -> Updated;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"delete"/utf8>>,
line => 89,
value => _assert_fail,
start => 2751,
'end' => 2821,
pattern_start => 2762,
pattern_end => 2784})
end,
Updated@1.
-file("src/lattice_text/text.gleam", 98).
?DOC(
" Delete a value and return both the updated text and deletion delta.\n"
"\n"
" Panics with `DeleteIndexOutOfBounds` when `index` is outside\n"
" `[0, length)`. Use `try_delete_with_delta` to handle an untrusted index\n"
" without crashing.\n"
).
-spec delete_with_delta(text(), integer()) -> {text(), text()}.
delete_with_delta(Text, Index) ->
Result@1 = case try_delete_with_delta(Text, Index) of
{ok, Result} -> Result;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"delete_with_delta"/utf8>>,
line => 99,
value => _assert_fail,
start => 3144,
'end' => 3202,
pattern_start => 3155,
pattern_end => 3165})
end,
Result@1.
-file("src/lattice_text/text.gleam", 116).
?DOC(" Return the visible graphemes as a list.\n").
-spec values(text()) -> list(binary()).
values(Text) ->
{text, Seq} = Text,
lattice_sequence@sequence:values(Seq).
-file("src/lattice_text/text.gleam", 122).
?DOC(" Return the visible text as a single string.\n").
-spec value(text()) -> binary().
value(Text) ->
_pipe = Text,
_pipe@1 = values(_pipe),
erlang:list_to_binary(_pipe@1).
-file("src/lattice_text/text.gleam", 138).
?DOC(
" Count the visible graphemes in the text.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" text.new(replica_id.new(\"A\"))\n"
" |> text.insert(0, \"a👍\")\n"
" |> text.length()\n"
" // -> 2\n"
" ```\n"
).
-spec length(text()) -> integer().
length(Text) ->
{text, Seq} = Text,
lattice_sequence@sequence:length(Seq).
-file("src/lattice_text/text.gleam", 474).
-spec slice_values(text(), integer(), integer()) -> binary().
slice_values(Text, Start, End) ->
lattice_text_core@grapheme:slice(values(Text), Start, End).
-file("src/lattice_text/text.gleam", 154).
?DOC(
" Return the graphemes in `[start, end)`, clamping both indexes to the\n"
" text bounds. An empty range (including `start > end`) yields `\"\"`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" text.new(replica_id.new(\"A\"))\n"
" |> text.insert(0, \"abcd\")\n"
" |> text.substring(1, 3)\n"
" // -> \"bc\"\n"
" ```\n"
).
-spec substring(text(), integer(), integer()) -> binary().
substring(Text, Start, End) ->
Len = length(Text),
slice_values(
Text,
gleam@int:clamp(Start, 0, Len),
gleam@int:clamp(End, 0, Len)
).
-file("src/lattice_text/text.gleam", 462).
-spec validate_range(integer(), integer(), integer()) -> {ok, nil} |
{error, range_error()}.
validate_range(Start, End, Length) ->
gleam@bool:guard(
((Start < 0) orelse (End > Length)) orelse (Start > End),
{error, {range_out_of_bounds, Start, End, Length}},
fun() -> {ok, nil} end
).
-file("src/lattice_text/text.gleam", 170).
?DOC(
" Return the graphemes in `[start, end)`, or an error when the range does\n"
" not satisfy `0 <= start <= end <= length`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" text.new(replica_id.new(\"A\"))\n"
" |> text.insert(0, \"abc\")\n"
" |> text.try_substring(0, 4)\n"
" // -> Error(text.RangeOutOfBounds(start: 0, end: 4, length: 3))\n"
" ```\n"
).
-spec try_substring(text(), integer(), integer()) -> {ok, binary()} |
{error, range_error()}.
try_substring(Text, Start, End) ->
gleam@result:'try'(
validate_range(Start, End, length(Text)),
fun(_use0) ->
nil = _use0,
{ok, slice_values(Text, Start, End)}
end
).
-file("src/lattice_text/text.gleam", 491).
-spec delete_grapheme(lattice_sequence@sequence:sequence(binary()), integer()) -> {ok,
{lattice_sequence@sequence:sequence(binary()),
lattice_sequence@sequence:sequence(binary())}} |
{error, range_error()}.
delete_grapheme(Seq, Index) ->
case lattice_sequence@sequence:try_delete_with_delta(Seq, Index) of
{ok, Pair} ->
{ok, Pair};
{error, {delete_index_out_of_bounds, Index@1, Length}} ->
{error, {range_out_of_bounds, Index@1, Index@1, Length}}
end.
-file("src/lattice_text/text.gleam", 483).
-spec delete_graphemes_with_delta(
lattice_sequence@sequence:sequence(binary()),
integer(),
integer()
) -> {ok,
{lattice_sequence@sequence:sequence(binary()),
lattice_sequence@sequence:sequence(binary())}} |
{error, range_error()}.
delete_graphemes_with_delta(Seq, Start, End) ->
lattice_text_core@grapheme:delete_graphemes(
Seq,
Start,
End,
fun delete_grapheme/2,
fun lattice_sequence@sequence:merge/2
).
-file("src/lattice_text/text.gleam", 217).
?DOC(
" Safely delete a grapheme range and return both the updated text and\n"
" deletion delta.\n"
).
-spec try_delete_range_with_delta(text(), integer(), integer()) -> {ok,
{text(), text()}} |
{error, range_error()}.
try_delete_range_with_delta(Text, Start, End) ->
{text, Seq} = Text,
gleam@result:'try'(
validate_range(Start, End, lattice_sequence@sequence:length(Seq)),
fun(_use0) ->
nil = _use0,
_pipe = delete_graphemes_with_delta(Seq, Start, End),
gleam@result:map(
_pipe,
fun(Pair) ->
{Updated, Delta} = Pair,
{{text, Updated}, {text, Delta}}
end
)
end
).
-file("src/lattice_text/text.gleam", 194).
?DOC(
" Delete the graphemes in `[start, end)`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" text.new(replica_id.new(\"A\"))\n"
" |> text.insert(0, \"abcd\")\n"
" |> text.delete_range(1, 3)\n"
" |> text.value()\n"
" // -> \"ad\"\n"
" ```\n"
"\n"
" Panics with `RangeOutOfBounds` when `[start, end)` is not a valid range in\n"
" `[0, length]`. Use `try_delete_range_with_delta` to handle untrusted\n"
" bounds without crashing.\n"
).
-spec delete_range(text(), integer(), integer()) -> text().
delete_range(Text, Start, End) ->
Updated@1 = case try_delete_range_with_delta(Text, Start, End) of
{ok, {Updated, _}} -> Updated;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"delete_range"/utf8>>,
line => 195,
value => _assert_fail,
start => 5581,
'end' => 5666,
pattern_start => 5592,
pattern_end => 5614})
end,
Updated@1.
-file("src/lattice_text/text.gleam", 206).
?DOC(
" Delete a grapheme range and return both the updated text and deletion\n"
" delta.\n"
"\n"
" Panics with `RangeOutOfBounds` when `[start, end)` is not a valid range in\n"
" `[0, length]`. Use `try_delete_range_with_delta` to handle untrusted\n"
" bounds without crashing.\n"
).
-spec delete_range_with_delta(text(), integer(), integer()) -> {text(), text()}.
delete_range_with_delta(Text, Start, End) ->
Result@1 = case try_delete_range_with_delta(Text, Start, End) of
{ok, Result} -> Result;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"delete_range_with_delta"/utf8>>,
line => 211,
value => _assert_fail,
start => 6045,
'end' => 6114,
pattern_start => 6056,
pattern_end => 6066})
end,
Result@1.
-file("src/lattice_text/text.gleam", 478).
-spec insert_error_to_range_error(lattice_sequence@sequence:insert_error()) -> range_error().
insert_error_to_range_error(Error) ->
{index_out_of_bounds, Index, Length} = Error,
{range_out_of_bounds, Index, Index, Length}.
-file("src/lattice_text/text.gleam", 270).
?DOC(
" Safely replace a grapheme range and return both the updated text and\n"
" replacement delta.\n"
).
-spec try_replace_range_with_delta(text(), integer(), integer(), binary()) -> {ok,
{text(), text()}} |
{error, range_error()}.
try_replace_range_with_delta(Text, Start, End, Value) ->
{text, Seq} = Text,
gleam@result:'try'(
validate_range(Start, End, lattice_sequence@sequence:length(Seq)),
fun(_use0) ->
nil = _use0,
gleam@result:'try'(
delete_graphemes_with_delta(Seq, Start, End),
fun(_use0@1) ->
{Deleted, Delete_delta} = _use0@1,
Graphemes = gleam@string:to_graphemes(Value),
gleam@result:'try'(
begin
_pipe = insert_graphemes_with_delta(
Graphemes,
Deleted,
Start
),
gleam@result:map_error(
_pipe,
fun insert_error_to_range_error/1
)
end,
fun(_use0@2) ->
{Updated, Insert_delta} = _use0@2,
Delta = case {Start =:= End, Graphemes} of
{true, []} ->
Updated;
{true, _} ->
Insert_delta;
{false, []} ->
Delete_delta;
{false, _} ->
lattice_sequence@sequence:merge(
Delete_delta,
Insert_delta
)
end,
{ok, {{text, Updated}, {text, Delta}}}
end
)
end
)
end
).
-file("src/lattice_text/text.gleam", 246).
?DOC(
" Replace the graphemes in `[start, end)` with a value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" text.new(replica_id.new(\"A\"))\n"
" |> text.insert(0, \"abcd\")\n"
" |> text.replace_range(1, 3, \"XY\")\n"
" |> text.value()\n"
" // -> \"aXYd\"\n"
" ```\n"
"\n"
" Panics with `RangeOutOfBounds` when `[start, end)` is not a valid range in\n"
" `[0, length]`. Use `try_replace_range_with_delta` to handle untrusted\n"
" bounds without crashing.\n"
).
-spec replace_range(text(), integer(), integer(), binary()) -> text().
replace_range(Text, Start, End, Value) ->
Updated@1 = case try_replace_range_with_delta(Text, Start, End, Value) of
{ok, {Updated, _}} -> Updated;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"replace_range"/utf8>>,
line => 247,
value => _assert_fail,
start => 7092,
'end' => 7185,
pattern_start => 7103,
pattern_end => 7125})
end,
Updated@1.
-file("src/lattice_text/text.gleam", 258).
?DOC(
" Replace a grapheme range and return both the updated text and\n"
" replacement delta.\n"
"\n"
" Panics with `RangeOutOfBounds` when `[start, end)` is not a valid range in\n"
" `[0, length]`. Use `try_replace_range_with_delta` to handle untrusted\n"
" bounds without crashing.\n"
).
-spec replace_range_with_delta(text(), integer(), integer(), binary()) -> {text(),
text()}.
replace_range_with_delta(Text, Start, End, Value) ->
Result@1 = case try_replace_range_with_delta(Text, Start, End, Value) of
{ok, Result} -> Result;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"replace_range_with_delta"/utf8>>,
line => 264,
value => _assert_fail,
start => 7587,
'end' => 7664,
pattern_start => 7598,
pattern_end => 7608})
end,
Result@1.
-file("src/lattice_text/text.gleam", 337).
?DOC(
" Safely move a grapheme and return both the updated text and move delta.\n"
"\n"
" The `to_index` is interpreted after removing the grapheme from\n"
" `from_index`.\n"
).
-spec try_move_with_delta(text(), integer(), integer()) -> {ok,
{text(), text()}} |
{error, lattice_sequence@sequence:move_error()}.
try_move_with_delta(Text, From_index, To_index) ->
{text, Seq} = Text,
case lattice_sequence@sequence:try_move_with_delta(
Seq,
From_index,
To_index
) of
{ok, {Updated, Delta}} ->
{ok, {{text, Updated}, {text, Delta}}};
{error, Error} ->
{error, Error}
end.
-file("src/lattice_text/text.gleam", 314).
?DOC(
" Move the grapheme at `from_index` to `to_index`.\n"
"\n"
" The `to_index` is interpreted after removing the grapheme from\n"
" `from_index`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" text.new(replica_id.new(\"A\"))\n"
" |> text.insert(0, \"abc\")\n"
" |> text.move(0, 2)\n"
" |> text.value()\n"
" // -> \"bca\"\n"
" ```\n"
"\n"
" Panics with a `MoveError` when either index is out of bounds. Use\n"
" `try_move_with_delta` to handle untrusted indices without crashing.\n"
).
-spec move(text(), integer(), integer()) -> text().
move(Text, From_index, To_index) ->
Updated@1 = case try_move_with_delta(Text, From_index, To_index) of
{ok, {Updated, _}} -> Updated;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"move"/utf8>>,
line => 315,
value => _assert_fail,
start => 9073,
'end' => 9160,
pattern_start => 9084,
pattern_end => 9106})
end,
Updated@1.
-file("src/lattice_text/text.gleam", 324).
?DOC(
" Move a grapheme and return both the updated text and move delta.\n"
"\n"
" Panics with a `MoveError` when either index is out of bounds. Use\n"
" `try_move_with_delta` to handle untrusted indices without crashing.\n"
).
-spec move_with_delta(text(), integer(), integer()) -> {text(), text()}.
move_with_delta(Text, From_index, To_index) ->
Result@1 = case try_move_with_delta(Text, From_index, To_index) of
{ok, Result} -> Result;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"move_with_delta"/utf8>>,
line => 329,
value => _assert_fail,
start => 9486,
'end' => 9557,
pattern_start => 9497,
pattern_end => 9507})
end,
Result@1.
-file("src/lattice_text/text.gleam", 350).
?DOC(" Create an anchor at the start of the text. Always resolves to 0.\n").
-spec start_anchor() -> lattice_sequence@sequence:anchor().
start_anchor() ->
lattice_sequence@sequence:start_anchor().
-file("src/lattice_text/text.gleam", 356).
?DOC(
" Create an anchor at the end of the text. Always resolves to the current\n"
" grapheme length, tracking growth.\n"
).
-spec end_anchor() -> lattice_sequence@sequence:anchor().
end_anchor() ->
lattice_sequence@sequence:end_anchor().
-file("src/lattice_text/text.gleam", 388).
?DOC(
" Safely create an anchor at the gap before the grapheme at `index`.\n"
"\n"
" Valid positions are `0 <= index <= length`.\n"
).
-spec try_anchor_at(text(), integer(), lattice_sequence@sequence:bias()) -> {ok,
lattice_sequence@sequence:anchor()} |
{error, lattice_sequence@sequence:anchor_error()}.
try_anchor_at(Text, Index, Bias) ->
{text, Seq} = Text,
lattice_sequence@sequence:try_anchor_at(Seq, Index, Bias).
-file("src/lattice_text/text.gleam", 376).
?DOC(
" Create an anchor at the gap before the grapheme at `index`.\n"
"\n"
" Anchors are stable positions that survive concurrent edits and merges:\n"
" resolve one back to a current grapheme index with `resolve_anchor`.\n"
" `Before` bias glues the anchor to the grapheme at `index`, so inserts at\n"
" the gap push it right; `After` bias glues it to the grapheme at\n"
" `index - 1`, so inserts at the gap land after it.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let doc = text.new(replica_id.new(\"A\")) |> text.insert(0, \"hello\")\n"
" let cursor = text.anchor_at(doc, 5, sequence.After)\n"
" let doc = text.insert(doc, 0, \"say \")\n"
" text.resolve_anchor(doc, cursor) // -> 9\n"
" ```\n"
).
-spec anchor_at(text(), integer(), lattice_sequence@sequence:bias()) -> lattice_sequence@sequence:anchor().
anchor_at(Text, Index, Bias) ->
Anchor@1 = case try_anchor_at(Text, Index, Bias) of
{ok, Anchor} -> Anchor;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"anchor_at"/utf8>>,
line => 381,
value => _assert_fail,
start => 11144,
'end' => 11200,
pattern_start => 11155,
pattern_end => 11165})
end,
Anchor@1.
-file("src/lattice_text/text.gleam", 421).
?DOC(
" Safely resolve an anchor to a current grapheme index in `[0, length]`.\n"
"\n"
" Anchors to compacted graphemes resolve through the forwarding map to the\n"
" gap the grapheme left behind — semantically the same as tombstone\n"
" collapse.\n"
"\n"
" Returns `Error(UnknownAnchorTarget)` when the anchor references a\n"
" grapheme this replica has never seen (created remotely and not yet\n"
" merged), or one that was compacted away and whose forwarding entry has\n"
" since been removed by the host's retention policy. Either way the anchor\n"
" is unusable and the holder should re-anchor.\n"
).
-spec try_resolve_anchor(text(), lattice_sequence@sequence:anchor()) -> {ok,
integer()} |
{error, lattice_sequence@sequence:anchor_error()}.
try_resolve_anchor(Text, Anchor) ->
{text, Seq} = Text,
lattice_sequence@sequence:try_resolve(Seq, Anchor).
-file("src/lattice_text/text.gleam", 405).
?DOC(
" Resolve an anchor to a current grapheme index in `[0, length]`.\n"
"\n"
" Anchors on deleted graphemes still resolve: they collapse to the gap\n"
" where the grapheme used to be. Anchors follow moved graphemes. Panics\n"
" with `UnknownAnchorTarget` when the target was never merged or was\n"
" compacted away and its forwarding has expired — hosts holding anchors\n"
" across compaction rounds should use `try_resolve_anchor` and treat\n"
" failure as \"re-anchor\".\n"
).
-spec resolve_anchor(text(), lattice_sequence@sequence:anchor()) -> integer().
resolve_anchor(Text, Anchor) ->
Index@1 = case try_resolve_anchor(Text, Anchor) of
{ok, Index} -> Index;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"lattice_text/text"/utf8>>,
function => <<"resolve_anchor"/utf8>>,
line => 406,
value => _assert_fail,
start => 12066,
'end' => 12121,
pattern_start => 12077,
pattern_end => 12086})
end,
Index@1.
-file("src/lattice_text/text.gleam", 430).
?DOC(" Encode an anchor as a self-describing JSON value.\n").
-spec anchor_to_json(lattice_sequence@sequence:anchor()) -> gleam@json:json().
anchor_to_json(Anchor) ->
lattice_sequence@sequence:anchor_to_json(Anchor).
-file("src/lattice_text/text.gleam", 435).
?DOC(" Decode an anchor from a JSON string produced by `anchor_to_json`.\n").
-spec anchor_from_json(binary()) -> {ok, lattice_sequence@sequence:anchor()} |
{error, gleam@json:decode_error()}.
anchor_from_json(Json_string) ->
lattice_sequence@sequence:anchor_from_json(Json_string).
-file("src/lattice_text/text.gleam", 453).
?DOC(
" Insert a value at the end of the text. Appending is always valid, so no\n"
" `try_` variant exists.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" text.new(replica_id.new(\"A\"))\n"
" |> text.insert(0, \"ab\")\n"
" |> text.append(\"cd\")\n"
" |> text.value()\n"
" // -> \"abcd\"\n"
" ```\n"
).
-spec append(text(), binary()) -> text().
append(Text, Value) ->
insert(Text, length(Text), Value).
-file("src/lattice_text/text.gleam", 458).
?DOC(" Append a value and return both the updated text and insertion delta.\n").
-spec append_with_delta(text(), binary()) -> {text(), text()}.
append_with_delta(Text, Value) ->
insert_with_delta(Text, length(Text), Value).
-file("src/lattice_text/text.gleam", 526).
?DOC(
" Compact everything at or below a stability frontier.\n"
"\n"
" Delegates to `sequence.compact`: stable tombstones are dropped, runs of\n"
" stable graphemes are merged into compact blocks, and every dropped ID\n"
" gets a forwarding entry so anchors and rebased operations still resolve.\n"
" See `lattice_sequence/sequence.compact` for the stability contract.\n"
).
-spec compact(text(), lattice_core@version_vector:version_vector()) -> {text(),
lattice_sequence@sequence:forwarding_map()}.
compact(Text, Stable) ->
{text, Seq} = Text,
{Compacted, Forwardings} = lattice_sequence@sequence:compact(Seq, Stable),
{{text, Compacted}, Forwardings}.
-file("src/lattice_text/text.gleam", 540).
?DOC(
" Remove previously emitted forwarding entries from the text.\n"
"\n"
" Forwardings are bounded by the host's retention policy: keep the map\n"
" returned by each `compact` round and expire old rounds by passing them\n"
" here.\n"
).
-spec remove_forwardings(text(), lattice_sequence@sequence:forwarding_map()) -> text().
remove_forwardings(Text, Map) ->
{text, Seq} = Text,
{text, lattice_sequence@sequence:remove_forwardings(Seq, Map)}.
-file("src/lattice_text/text.gleam", 546).
?DOC(" The stability frontier this text was last compacted at.\n").
-spec frontier(text()) -> lattice_core@version_vector:version_vector().
frontier(Text) ->
{text, Seq} = Text,
lattice_sequence@sequence:frontier(Seq).
-file("src/lattice_text/text.gleam", 552).
?DOC(" Merge two text CRDT states.\n").
-spec merge(text(), text()) -> text().
merge(A, B) ->
{text, A_seq} = A,
{text, B_seq} = B,
{text, lattice_sequence@sequence:merge(A_seq, B_seq)}.
-file("src/lattice_text/text.gleam", 559).
?DOC(" Encode text using the canonical sequence JSON envelope.\n").
-spec to_json(text()) -> gleam@json:json().
to_json(Text) ->
{text, Seq} = Text,
lattice_sequence@sequence:to_json(Seq, fun gleam@json:string/1).
-file("src/lattice_text/text.gleam", 565).
?DOC(" Decode text from the canonical sequence JSON envelope.\n").
-spec from_json(binary()) -> {ok, text()} | {error, gleam@json:decode_error()}.
from_json(Json_string) ->
case lattice_sequence@sequence:from_json(
Json_string,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, Seq} ->
{ok, {text, Seq}};
{error, Error} ->
{error, Error}
end.