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:
parent
d967dfe405
commit
73168d3c0b
|
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "ged"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
|
@ -0,0 +1 @@
|
|||
pub mod rope;
|
||||
|
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
mod core;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
Loading…
Reference in New Issue