In-progress implementation of rope

Just the beginnings of a rope implementation but I want to refactor
already so saving what I've got so far.
This commit is contained in:
Matthew Gordon 2024-10-17 19:32:57 -03:00
parent d967dfe405
commit 73168d3c0b
4 changed files with 210 additions and 0 deletions

6
Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "ged"
version = "0.1.0"
edition = "2021"
[dependencies]

1
src/core/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod rope;

198
src/core/rope.rs Normal file
View File

@ -0,0 +1,198 @@
use std::rc::Rc;
/// [Rope](https://en.wikipedia.org/wiki/Rope_(data_structure)) data structure
/// implementation.
///
/// This is the main data structure used to store text file contents while they
/// are being edited.
#[derive(Debug)]
pub struct Rope {
/// Total number of bytes in the string contained in the left subtree, or
/// the number of bytes in the string if this is a leaf.
bytes_weight: usize,
/// Total number of characters in the string contained in the left subtree,
/// or the number of characters in the string if this is a leaf.
chars_weight: usize,
/// Total number of line endings in the string contained in the left
/// subtree, or the number of line endings in the string if this is a leaf.
lines_weight: usize,
/// The children (subtrees) if this is a branch node, or the string fragment
/// if this is a leaf.
contents: NodeContents,
}
#[derive(Debug)]
pub enum NodeContents {
/// The children of a branch Rope node
Children {
/// The root of the left subtree
left: Rc<Rope>,
/// The root of the right subtree
right: Rc<Rope>,
},
/// The string fragment contained in a Rope leaf node
String(String),
}
impl Rope {
pub fn new(contents: impl Into<String>) -> Rc<Self> {
let string = contents.into();
let bytes_weight = string.len();
let chars_weight = string.chars().count();
let lines_weight = string.chars().filter(|&c| c == '\n').count();
Rc::new(Rope {
bytes_weight,
chars_weight,
lines_weight,
contents: NodeContents::String(string),
})
}
pub fn join(left: Rc<Rope>, right: Rc<Rope>) -> Rc<Self> {
Rc::new(Rope {
bytes_weight: left.total_bytes(),
chars_weight: left.total_chars(),
lines_weight: left.total_lines(),
contents: NodeContents::Children { left, right },
})
}
pub fn total_bytes(&self) -> usize {
if let NodeContents::Children { left, right } = &self.contents {
left.bytes_weight + right.bytes_weight
} else {
self.bytes_weight
}
}
pub fn total_chars(&self) -> usize {
if let NodeContents::Children { left, right } = &self.contents {
left.chars_weight + right.chars_weight
} else {
self.chars_weight
}
}
pub fn total_lines(&self) -> usize {
if let NodeContents::Children { left, right } = &self.contents {
left.lines_weight + right.lines_weight
} else {
self.lines_weight
}
}
pub fn is_leaf(&self) -> bool {
match self.contents {
NodeContents::Children { .. } => false,
NodeContents::String(_) => true,
}
}
fn iter_nodes(self: Rc<Self>) -> NodeIterator {
NodeIterator::new(self)
}
pub fn iter_chars(self: Rc<Self>) -> CharIterator {
CharIterator::new(self)
}
}
struct NodeIterator {
stack: Vec<Rc<Rope>>,
}
impl NodeIterator {
fn new(rope: Rc<Rope>) -> Self {
let mut result = NodeIterator { stack: vec![] };
result.push_tree(rope);
result
}
fn push_tree(&mut self, node: Rc<Rope>) {
let mut curr = node;
self.stack.push(curr.clone());
while let NodeContents::Children { left, .. } = &curr.contents {
self.stack.push(left.clone());
curr = left.clone();
}
}
}
impl Iterator for NodeIterator {
type Item = Rc<Rope>;
fn next(&mut self) -> Option<Self::Item> {
let curr = self.stack.pop()?;
if let NodeContents::Children { right, .. } = &curr.contents {
self.push_tree(right.clone());
self.next()
} else {
Some(curr)
}
}
}
pub struct CharIterator<'a> {
node_iterator: NodeIterator,
current_node: Rc<Rope>,
str_iterator: std::str::Chars<'a>
}
impl<'a> CharIterator<'a> {
fn new(rope: Rc<Rope>) -> Self{
let mut node_iterator = NodeIterator::new(rope);
let str_iterator = node_iterator.next().contents
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn node_iterator_for_single_node_returns_node_and_only_node() {
let target = Rope::new("The");
let result: Vec<_> = target.iter_nodes().collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].bytes_weight, 3);
assert_eq!(result[0].chars_weight, 3);
assert_eq!(result[0].lines_weight, 0);
match &result[0].contents {
NodeContents::String(text) => assert_eq!(text, "The"),
_ => panic!(),
}
}
#[test]
fn node_iterator_returns_nodes_in_correct_order() {
let strings = vec![
"abc", "def", "g", "hi", "jklm", "n", "op", "qr", "stuv", "xyz",
];
let target = Rope::join(
Rope::join(
Rope::join(Rope::new("abc"), Rope::new("def")),
Rope::join(Rope::new("g"), Rope::new("hi")),
),
Rope::join(
Rope::join(
Rope::join(Rope::new("jklm"), Rope::new("n")),
Rope::join(Rope::new("op"), Rope::new("qr")),
),
Rope::join(Rope::new("stuv"), Rope::new("xyz")),
),
);
let result: Vec<_> = target.iter_nodes().collect();
assert_eq!(result.len(), 10);
for (node, string) in result.iter().zip(strings) {
match &node.contents {
NodeContents::String(text) => assert_eq!(text, string),
_ => panic!(),
}
}
}
}

5
src/main.rs Normal file
View File

@ -0,0 +1,5 @@
mod core;
fn main() {
println!("Hello, world!");
}