Write enough of the rope to append at least two nodes to the list
This commit is contained in:
parent
24d266ab34
commit
02af9e4ea7
|
@ -15,5 +15,5 @@ tui = { version = "0.19", default-features = false, features = [ "crosst
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "bench"
|
name = "bench"
|
||||||
main = "bin/bench.rs"
|
# main = "bin/bench.rs"
|
||||||
features = [ "bench" ]
|
# features = [ "bench" ]
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
use std::{mem, ops::Deref};
|
||||||
|
|
||||||
|
const CHUNK_SIZE: usize = 10;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct NodeId(usize);
|
||||||
|
|
||||||
|
impl Deref for NodeId {
|
||||||
|
type Target = usize;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Node {
|
||||||
|
Interior {
|
||||||
|
// the number of characters to the left of this node
|
||||||
|
// the number of lines to the left of this node
|
||||||
|
char_count: usize,
|
||||||
|
|
||||||
|
left: NodeId,
|
||||||
|
right: NodeId,
|
||||||
|
},
|
||||||
|
Leaf(LeafNode),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Node::Interior { char_count, .. } => 0,
|
||||||
|
Node::Leaf(ln) => ln.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LeafNode(String);
|
||||||
|
|
||||||
|
impl LeafNode {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take(&mut self) -> String {
|
||||||
|
let content = mem::replace(&mut self.0, "".to_owned());
|
||||||
|
content
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for LeafNode {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
Self(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a Rope is a regular tree which adds on some extra behavior for dealing with a continuous data
|
||||||
|
// structure.
|
||||||
|
pub struct Rope {
|
||||||
|
node_count: usize,
|
||||||
|
contents: Vec<Option<Node>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rope {
|
||||||
|
pub fn insert_at(&mut self, loc: usize, text: String) {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, text: String) {
|
||||||
|
if self.node_count == 0 {
|
||||||
|
let node = Node::Leaf(LeafNode::from(text));
|
||||||
|
self.node_count += 1;
|
||||||
|
self.contents.push(Some(node));
|
||||||
|
} else {
|
||||||
|
// Let's grab a node. If it's a leaf node, we need to convert it into a interior node.
|
||||||
|
// In doing so, we need to move the current leaf node into a left-hand child and create
|
||||||
|
// the right-hand child.
|
||||||
|
//
|
||||||
|
// If it's an interior node, we'll dig deeper. Not doing any tree rebalancing at the
|
||||||
|
// moment.
|
||||||
|
//
|
||||||
|
// First thing we need to know is what mode of node we have. That's going to determine
|
||||||
|
// which branch of operations we go into. We want to do this without borrowing the data
|
||||||
|
// for anything more than the match.
|
||||||
|
// let mut node = mem::replace(&mut self.contents[0], None);
|
||||||
|
match self.contents[0] {
|
||||||
|
Some(Node::Interior { ref left, ref right, .. }) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
Some(Node::Leaf(_)) => {
|
||||||
|
let Some(Node::Leaf(mut ln)) = mem::replace(&mut self.contents[0], None) else {
|
||||||
|
panic!("Should never leave an empty space in the node list")
|
||||||
|
};
|
||||||
|
let contents = ln.take();
|
||||||
|
|
||||||
|
let lnode = Node::Leaf(LeafNode::from(contents));
|
||||||
|
let rnode = Node::Leaf(LeafNode::from(text));
|
||||||
|
|
||||||
|
let lnode_id = self.node_count;
|
||||||
|
let rnode_id = self.node_count + 1;
|
||||||
|
|
||||||
|
let interior_node = Node::Interior {
|
||||||
|
char_count: lnode.len(),
|
||||||
|
left: NodeId(lnode_id),
|
||||||
|
right: NodeId(rnode_id),
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = mem::replace(&mut self.contents[0], Some(interior_node));
|
||||||
|
|
||||||
|
self.node_count += 2;
|
||||||
|
self.contents.push(Some(lnode));
|
||||||
|
self.contents.push(Some(rnode));
|
||||||
|
}
|
||||||
|
None => panic!("Should never leave an empty space in the node list"),
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
let mut node = &mut self.contents[0];
|
||||||
|
if let Node::Leaf(s) = node {
|
||||||
|
let contents = s.take();
|
||||||
|
|
||||||
|
let lnode = Node::Leaf(LeafNode::from(contents));
|
||||||
|
let rnode = Node::Leaf(LeafNode::from(text));
|
||||||
|
self.contents.push(lnode);
|
||||||
|
self.contents.push(rnode);
|
||||||
|
let lnode_id = self.node_count;
|
||||||
|
let rnode_id = self.node_count + 1;
|
||||||
|
self.node_count += 2;
|
||||||
|
*node = Node::Interior{
|
||||||
|
char_count: 0,
|
||||||
|
left: NodeId(lnode_id),
|
||||||
|
right: NodeId(rnode_id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
let mut r = String::new();
|
||||||
|
let mut stack = vec![NodeId(0)];
|
||||||
|
|
||||||
|
while let Some(current_id) = stack.pop() {
|
||||||
|
let node = &self.contents[*current_id];
|
||||||
|
match node {
|
||||||
|
Some(Node::Interior{ left, right, .. }) => {
|
||||||
|
stack.push(*right);
|
||||||
|
stack.push(*left);
|
||||||
|
}
|
||||||
|
Some(Node::Leaf(ln)) => r.push_str(ln.as_str()),
|
||||||
|
None => panic!("Should never leave an empty space in the node list"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
// This can be optimized later. Do a traversal of each right node. We already have
|
||||||
|
// character counts of each left tree. Only count the length of the final right leaf.
|
||||||
|
self.contents.iter().fold(0, |acc, node| {
|
||||||
|
if let Some(Node::Leaf(s)) = node {
|
||||||
|
acc + s.len()
|
||||||
|
} else {
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn max_depth(&self) -> usize {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn node_count(&self) -> usize {
|
||||||
|
self.node_count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Rope {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
node_count: 0,
|
||||||
|
contents: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the initial rope. The simplest way is to split along lines and turn each line into its
|
||||||
|
// own leaf node.
|
||||||
|
impl From<String> for Rope {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
let mut rope = Rope::default();
|
||||||
|
#[allow(unused_assignments)]
|
||||||
|
let mut first = s.as_str();
|
||||||
|
let mut lst = s.as_str();
|
||||||
|
|
||||||
|
while lst.len() > CHUNK_SIZE {
|
||||||
|
(first, lst) = lst.split_at(CHUNK_SIZE);
|
||||||
|
println!("[{}] [{}]", first, lst);
|
||||||
|
rope.append(first.to_owned());
|
||||||
|
}
|
||||||
|
rope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_creates_a_rope_from_a_string() {
|
||||||
|
let content =
|
||||||
|
"This is some basic context which is much smaller than the rope is designed for."
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
let rope = Rope::from(content.clone());
|
||||||
|
|
||||||
|
assert_eq!(rope.to_string(), content);
|
||||||
|
assert_eq!(rope.len(), content.len());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod doc_rope;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
@ -14,6 +14,10 @@ impl Document {
|
||||||
Self { rows: contents }
|
Self { rows: contents }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn line(&self, id: usize) -> Option<&str> {
|
||||||
|
self.rows.get(id).map(|x| x.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn contents(&self) -> String {
|
pub fn contents(&self) -> String {
|
||||||
self.rows.join("\n")
|
self.rows.join("\n")
|
||||||
}
|
}
|
||||||
|
@ -117,7 +121,7 @@ mod test_utils {
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn with_file<F>(test: F)
|
pub fn with_moby_dick<F>(test: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(Document),
|
F: FnOnce(Document),
|
||||||
{
|
{
|
||||||
|
@ -163,12 +167,18 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_inserts_a_line() {
|
fn it_inserts_a_line() {
|
||||||
with_file(|mut doc| {
|
with_moby_dick(|mut doc| {
|
||||||
let mut cursor = Cursor::default();
|
let mut cursor = Cursor::default();
|
||||||
|
|
||||||
let num_lines = doc.row_count();
|
let num_lines = doc.row_count();
|
||||||
|
assert_eq!(
|
||||||
|
doc.line(num_lines - 3),
|
||||||
|
Some("subscribe to our email newsletter to hear about new eBooks.")
|
||||||
|
);
|
||||||
|
|
||||||
doc.new_line(&mut cursor);
|
doc.new_line(&mut cursor);
|
||||||
assert_eq!(doc.row_count(), num_lines + 1);
|
assert_eq!(doc.row_count(), num_lines + 1);
|
||||||
|
assert_eq!(doc.line(0), Some(""));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +187,7 @@ pub mod bench {
|
||||||
use super::{test_utils::*, *};
|
use super::{test_utils::*, *};
|
||||||
|
|
||||||
pub fn bench_insert_lines() {
|
pub fn bench_insert_lines() {
|
||||||
with_file(|doc| {
|
with_moby_dick(|doc| {
|
||||||
let performance = benchmark(
|
let performance = benchmark(
|
||||||
1000,
|
1000,
|
||||||
|| doc.clone(),
|
|| doc.clone(),
|
||||||
|
|
Loading…
Reference in New Issue