Add Rope::split() and Rope::empty()
This commit is contained in:
parent
f85a771071
commit
a22968456c
188
src/core/rope.rs
188
src/core/rope.rs
|
|
@ -37,6 +37,12 @@ impl Rope {
|
||||||
Rc::new(Rope::Leaf { text })
|
Rc::new(Rope::Leaf { text })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new empty Rope
|
||||||
|
pub fn empty() -> Rc<Self> {
|
||||||
|
let text = "".into();
|
||||||
|
Rc::new(Rope::Leaf { text })
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the total number of bytes in the text.
|
/// Return the total number of bytes in the text.
|
||||||
pub fn total_bytes(&self) -> usize {
|
pub fn total_bytes(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -91,21 +97,69 @@ impl Rope {
|
||||||
/// Concatenate two Ropes without rebalancing.
|
/// Concatenate two Ropes without rebalancing.
|
||||||
///
|
///
|
||||||
/// Combines to ropes by creating a new parent node and adding `left` and
|
/// Combines to ropes by creating a new parent node and adding `left` and
|
||||||
/// `right` as children. Having this separate from [Rope::concatenate()] is mostly
|
/// `right` as children. If `right` is [None] then the resulting node will
|
||||||
/// useful for testing purposes.
|
/// only have one child.
|
||||||
fn join(left: Rc<Rope>, right: Rc<Rope>) -> Rc<Self> {
|
fn join(left: Rc<Rope>, right: Option<Rc<Rope>>) -> Rc<Self> {
|
||||||
Rc::new(Rope::Branch {
|
Rc::new(Rope::Branch {
|
||||||
bytes_weight: left.total_bytes(),
|
bytes_weight: left.total_bytes(),
|
||||||
chars_weight: left.total_chars(),
|
chars_weight: left.total_chars(),
|
||||||
lines_weight: left.total_lines(),
|
lines_weight: left.total_lines(),
|
||||||
left,
|
left,
|
||||||
right: Some(right),
|
right,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Split the rope in two at character `index`.
|
||||||
|
///
|
||||||
|
/// The result is two Ropes—one containing the first 'i' characters of the
|
||||||
|
/// text and the other containing the rest of the text.
|
||||||
|
pub fn split(self: &Rc<Self>, index: usize) -> (Rc<Self>, Rc<Self>) {
|
||||||
|
match *self.as_ref() {
|
||||||
|
Rope::Branch {
|
||||||
|
chars_weight,
|
||||||
|
ref left,
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if index < chars_weight {
|
||||||
|
let (first, second) = left.split(index);
|
||||||
|
(
|
||||||
|
first.rebalance(),
|
||||||
|
Rope::join(second, right.as_ref().map(|r| r.clone())).rebalance(),
|
||||||
|
)
|
||||||
|
} else if let Some(right) = right {
|
||||||
|
if index > chars_weight {
|
||||||
|
let (first, second) = right.split(index - chars_weight);
|
||||||
|
(
|
||||||
|
Rope::join(left.clone(), Some(first)).rebalance(),
|
||||||
|
second.rebalance(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(left.clone(), right.clone())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(left.clone(), Rope::empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rope::Leaf { ref text } => {
|
||||||
|
if let Some((byte_index, _)) = text.char_indices().nth(index) {
|
||||||
|
let (first, second) = text.split_at(byte_index);
|
||||||
|
(Rope::new(first), Rope::new(second))
|
||||||
|
} else {
|
||||||
|
(self.clone(), Rope::empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rebalance(self: Rc<Rope>) -> Rc<Rope> {
|
||||||
|
// TODO
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if this node is a leaf node, false otherwise
|
/// Returns true if this node is a leaf node, false otherwise
|
||||||
pub fn is_leaf(&self) -> bool {
|
pub fn is_leaf(&self) -> bool {
|
||||||
match self {
|
match &self {
|
||||||
Rope::Branch { .. } => false,
|
Rope::Branch { .. } => false,
|
||||||
Rope::Leaf { .. } => true,
|
Rope::Leaf { .. } => true,
|
||||||
}
|
}
|
||||||
|
|
@ -191,8 +245,7 @@ impl Iterator for CharIterator {
|
||||||
type Item = char;
|
type Item = char;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
dbg!(self
|
self.current_text
|
||||||
.current_text
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|text| {
|
.and_then(|text| {
|
||||||
let c = text.chars().nth(self.str_index);
|
let c = text.chars().nth(self.str_index);
|
||||||
|
|
@ -207,7 +260,7 @@ impl Iterator for CharIterator {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,6 +269,58 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ntest::timeout;
|
use ntest::timeout;
|
||||||
|
|
||||||
|
fn small_test_rope() -> Rc<Rope> {
|
||||||
|
Rope::join(
|
||||||
|
Rope::join(
|
||||||
|
Rope::join(Rope::new("abc"), Some(Rope::new("def"))),
|
||||||
|
Some(Rope::join(Rope::new("g"), Some(Rope::new("hi")))),
|
||||||
|
),
|
||||||
|
Some(Rope::join(
|
||||||
|
Rope::join(
|
||||||
|
Rope::join(Rope::new("jklm"), Some(Rope::new("n"))),
|
||||||
|
Some(Rope::join(Rope::new("op"), Some(Rope::new("qr")))),
|
||||||
|
),
|
||||||
|
Some(Rope::join(Rope::new("stuv"), Some(Rope::new("wxyz")))),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn small_test_rope_leaf_strings() -> Vec<&'static str> {
|
||||||
|
vec![
|
||||||
|
"abc", "def", "g", "hi", "jklm", "n", "op", "qr", "stuv", "wxyz",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn small_test_rope_full_string() -> &'static str {
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn small_test_rope_with_multibyte_chars() -> Rc<Rope> {
|
||||||
|
Rope::join(
|
||||||
|
Rope::join(
|
||||||
|
Rope::join(Rope::new("aあbc"), Some(Rope::new("deえf"))),
|
||||||
|
Some(Rope::join(Rope::new("g"), Some(Rope::new("hiい")))),
|
||||||
|
),
|
||||||
|
Some(Rope::join(
|
||||||
|
Rope::join(
|
||||||
|
Rope::join(Rope::new("jklm"), Some(Rope::new("n"))),
|
||||||
|
Some(Rope::join(Rope::new("おop"), Some(Rope::new("qr")))),
|
||||||
|
),
|
||||||
|
Some(Rope::join(Rope::new("stuうv"), Some(Rope::new("wxyz")))),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn small_test_rope_with_multibyte_chars_leaf_strings() -> Vec<&'static str> {
|
||||||
|
vec![
|
||||||
|
"aあbc", "deえf", "g", "hiい", "jklm", "n", "おop", "qr", "stuうv", "wxyz",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn small_test_rope_with_multibyte_chars_full_string() -> &'static str {
|
||||||
|
"aあbcdeえfghiいjklmnおopqrstuうvwxyz"
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[timeout(100)]
|
#[timeout(100)]
|
||||||
fn node_iterator_for_single_node_returns_node_and_only_node() {
|
fn node_iterator_for_single_node_returns_node_and_only_node() {
|
||||||
|
|
@ -235,22 +340,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[timeout(100)]
|
#[timeout(100)]
|
||||||
fn node_iterator_returns_nodes_in_correct_order() {
|
fn node_iterator_returns_nodes_in_correct_order() {
|
||||||
let strings = vec![
|
let strings = small_test_rope_leaf_strings();
|
||||||
"abc", "def", "g", "hi", "jklm", "n", "op", "qr", "stuv", "wxyz",
|
let target = small_test_rope();
|
||||||
];
|
|
||||||
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("wxyz")),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let result: Vec<_> = target.iter_nodes().take(26).collect();
|
let result: Vec<_> = target.iter_nodes().take(26).collect();
|
||||||
assert_eq!(result.len(), 10);
|
assert_eq!(result.len(), 10);
|
||||||
|
|
@ -265,25 +356,46 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[timeout(100)]
|
#[timeout(100)]
|
||||||
fn char_iterator_returns_chars_in_correct_order() {
|
fn char_iterator_returns_chars_in_correct_order() {
|
||||||
let target = Rope::join(
|
let target = small_test_rope();
|
||||||
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("wxyz")),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let result: Vec<_> = target.iter_chars().collect();
|
let result: Vec<_> = target.iter_chars().collect();
|
||||||
assert_eq!(result.len(), 26);
|
assert_eq!(result.len(), 26);
|
||||||
for (&target_char, expected_char) in result.iter().zip("abcdefghijklmnopqrstuvwxyz".chars())
|
for (&target_char, expected_char) in
|
||||||
|
result.iter().zip(small_test_rope_full_string().chars())
|
||||||
{
|
{
|
||||||
assert_eq!(target_char, expected_char);
|
assert_eq!(target_char, expected_char);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[timeout(100)]
|
||||||
|
fn split_splits_at_correct_location() {
|
||||||
|
let target = small_test_rope();
|
||||||
|
let full_string = small_test_rope_full_string();
|
||||||
|
for i in 0..(full_string.chars().count()) {
|
||||||
|
let (first, second) = target.split(i);
|
||||||
|
let first_string: String = first.iter_chars().collect();
|
||||||
|
let second_string: String = second.iter_chars().collect();
|
||||||
|
assert_eq!(first_string, full_string[0..i]);
|
||||||
|
assert_eq!(second_string, full_string[i..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[timeout(100)]
|
||||||
|
fn split_splits_at_correct_location_with_multibyte_chars() {
|
||||||
|
let target = small_test_rope_with_multibyte_chars();
|
||||||
|
let expected_chars: Vec<_> = small_test_rope_with_multibyte_chars_full_string()
|
||||||
|
.chars()
|
||||||
|
.collect();
|
||||||
|
for i in 0..(expected_chars.len()) {
|
||||||
|
let (first, second) = target.split(i);
|
||||||
|
let string: String = first.iter_chars().collect();
|
||||||
|
let expected: String = expected_chars[0..i].iter().collect();
|
||||||
|
assert_eq!(string, expected);
|
||||||
|
let string: String = second.iter_chars().collect();
|
||||||
|
let expected: String = expected_chars[i..].iter().collect();
|
||||||
|
assert_eq!(string, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue