Add Rope::insert_char_at_index()

This commit is contained in:
Matthew Gordon 2024-10-19 21:47:30 -03:00
parent 1164922ffe
commit c3ebef8fc1
1 changed files with 40 additions and 7 deletions

View File

@ -94,15 +94,33 @@ impl Rope {
} }
} }
pub fn concat(first: Rc<Rope>, second: Rc<Rope>) -> Rc<Rope> { /// Returns a new rope created from concatenating `other` onto the end of
Rope::join(first, Some(second)).rebalance() /// this one.
pub fn concat(self: Rc<Rope>, other: Rc<Rope>) -> Rc<Rope> {
Rope::join(self, Some(other)).rebalance()
}
/// Insert new text into the rope at a given character index.
pub fn insert_at_char_index(self: &Rc<Rope>, index: usize, text: impl Into<String>) -> Rc<Rope> {
let new_node = Rope::new(text);
let total_chars = self.total_chars();
if index == 0 {
new_node.concat(self.clone())
} else if index < total_chars {
let (before, after) = self.split_at_char_index(index);
before.concat(new_node).concat(after)
} else if index == total_chars {
self.clone().concat(new_node)
} else {
panic!("Attempt to insert past end of rope.")
}
} }
/// Split the rope in two at character `index`. /// Split the rope in two at character `index`.
/// ///
/// The result is two Ropes—one containing the first 'i' characters of the /// The result is two Ropes—one containing the first 'i' characters of the
/// text and the other containing the rest of the text. /// text and the other containing the rest of the text.
pub fn split(self: &Rc<Self>, index: usize) -> (Rc<Self>, Rc<Self>) { pub fn split_at_char_index(self: &Rc<Self>, index: usize) -> (Rc<Self>, Rc<Self>) {
match *self.as_ref() { match *self.as_ref() {
Rope::Branch { Rope::Branch {
chars_weight, chars_weight,
@ -111,14 +129,14 @@ impl Rope {
.. ..
} => { } => {
if index < chars_weight { if index < chars_weight {
let (first, second) = left.split(index); let (first, second) = left.split_at_char_index(index);
( (
first.rebalance(), first.rebalance(),
Rope::join(second, right.as_ref().map(|r| r.clone())).rebalance(), Rope::join(second, right.as_ref().map(|r| r.clone())).rebalance(),
) )
} else if let Some(right) = right { } else if let Some(right) = right {
if index > chars_weight { if index > chars_weight {
let (first, second) = right.split(index - chars_weight); let (first, second) = right.split_at_char_index(index - chars_weight);
( (
Rope::join(left.clone(), Some(first)).rebalance(), Rope::join(left.clone(), Some(first)).rebalance(),
second.rebalance(), second.rebalance(),
@ -430,7 +448,7 @@ mod tests {
let target = small_test_rope(); let target = small_test_rope();
let full_string = small_test_rope_full_string(); let full_string = small_test_rope_full_string();
for i in 0..(full_string.chars().count()) { for i in 0..(full_string.chars().count()) {
let (first, second) = target.split(i); let (first, second) = target.split_at_char_index(i);
let first_string: String = first.iter_chars().collect(); let first_string: String = first.iter_chars().collect();
let second_string: String = second.iter_chars().collect(); let second_string: String = second.iter_chars().collect();
assert_eq!(first_string, full_string[0..i]); assert_eq!(first_string, full_string[0..i]);
@ -446,7 +464,7 @@ mod tests {
.chars() .chars()
.collect(); .collect();
for i in 0..(expected_chars.len()) { for i in 0..(expected_chars.len()) {
let (first, second) = target.split(i); let (first, second) = target.split_at_char_index(i);
let string: String = first.iter_chars().collect(); let string: String = first.iter_chars().collect();
let expected: String = expected_chars[0..i].iter().collect(); let expected: String = expected_chars[0..i].iter().collect();
assert_eq!(string, expected); assert_eq!(string, expected);
@ -525,4 +543,19 @@ mod tests {
1 1
); );
} }
#[test]
fn insert_at_char_index() {
let target = Rope::new("The brown dog");
assert_eq!(target.iter_chars().collect::<String>(), "The brown dog");
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");
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");
assert_eq!(rope2.iter_chars().collect::<String>(), "The quick brown dog");
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.");
}
} }