Skip to main content

Module rope_helpers

Module rope_helpers 

Source
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_length field 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 \n boundary that range_of includes 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_text test 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 + BlockOffsetIndex instead of an O(N) linear walk of all blocks.
recompute_all_frame_byte_ranges
Recompute Frame.byte_range for every frame in store.frames based on current block_offsets and 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 text to the end of the rope and register block_id at 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 \n boundary (only if the rope is already non-empty). Registers block_id at the resulting byte position. Returns that byte position. Used when insert_frame_uc creates 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 \n boundary 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 by block_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 one to_string() allocation replaces the O(N) per-block walk + per-block Cow<str> materialization that build_full_text / export_plain_text would otherwise do.
rope_insert_block_at
Insert text as a new block at byte_pos in the rope, prepending a \n boundary. Used by insert_table_uc to 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 \n inter-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 text at byte_offset_in_block inside the block identified by block_id. Looks up the block’s start in the rope via block_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_block and end_block by deleting the rope range [start_block.start + byte_so .. end_block.start + byte_eo) — i.e. the suffix of start_block, every block between (and their boundary newlines), and the prefix of end_block. Removes the index entries for every block strictly between start_block and end_block (inclusive of end_block itself); the surviving content lives in start_block. Shifts any blocks past end_block by 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 \n either 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’s byte_start and 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_frame to 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.