More WIP remove_text().
This commit is contained in:
parent
0794d5c6ec
commit
28a4da97a7
|
@ -162,6 +162,29 @@ impl Line {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Appends `text` to the end of line, just before the line ending (if
|
||||||
|
/// any).
|
||||||
|
/// NOTE: panics if it encounters a line ending in the text.
|
||||||
|
pub fn append_text(&mut self, text: &str) {
|
||||||
|
let mut i = self.text.len();
|
||||||
|
|
||||||
|
// Grow data size
|
||||||
|
self.text.grow(text.len(), 0);
|
||||||
|
|
||||||
|
// Copy new bytes in
|
||||||
|
for g in text.graphemes(true) {
|
||||||
|
if is_line_ending(g) {
|
||||||
|
panic!("Line::append_text(): line ending in inserted text.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for b in g.bytes() {
|
||||||
|
self.text[i] = b;
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Remove the text between grapheme positions 'pos_a' and 'pos_b'.
|
/// Remove the text between grapheme positions 'pos_a' and 'pos_b'.
|
||||||
pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) {
|
pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) {
|
||||||
// Bounds checks
|
// Bounds checks
|
||||||
|
@ -463,6 +486,28 @@ fn text_line_insert_text() {
|
||||||
assert!(tl.ending == LineEnding::CRLF);
|
assert!(tl.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn text_line_append_text() {
|
||||||
|
let mut tl = Line::new_from_str("Hello\r\n");
|
||||||
|
|
||||||
|
tl.append_text(" world!");
|
||||||
|
|
||||||
|
assert!(tl.text.len() == 12);
|
||||||
|
assert!(tl.text[0] == ('H' as u8));
|
||||||
|
assert!(tl.text[1] == ('e' as u8));
|
||||||
|
assert!(tl.text[2] == ('l' as u8));
|
||||||
|
assert!(tl.text[3] == ('l' as u8));
|
||||||
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
|
assert!(tl.text[5] == (' ' as u8));
|
||||||
|
assert!(tl.text[6] == ('w' as u8));
|
||||||
|
assert!(tl.text[7] == ('o' as u8));
|
||||||
|
assert!(tl.text[8] == ('r' as u8));
|
||||||
|
assert!(tl.text[9] == ('l' as u8));
|
||||||
|
assert!(tl.text[10] == ('d' as u8));
|
||||||
|
assert!(tl.text[11] == ('!' as u8));
|
||||||
|
assert!(tl.ending == LineEnding::CRLF);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_remove_text() {
|
fn text_line_remove_text() {
|
||||||
let mut tl = Line::new_from_str("Hello world!\r\n");
|
let mut tl = Line::new_from_str("Hello world!\r\n");
|
||||||
|
|
|
@ -150,7 +150,9 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
// All other cases
|
// All other cases
|
||||||
else {
|
else {
|
||||||
self.root.remove_text_recursive(pos_a, pos_b);
|
if self.root.remove_text_recursive(pos_a, pos_b) {
|
||||||
|
panic!("Buffer::remove_text(): dangling left side remains. This should never happen!");
|
||||||
|
}
|
||||||
self.root.set_last_line_ending_recursive();
|
self.root.set_last_line_ending_recursive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,6 +595,120 @@ fn insert_text_in_non_empty_buffer_7() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remove_text_1() {
|
||||||
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
assert!(buf.len() == 29);
|
||||||
|
assert!(buf.root.line_count == 6);
|
||||||
|
|
||||||
|
buf.remove_text(0, 3);
|
||||||
|
|
||||||
|
let mut iter = buf.grapheme_iter();
|
||||||
|
|
||||||
|
assert!(buf.len() == 26);
|
||||||
|
assert!(buf.root.line_count == 5);
|
||||||
|
assert!(Some("t") == iter.next());
|
||||||
|
assert!(Some("h") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("r") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("p") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("o") == iter.next());
|
||||||
|
assert!(Some("p") == iter.next());
|
||||||
|
assert!(Some("l") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("o") == iter.next());
|
||||||
|
assert!(Some("f") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("t") == iter.next());
|
||||||
|
assert!(Some("h") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("w") == iter.next());
|
||||||
|
assert!(Some("o") == iter.next());
|
||||||
|
assert!(Some("r") == iter.next());
|
||||||
|
assert!(Some("l") == iter.next());
|
||||||
|
assert!(Some("d") == iter.next());
|
||||||
|
assert!(Some("!") == iter.next());
|
||||||
|
assert!(None == iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remove_text_2() {
|
||||||
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
assert!(buf.len() == 29);
|
||||||
|
assert!(buf.root.line_count == 6);
|
||||||
|
|
||||||
|
buf.remove_text(6, 18);
|
||||||
|
|
||||||
|
let mut iter = buf.grapheme_iter();
|
||||||
|
|
||||||
|
assert!(buf.len() == 17);
|
||||||
|
assert!(buf.root.line_count == 4);
|
||||||
|
assert!(Some("H") == iter.next());
|
||||||
|
assert!(Some("i") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("t") == iter.next());
|
||||||
|
assert!(Some("h") == iter.next());
|
||||||
|
assert!(Some("f") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("t") == iter.next());
|
||||||
|
assert!(Some("h") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("w") == iter.next());
|
||||||
|
assert!(Some("o") == iter.next());
|
||||||
|
assert!(Some("r") == iter.next());
|
||||||
|
assert!(Some("l") == iter.next());
|
||||||
|
assert!(Some("d") == iter.next());
|
||||||
|
assert!(Some("!") == iter.next());
|
||||||
|
assert!(None == iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remove_text_3() {
|
||||||
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
assert!(buf.len() == 29);
|
||||||
|
assert!(buf.root.line_count == 6);
|
||||||
|
|
||||||
|
buf.remove_text(17, 29);
|
||||||
|
|
||||||
|
let mut iter = buf.grapheme_iter();
|
||||||
|
|
||||||
|
assert!(buf.len() == 17);
|
||||||
|
assert!(buf.root.line_count == 4);
|
||||||
|
assert!(Some("H") == iter.next());
|
||||||
|
assert!(Some("i") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("t") == iter.next());
|
||||||
|
assert!(Some("h") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("r") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("p") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("o") == iter.next());
|
||||||
|
assert!(Some("p") == iter.next());
|
||||||
|
assert!(Some("l") == iter.next());
|
||||||
|
assert!(Some("e") == iter.next());
|
||||||
|
assert!(Some("\n") == iter.next());
|
||||||
|
assert!(Some("o") == iter.next());
|
||||||
|
assert!(None == iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_lines_1() {
|
fn remove_lines_1() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
|
@ -317,15 +317,18 @@ impl BufferNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Removes text between grapheme positions pos_a and pos_b
|
/// Removes text between grapheme positions pos_a and pos_b.
|
||||||
pub fn remove_text_recursive(&mut self, pos_a: uint, pos_b: uint) {
|
/// Returns true if a dangling left side remains from the removal.
|
||||||
|
/// Returns false otherwise.
|
||||||
|
pub fn remove_text_recursive(&mut self, pos_a: uint, pos_b: uint) -> bool {
|
||||||
let mut temp_node = BufferNode::new();
|
let mut temp_node = BufferNode::new();
|
||||||
let mut total_side_removal = false;
|
let mut total_side_removal = false;
|
||||||
|
let mut dangling_line = false;
|
||||||
|
let mut do_merge_fix = false;
|
||||||
|
let mut merge_line_number: uint = 0;
|
||||||
|
|
||||||
match self.data {
|
match self.data {
|
||||||
BufferNodeData::Branch(ref mut left, ref mut right) => {
|
BufferNodeData::Branch(ref mut left, ref mut right) => {
|
||||||
let mut right_line: Option<Line> = None;
|
|
||||||
|
|
||||||
// Check for complete removal of both sides, which
|
// Check for complete removal of both sides, which
|
||||||
// should never happen here
|
// should never happen here
|
||||||
if pos_a == 0 && pos_b == self.grapheme_count {
|
if pos_a == 0 && pos_b == self.grapheme_count {
|
||||||
|
@ -347,38 +350,50 @@ impl BufferNode {
|
||||||
if pos_a < left.grapheme_count {
|
if pos_a < left.grapheme_count {
|
||||||
let a = pos_a;
|
let a = pos_a;
|
||||||
let b = left.grapheme_count;
|
let b = left.grapheme_count;
|
||||||
left.remove_text_recursive(a, b);
|
dangling_line = left.remove_text_recursive(a, b);
|
||||||
}
|
}
|
||||||
mem::swap(&mut temp_node, &mut (**left));
|
mem::swap(&mut temp_node, &mut (**left));
|
||||||
}
|
}
|
||||||
// Partial removal of only left side
|
// Partial removal of one or both sides
|
||||||
else if pos_b < left.grapheme_count {
|
|
||||||
left.remove_text_recursive(pos_a, pos_b);
|
|
||||||
}
|
|
||||||
// Partial removal of only right side
|
|
||||||
else if pos_a > left.grapheme_count {
|
|
||||||
right.remove_text_recursive(pos_a - left.grapheme_count, pos_b - left.grapheme_count);
|
|
||||||
}
|
|
||||||
// Partial removal of both sides
|
|
||||||
else {
|
else {
|
||||||
// TODO
|
// Right side
|
||||||
|
if pos_b > left.grapheme_count {
|
||||||
|
let a = if pos_a > left.grapheme_count {pos_a - left.grapheme_count} else {0};
|
||||||
|
let b = pos_b - left.grapheme_count;
|
||||||
|
right.remove_text_recursive(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left side
|
||||||
|
if pos_a < left.grapheme_count {
|
||||||
|
let a = pos_a;
|
||||||
|
let b = min(pos_b, left.grapheme_count);
|
||||||
|
do_merge_fix = left.remove_text_recursive(a, b);
|
||||||
|
merge_line_number = left.line_count - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
BufferNodeData::Leaf(ref mut line) => {
|
BufferNodeData::Leaf(ref mut line) => {
|
||||||
// TODO
|
line.remove_text(pos_a, pos_b);
|
||||||
|
dangling_line = line.ending == LineEnding::None;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do the merge fix if necessary
|
||||||
|
if do_merge_fix {
|
||||||
|
self.merge_line_with_next_recursive(merge_line_number, None);
|
||||||
|
}
|
||||||
// If one of the sides was completely removed, replace self with the
|
// If one of the sides was completely removed, replace self with the
|
||||||
// remaining side.
|
// remaining side.
|
||||||
if total_side_removal {
|
else if total_side_removal {
|
||||||
mem::swap(&mut temp_node, self);
|
mem::swap(&mut temp_node, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_stats();
|
self.update_stats();
|
||||||
self.rebalance();
|
self.rebalance();
|
||||||
|
|
||||||
|
return dangling_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -436,6 +451,89 @@ impl BufferNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn merge_line_with_next_recursive(&mut self, line_number: uint, fetched_line: Option<&Line>) {
|
||||||
|
match fetched_line {
|
||||||
|
None => {
|
||||||
|
let mut line: Option<Line> = self.pull_out_line_recursive(line_number + 1);
|
||||||
|
if let Some(ref l) = line {
|
||||||
|
self.merge_line_with_next_recursive(line_number, Some(l));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(line) => {
|
||||||
|
match self.data {
|
||||||
|
BufferNodeData::Branch(ref mut left, ref mut right) => {
|
||||||
|
if line_number < left.line_count {
|
||||||
|
left.merge_line_with_next_recursive(line_number, Some(line));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
right.merge_line_with_next_recursive(line_number - left.line_count, Some(line));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
BufferNodeData::Leaf(ref mut line2) => {
|
||||||
|
line2.append_text(line.as_str());
|
||||||
|
line2.ending = line.ending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_stats();
|
||||||
|
self.rebalance();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Removes a single line out of the text and returns it.
|
||||||
|
pub fn pull_out_line_recursive(&mut self, line_number: uint) -> Option<Line> {
|
||||||
|
let mut pulled_line = Line::new();
|
||||||
|
let mut temp_node = BufferNode::new();
|
||||||
|
let mut side_removal = false;
|
||||||
|
|
||||||
|
match self.data {
|
||||||
|
BufferNodeData::Branch(ref mut left, ref mut right) => {
|
||||||
|
if line_number < left.line_count {
|
||||||
|
if let BufferNodeData::Leaf(ref mut line) = left.data {
|
||||||
|
mem::swap(&mut pulled_line, line);
|
||||||
|
mem::swap(&mut temp_node, &mut (**right));
|
||||||
|
side_removal = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pulled_line = left.pull_out_line_recursive(line_number).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if line_number < self.line_count {
|
||||||
|
if let BufferNodeData::Leaf(ref mut line) = right.data {
|
||||||
|
mem::swap(&mut pulled_line, line);
|
||||||
|
mem::swap(&mut temp_node, &mut (**left));
|
||||||
|
side_removal = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pulled_line = right.pull_out_line_recursive(line_number - left.line_count).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
BufferNodeData::Leaf(ref mut line) => {
|
||||||
|
panic!("pull_out_line_recursive(): inside leaf node. This should never happen!");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if side_removal {
|
||||||
|
mem::swap(&mut temp_node, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_stats();
|
||||||
|
self.rebalance();
|
||||||
|
|
||||||
|
return Some(pulled_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Ensures that the last line in the node tree has no
|
/// Ensures that the last line in the node tree has no
|
||||||
/// ending line break.
|
/// ending line break.
|
||||||
pub fn set_last_line_ending_recursive(&mut self) {
|
pub fn set_last_line_ending_recursive(&mut self) {
|
||||||
|
|
|
@ -87,5 +87,5 @@ pub fn grapheme_pos_to_byte_pos(text: &str, pos: uint) -> uint {
|
||||||
return text.len();
|
return text.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
panic!("grapheme_pos_to_byte_pos(): char position off the end of the string.");
|
panic!("grapheme_pos_to_byte_pos(): grapheme position off the end of the string.");
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user