Finding shape of things after leaving rope unfinished
This commit is contained in:
parent
fff7595b9a
commit
a621985347
|
|
@ -1,3 +1,2 @@
|
|||
pub mod rope;
|
||||
|
||||
mod fibbonacci;
|
||||
mod text_buffer;
|
||||
pub use text_buffer::TextBuffer;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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]
|
||||
Loading…
Reference in New Issue