Expand description
Helpers for writing the global character rope from use cases.
Each helper mutates store.rope and store.block_offsets
together so callers can stay oblivious to the underlying layout.
Read helpers (block_content_via_store) prefer rope bytes but
fall back to Block.plain_text when the rope is stale (e.g.
after an unmirrored use case). The fallback goes away in step
7c when Block.plain_text is removed.
Functions§
- block_
char_ length - Logical character count of a block — what the old
Block.text_lengthfield used to cache. Image anchors are stored as\u{FFFC}(one char, three bytes) inside the rope content, so the char count already covers them. Returns 0 for blocks not registered in the offset index. - block_
char_ to_ byte_ in_ block - Convert an in-block char offset to an in-block byte offset using the rope’s index. Both inputs and outputs are relative to the start of the block’s content (NOT absolute rope positions). The char offset is clamped to the block’s logical length so callers don’t need to pre-validate.
- block_
content_ via_ store - Read a block’s content from the global rope via
block_offsets, stripping the trailing\nboundary thatrange_ofincludes for non-last entries. Returns an empty string if the block isn’t registered in the offset index (e.g. a freshly-created block that hasn’t been spliced into the rope yet —setup_with_texttest docs use this path). - find_
block_ at_ char_ position - Locate which block contains a given absolute char position in the
document, returning
(block_id, char_offset_in_block, block_char_start)in O(log n) using the rope +BlockOffsetIndexinstead of an O(N) linear walk of all blocks. - recompute_
all_ frame_ byte_ ranges - Recompute
Frame.byte_rangefor every frame instore.framesbased on currentblock_offsetsand the frame tree structure. Plan §1.6 invariant: each frame’s byte_range is the (min_start, max_end) over all its descendant blocks, sub-frames, and table anchors+cells. - rope_
append_ block - Append
textto the end of the rope and registerblock_idat the byte position where the text starts. Returns that byte offset. - rope_
append_ empty_ block - Append a new empty block to the end of the rope, separating it
from any prior content with a
\nboundary (only if the rope is already non-empty). Registersblock_idat the resulting byte position. Returns that byte position. Used wheninsert_frame_uccreates a new top-level frame with a single empty block. - rope_
append_ table_ anchor - Append a U+FFFC table-anchor sentinel at the end of the rope and
register a
TableAnchor(table_id)marker. If the rope is already non-empty, prepends a\nboundary so the sentinel doesn’t run into the previous entry’s content. - rope_
delete_ in_ block - Delete bytes
[byte_start_in_block..byte_end_in_block)from inside the block identified byblock_id. Shifts subsequent block offsets by the deleted byte length. No-op for blocks not in the index. - rope_
flat_ text_ if_ simple - Fast-path full-document plain text: returns
Some(rope.to_string())iff the document is in the canonical flat layout — no table anchors in the offset index, single top-level frame. In that case the rope’s byte order is the same as the document-flow order, so oneto_string()allocation replaces the O(N) per-block walk + per-blockCow<str>materialization thatbuild_full_text/export_plain_textwould otherwise do. - rope_
insert_ block_ at - Insert
textas a new block atbyte_posin the rope, prepending a\nboundary. Used byinsert_table_ucto place cell blocks at the end of their containing top-level frame’s range (plan §1.6), rather than always at rope end. - rope_
insert_ block_ boundary - Append a single
\ninter-block boundary character to the end of the rope. Does NOT register a block — this is the sentinel between two adjacent blocks within the same frame (plan §1.4). - rope_
insert_ in_ block - Insert
textatbyte_offset_in_blockinside the block identified byblock_id. Looks up the block’s start in the rope viablock_offsets.range_of(), splices into the rope, and shifts subsequent block offsets by the inserted byte length. - rope_
insert_ table_ anchor - Insert a U+FFFC OBJECT REPLACEMENT CHARACTER sentinel in the rope
at the table-anchor position, registering a
TableAnchor(table_id)marker in the offset index (plan §1.6). - rope_
merge_ block_ range - Merge
start_blockandend_blockby deleting the rope range[start_block.start + byte_so .. end_block.start + byte_eo)— i.e. the suffix ofstart_block, every block between (and their boundary newlines), and the prefix ofend_block. Removes the index entries for every block strictly betweenstart_blockandend_block(inclusive ofend_blockitself); the surviving content lives instart_block. Shifts any blocks pastend_blockby the negative delta. - rope_
remove_ block - Remove a registered block from the rope: drops its content bytes
plus one boundary
\n(the one after, if the block has a successor; the one before, if it’s the last entry), removes the entry from the index, and shifts trailing entries by the negative byte delta. - rope_
remove_ table_ anchor - Remove a TableAnchor sentinel from the rope, undoing the effect
of
rope_insert_table_anchor. Looks up the anchor’s byte range (always 3 bytes for the U+FFFC plus 1 byte of inter-marker\neither before or after, depending on what’s adjacent), removes those 4 bytes from the rope, drops the entry, shifts trailing entries by -4. - rope_
replace_ block_ content - Replace the entire content of a registered block in the rope with
new_text. Preserves the block’sbyte_startand its trailing boundary\n(if any); subsequent entries shift by the net length delta. - rope_
reset - Reset the rope to empty and clear
block_offsets. Called by importers when they replace the entire document content. - rope_
split_ block - Split an existing block in the rope at
byte_offset_in_block: - top_
level_ frame_ end_ byte - Walks up
frame.parent_frameto find the top-level ancestor of the given frame, then returns the end byte of that top-level frame’s current rope range — i.e. the byte position where blocks belonging to that frame’s subtree (e.g. table cells per plan §1.6) should be inserted so they land BEFORE any following top-level frame’s content.