rope

A B-tree rope data structure for efficient text manipulation in MoonBit

rope
text
editor
data-structure
btree
moon add Teddy-Yangjiale/rope@0.1.0
Download zip
Version
0.1.0
License
Apache-2.0
Last updated
last month
Downloads
18
README

#Rope-MoonBit

A B-tree rope data structure library for MoonBit, designed for efficient text editing operations.

A rope is a tree-based data structure that represents text as a sequence of small strings, enabling O(log n) insertions and deletions even for very large documents. This makes ropes ideal for text editors, language servers, and any application that performs frequent edits on large texts.

#Features

  • O(log n) insert and remove at arbitrary positions
  • O(n) construction from a string or via RopeBuilder
  • Correct Unicode support: char indices and UTF-8 byte offsets
  • Line/char index conversions for editor protocol (LSP/DAP) compatibility
  • Lazy iterators for chunks, chars, and lines
  • Slice views without copying
  • Targets: wasm-gc, native, js

#Installation

Add to your moon.mod:

{ "deps": { "Teddy-Yangjiale/rope": "0.1.0" } }

Then run:

moon update moon install

#Quick Start

fn main {
// Create a rope from a string
let r = Rope::from_str("Hello, world!")
println(r.len_chars()) // 13
println(r.len_bytes()) // 13 (UTF-8 bytes)
println(r.len_lines()) // 1

// Efficient insert and remove
let r2 = r.insert(7, "beautiful ")
println(r2.to_string()) // "Hello, beautiful world!"

let r3 = r2.remove(7, 17)
println(r3.to_string()) // "Hello, world!"

// Split and concat
let (left, right) = r.split(7)
println(left.to_string()) // "Hello, "
println(right.to_string()) // "world!"
let rejoined = left.concat(right)
println(rejoined.to_string()) // "Hello, world!"
}

#API Reference

#Construction

FunctionDescription
Rope::new() -> RopeCreate an empty rope
Rope::from_str(s: String) -> RopeBuild a rope from a string
RopeBuilder::new() -> RopeBuilderCreate a builder for incremental construction
RopeBuilder::push(self, chunk: String) -> UnitAppend a chunk
RopeBuilder::build(self) -> RopeFinalize and build the rope

#Queries

FunctionDescription
Rope::len_chars(self) -> IntNumber of Unicode code points
Rope::len_bytes(self) -> IntNumber of UTF-8 bytes
Rope::len_lines(self) -> IntNumber of lines (newlines + 1)
Rope::is_empty(self) -> BoolWhether the rope is empty

#Editing

FunctionDescription
Rope::insert(self, char_idx: Int, text: String) -> RopeInsert text at char index
Rope::remove(self, start: Int, end_: Int) -> RopeRemove chars [start, end_)
Rope::append(self, text: String) -> RopeAppend text to the end
Rope::split(self, char_idx: Int) -> (Rope, Rope)Split into two ropes
Rope::concat(self, other: Rope) -> RopeConcatenate two ropes

#Index Conversion

FunctionDescription
Rope::char_at(self, char_idx: Int) -> CharCharacter at index
Rope::line_to_char(self, line_idx: Int) -> IntFirst char index of a line
Rope::char_to_line(self, char_idx: Int) -> IntLine number of a char
Rope::char_to_byte(self, char_idx: Int) -> IntUTF-8 byte offset of a char
Rope::byte_to_char(self, byte_idx: Int) -> IntChar index at a UTF-8 byte offset

#Slices

FunctionDescription
Rope::slice(self, start: Int, end_: Int) -> RopeSliceView of [start, end_)
Rope::line(self, line_idx: Int) -> RopeSliceView of a single line
RopeSlice::to_string(self) -> StringMaterialize slice to string
RopeSlice::len_chars(self) -> IntLength of slice in chars

#Iterators

FunctionDescription
Rope::chunks(self) -> Iter[String]Iterate over leaf string chunks
Rope::chars(self) -> Iter[Char]Iterate over all characters
Rope::lines(self) -> Iter[RopeSlice]Iterate over lines as slices

#Equality

FunctionDescription
Rope::op_equal(self, other: Rope) -> BoolContent equality

#RopeBuilder Example

Use RopeBuilder when constructing a rope from many pieces — it builds the B-tree once at build() time, avoiding repeated tree reconstruction:

fn load_file_lines(lines: Array[String]) -> Rope {
let builder = RopeBuilder::new()
for line in lines {
builder.push(line)
builder.push("\n")
}
builder.build()
}

#Unicode Example

Char indices are Unicode code points, not bytes:

fn unicode_demo {
let r = Rope::from_str("你好世界")
println(r.len_chars()) // 4 (code points)
println(r.len_bytes()) // 12 (UTF-8 bytes)
println(r.char_at(0)) // '你'
println(r.char_to_byte(1)) // 3 (second char starts at byte 3)
}

#Running the Example

git clone https://github.com/Teddy-Yangjiale/Rope-Moonbit cd Rope-Moonbit moon run examples

#Running Tests

moon test

#Design

The rope is a B-tree where:
  • Leaves hold UTF-16 string chunks (up to 256 code units each)
  • Internal nodes hold up to 8 children with pre-computed metrics (bytes, chars, newlines)
  • All operations produce new nodes via structural sharing (persistent/immutable style)

This design gives O(log n) worst-case for insert/remove and O(n) for construction.

#License

Apache-2.0. See LICENSE.