Finding shape of things after leaving rope unfinished

This commit is contained in:
Matthew Gordon 2025-10-21 22:12:25 -03:00
parent fff7595b9a
commit a621985347
6 changed files with 181 additions and 7 deletions

View File

@ -1,3 +1,2 @@
pub mod rope;
mod fibbonacci;
mod text_buffer;
pub use text_buffer::TextBuffer;

138
core/src/text_buffer/mod.rs Normal file
View File

@ -0,0 +1,138 @@
use std::rc::Rc;
mod rope;
use rope::Rope;
pub struct TextBuffer {
contents: Rc<Rope>,
}
pub enum Point {
End,
}
impl TextBuffer {
pub fn new() -> Self {
Self {
contents: Rope::empty(),
}
}
pub fn num_bytes(&self) -> usize {
self.contents.total_bytes()
}
pub fn num_chars(&self) -> usize {
self.contents.total_chars()
}
pub fn num_lines(&self) -> usize {
let num_chars = self.num_chars();
if num_chars == 0 {
0
} else {
dbg!(self.contents.total_lines())
+ if self
.contents
.get_char_at_index(self.contents.total_chars() - 1)
== '\n'
{
0
} else {
1
}
}
}
pub fn insert_char(&mut self, c: char, point: Point) {
match point {
Point::End => {
self.contents = self
.contents
.insert_at_char_index(self.contents.total_chars(), c)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_creates_empty_buffer() {
let target = TextBuffer::new();
assert_eq!(0, target.num_bytes());
assert_eq!(0, target.num_chars());
assert_eq!(0, target.num_lines());
}
#[test]
fn insert_char_at_end_increases_counts_as_expected() {
let mut target = TextBuffer::new();
target.insert_char('A', Point::End);
assert_eq!(1, target.num_bytes());
assert_eq!(1, target.num_chars());
assert_eq!(1, target.num_lines());
target.insert_char(' ', Point::End);
assert_eq!(2, target.num_bytes());
assert_eq!(2, target.num_chars());
assert_eq!(1, target.num_lines());
target.insert_char('c', Point::End);
assert_eq!(3, target.num_bytes());
assert_eq!(3, target.num_chars());
assert_eq!(1, target.num_lines());
target.insert_char('a', Point::End);
assert_eq!(4, target.num_bytes());
assert_eq!(4, target.num_chars());
assert_eq!(1, target.num_lines());
target.insert_char('t', Point::End);
assert_eq!(5, target.num_bytes());
assert_eq!(5, target.num_chars());
assert_eq!(1, target.num_lines());
target.insert_char('\n', Point::End);
assert_eq!(6, target.num_bytes());
assert_eq!(6, target.num_chars());
assert_eq!(1, target.num_lines());
target.insert_char('A', Point::End);
assert_eq!(7, target.num_bytes());
assert_eq!(7, target.num_chars());
assert_eq!(2, target.num_lines());
target.insert_char('\n', Point::End);
assert_eq!(8, target.num_bytes());
assert_eq!(8, target.num_chars());
assert_eq!(2, target.num_lines());
target.insert_char('\n', Point::End);
assert_eq!(9, target.num_bytes());
assert_eq!(9, target.num_chars());
assert_eq!(3, target.num_lines());
target.insert_char('*', Point::End);
assert_eq!(10, target.num_bytes());
assert_eq!(10, target.num_chars());
assert_eq!(4, target.num_lines());
target.insert_char('*', Point::End);
assert_eq!(11, target.num_bytes());
assert_eq!(11, target.num_chars());
assert_eq!(4, target.num_lines());
target.insert_char(' ', Point::End);
assert_eq!(12, target.num_bytes());
assert_eq!(12, target.num_chars());
assert_eq!(4, target.num_lines());
target.insert_char('猫', Point::End);
assert_eq!(15, target.num_bytes());
assert_eq!(13, target.num_chars());
assert_eq!(4, target.num_lines());
target.insert_char(' ', Point::End);
assert_eq!(16, target.num_bytes());
assert_eq!(14, target.num_chars());
assert_eq!(4, target.num_lines());
target.insert_char('\n', Point::End);
assert_eq!(17, target.num_bytes());
assert_eq!(15, target.num_chars());
assert_eq!(4, target.num_lines());
target.insert_char('_', Point::End);
assert_eq!(18, target.num_bytes());
assert_eq!(16, target.num_chars());
assert_eq!(5, target.num_lines());
}
}

View File

@ -1,6 +1,8 @@
use super::fibbonacci::fibbonacci;
use std::rc::Rc;
mod fibbonacci;
use fibbonacci::fibbonacci;
/// [Rope](https://en.wikipedia.org/wiki/Rope_(data_structure)) data structure
/// implementation.
///
@ -91,7 +93,29 @@ impl Rope {
right: Some(right),
..
} => lines_weight + right.total_lines(),
Rope::Leaf { text } => text.lines().count(),
Rope::Leaf { text } => text.chars().filter(|&c| c == '\n').count(),
}
}
/// Return the character as a given character index.
pub fn get_char_at_index(&self, index: usize) -> char {
match self {
Rope::Branch {
left, right: None, ..
} => left.get_char_at_index(index),
Rope::Branch {
chars_weight,
left,
right: Some(right),
..
} => {
if index < *chars_weight {
left.get_char_at_index(index)
} else {
right.get_char_at_index(index - chars_weight)
}
}
Rope::Leaf { text } => text.chars().nth(index).unwrap(),
}
}
@ -303,7 +327,7 @@ impl Iterator for CharIterator {
fn next(&mut self) -> Option<Self::Item> {
match self {
CharIterator(inner) => inner.next().map(|p| p.character)
CharIterator(inner) => inner.next().map(|p| p.character),
}
}
}

View File

@ -80,7 +80,7 @@ fn node_iterator_for_single_node_returns_node_and_only_node() {
assert_eq!(result.len(), 1);
assert_eq!(result[0].total_bytes(), 3);
assert_eq!(result[0].total_chars(), 3);
assert_eq!(result[0].total_lines(), 1);
assert_eq!(result[0].total_lines(), 0);
match result[0].as_ref() {
Rope::Leaf { text, .. } => assert_eq!(text, "The"),
_ => panic!(),
@ -217,13 +217,24 @@ fn depths_have_correct_values() {
);
}
#[test]
fn get_char_at_index() {
let target = small_test_rope_with_multibyte_chars();
let expected = small_test_rope_with_multibyte_chars_full_string();
for (i, c) in expected.chars().enumerate() {
assert_eq!(target.get_char_at_index(i), c);
}
}
#[test]
fn insert_at_char_index() {
let target = Rope::new("The brown dog");
assert_eq!(target.iter_chars().collect::<String>(), "The brown dog");
assert_eq!(0, target.total_lines());
let rope1 = target.insert_at_char_index(4, "quick");
assert_eq!(target.iter_chars().collect::<String>(), "The brown dog");
assert_eq!(rope1.iter_chars().collect::<String>(), "The quickbrown dog");
assert_eq!(0, rope1.total_lines());
let rope2 = rope1.insert_at_char_index(9, " ");
assert_eq!(target.iter_chars().collect::<String>(), "The brown dog");
assert_eq!(rope1.iter_chars().collect::<String>(), "The quickbrown dog");
@ -231,12 +242,14 @@ fn insert_at_char_index() {
rope2.iter_chars().collect::<String>(),
"The quick brown dog"
);
assert_eq!(0, rope2.total_lines());
let rope3 =
rope2.insert_at_char_index("The quick brown dog".len(), " jumps over the lazy fox.");
assert_eq!(
rope3.iter_chars().collect::<String>(),
"The quick brown dog jumps over the lazy fox."
);
assert_eq!(0, rope3.total_lines());
}
#[test]