lattice_text_core/grapheme
Backend-agnostic grapheme and range helpers shared by lattice text CRDTs.
A text CRDT is a sequence CRDT of single-grapheme values plus a thin layer
of grapheme/range bookkeeping: splitting inserted strings into graphemes,
validating [start, end) ranges, slicing out substrings, and folding
multi-grapheme inserts/deletes into a single mergeable delta. That layer is
identical regardless of which sequence CRDT stores the graphemes, so it
lives here and is shared by lattice_text (backed by lattice_sequence)
and lattice_text_fugue (backed by lattice_fugue).
The multi-grapheme fold helpers are generic over the backend’s state type
s and take the backend’s insert, delete, and merge as ordinary
function arguments, so no dependency on any particular sequence package is
needed.
Types
An error returned when a grapheme range does not satisfy
0 <= start <= end <= length.
pub type RangeError {
RangeOutOfBounds(start: Int, end: Int, length: Int)
}
Constructors
-
RangeOutOfBounds(start: Int, end: Int, length: Int)
Values
pub fn delete_graphemes(
state: s,
start: Int,
end: Int,
delete: fn(s, Int) -> Result(#(s, s), e),
merge: fn(s, s) -> s,
) -> Result(#(s, s), e)
Delete the graphemes in [start, end) one at a time, threading a merged
delta of every deletion.
Generic over the backend state s and delete-error e:
deletedeletes the single grapheme at an index, returning the updated state and its delta, or the backend’s own error.mergejoins two deltas.
Repeatedly deletes at start, since each deletion shifts the following
graphemes left. An empty range is a no-op whose delta is the unchanged
state.
pub fn insert_graphemes(
graphemes: List(String),
state: s,
index: Int,
length: fn(s) -> Int,
insert_many: fn(s, Int, List(String)) -> Result(#(s, s), e),
index_out_of_bounds: fn(Int, Int) -> e,
) -> Result(#(s, s), e)
Insert a list of graphemes at index as a single batched operation,
returning the updated state and one delta covering every inserted node.
Generic over the backend state s and insert-error e:
lengthreports the backend’s current visible length.insert_manyinserts the whole grapheme run starting atindex, returning the updated state and its combined delta, or a backend error.index_out_of_boundsbuilds the backend error for an invalidindex.
Returns Error (via index_out_of_bounds) when index is outside
[0, length], mirroring the backend’s own bounds contract even when the
grapheme list is empty. An empty grapheme list at a valid index is a no-op
whose delta is the unchanged state, and never reaches insert_many.
pub fn slice(
graphemes: List(String),
start: Int,
end: Int,
) -> String
Return the graphemes in [start, end) as a string.
Indexes are used as-is (no clamping); callers that need clamping or
validation should apply it first with validate_range.
pub fn validate_range(
start: Int,
end: Int,
length: Int,
) -> Result(Nil, RangeError)
Validate that [start, end) is a range within [0, length].
Examples
validate_range(1, 3, 4)
// -> Ok(Nil)
validate_range(0, 5, 3)
// -> Error(RangeOutOfBounds(start: 0, end: 5, length: 3))