diff --git a/src/buffer/text_line.rs b/src/buffer/line.rs similarity index 75% rename from src/buffer/text_line.rs rename to src/buffer/line.rs index 64d70f8..459c122 100644 --- a/src/buffer/text_line.rs +++ b/src/buffer/line.rs @@ -2,30 +2,30 @@ use std::mem; use std::str::Graphemes; -use string_utils::{grapheme_pos_to_byte_pos, is_line_ending}; +use string_utils::{grapheme_count, grapheme_pos_to_byte_pos, is_line_ending}; /// A single line of text -pub struct TextLine { +pub struct Line { text: Vec, // The text data, stored as UTF8 - ending: LineEnding, // The type of line ending, if any + pub ending: LineEnding, // The type of line ending, if any } -impl TextLine { - /// Creates a new empty TextLine - pub fn new() -> TextLine { - TextLine { +impl Line { + /// Creates a new empty Line + pub fn new() -> Line { + Line { text: Vec::new(), ending: LineEnding::None, } } - /// Creates a new TextLine from a str. - pub fn new_from_str(text: &str) -> TextLine { - // Initialize TextLine - let mut tl = TextLine { + /// Creates a new Line from a str. + pub fn new_from_str(text: &str) -> Line { + // Initialize Line + let mut tl = Line { text: Vec::with_capacity(text.len()), ending: LineEnding::None, }; @@ -102,6 +102,24 @@ impl TextLine { } + /// Returns the total number of unicode graphemes in the line + pub fn grapheme_count(&self) -> uint { + let mut count = grapheme_count(self.as_str()); + match self.ending { + LineEnding::None => {}, + _ => {count += 1;} + } + return count; + } + + + /// Returns the total number of unicode graphemes in the line, + /// not counting the line ending grapheme, if any. + pub fn grapheme_count_sans_line_ending(&self) -> uint { + grapheme_count(self.as_str()) + } + + /// Returns an immutable string slice into the text block's memory pub fn as_str<'a>(&'a self) -> &'a str { unsafe { @@ -133,7 +151,7 @@ impl TextLine { let mut i = byte_pos; for g in text.graphemes(true) { if is_line_ending(g) { - panic!("TextLine::insert_text(): line ending in inserted text."); + panic!("Line::insert_text(): line ending in inserted text."); } for b in g.bytes() { @@ -144,9 +162,36 @@ impl TextLine { } + /// Remove the text between grapheme positions 'pos_a' and 'pos_b'. + pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) { + // Bounds checks + if pos_a > pos_b { + panic!("Line::remove_text(): pos_a must be less than or equal to pos_b."); + } + + // Find removal positions in bytes + let byte_pos_a = grapheme_pos_to_byte_pos(self.as_str(), pos_a); + let byte_pos_b = grapheme_pos_to_byte_pos(self.as_str(), pos_b); + + // Move bytes to fill in the gap left by the removed bytes + let mut from = byte_pos_b; + let mut to = byte_pos_a; + while from < self.text.len() { + self.text[to] = self.text[from]; + + from += 1; + to += 1; + } + + // Remove data from the end + let final_text_size = self.text.len() + byte_pos_a - byte_pos_b; + self.text.truncate(final_text_size); + } + + /// Returns an iterator over the graphemes of the line - pub fn grapheme_iter<'a>(&'a self) -> TextLineIter<'a> { - TextLineIter { + pub fn grapheme_iter<'a>(&'a self) -> LineGraphemeIter<'a> { + LineGraphemeIter { graphemes: self.as_str().graphemes(true), ending: self.ending, done: false, @@ -155,10 +200,10 @@ impl TextLine { /// Returns an iterator over the graphemes of the line - pub fn grapheme_iter_at_index<'a>(&'a self, index: uint) -> TextLineIter<'a> { + pub fn grapheme_iter_at_index<'a>(&'a self, index: uint) -> LineGraphemeIter<'a> { let temp: &str = unsafe{mem::transmute(self.text.as_slice())}; - let mut iter = TextLineIter { + let mut iter = LineGraphemeIter { graphemes: temp.graphemes(true), ending: self.ending, done: false, @@ -202,14 +247,14 @@ pub const LINE_ENDINGS: [&'static str, ..9] = ["", ]; -/// An iterator over the graphemes of a TextLine -pub struct TextLineIter<'a> { +/// An iterator over the graphemes of a Line +pub struct LineGraphemeIter<'a> { graphemes: Graphemes<'a>, ending: LineEnding, done: bool, } -impl<'a> Iterator<&'a str> for TextLineIter<'a> { +impl<'a> Iterator<&'a str> for LineGraphemeIter<'a> { fn next(&mut self) -> Option<&'a str> { if self.done { return None; @@ -237,12 +282,12 @@ impl<'a> Iterator<&'a str> for TextLineIter<'a> { //========================================================================= -// TextLine tests +// Line tests //========================================================================= #[test] fn new_text_line() { - let tl = TextLine::new(); + let tl = Line::new(); assert!(tl.text.len() == 0); assert!(tl.ending == LineEnding::None); @@ -250,7 +295,7 @@ fn new_text_line() { #[test] fn new_text_line_from_str() { - let tl = TextLine::new_from_str("Hello!"); + let tl = Line::new_from_str("Hello!"); assert!(tl.text.len() == 6); assert!(tl.text[0] == ('H' as u8)); @@ -264,7 +309,7 @@ fn new_text_line_from_str() { #[test] fn new_text_line_from_empty_str() { - let tl = TextLine::new_from_str(""); + let tl = Line::new_from_str(""); assert!(tl.text.len() == 0); assert!(tl.ending == LineEnding::None); @@ -272,7 +317,7 @@ fn new_text_line_from_empty_str() { #[test] fn new_text_line_from_str_with_lf() { - let tl = TextLine::new_from_str("Hello!\n"); + let tl = Line::new_from_str("Hello!\n"); assert!(tl.text.len() == 6); assert!(tl.text[0] == ('H' as u8)); @@ -286,7 +331,7 @@ fn new_text_line_from_str_with_lf() { #[test] fn new_text_line_from_str_with_crlf() { - let tl = TextLine::new_from_str("Hello!\r\n"); + let tl = Line::new_from_str("Hello!\r\n"); assert!(tl.text.len() == 6); assert!(tl.text[0] == ('H' as u8)); @@ -300,7 +345,7 @@ fn new_text_line_from_str_with_crlf() { #[test] fn new_text_line_from_str_with_crlf_and_too_long() { - let tl = TextLine::new_from_str("Hello!\r\nLa la la la"); + let tl = Line::new_from_str("Hello!\r\nLa la la la"); assert!(tl.text.len() == 6); assert!(tl.text[0] == ('H' as u8)); @@ -314,7 +359,7 @@ fn new_text_line_from_str_with_crlf_and_too_long() { #[test] fn text_line_insert_text() { - let mut tl = TextLine::new_from_str("Hello!\r\n"); + let mut tl = Line::new_from_str("Hello!\r\n"); tl.insert_text(" world", 5); @@ -334,14 +379,30 @@ fn text_line_insert_text() { assert!(tl.ending == LineEnding::CRLF); } +#[test] +fn text_line_remove_text() { + let mut tl = Line::new_from_str("Hello world!\r\n"); + + tl.remove_text(5, 11); + + assert!(tl.text.len() == 6); + 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.ending == LineEnding::CRLF); +} + //========================================================================= -// TextLineIter tests +// LineGraphemeIter tests //========================================================================= #[test] fn text_line_grapheme_iter() { - let tl = TextLine::new_from_str("Hello!"); + let tl = Line::new_from_str("Hello!"); let mut iter = tl.grapheme_iter(); assert!(iter.next() == Some("H")); @@ -355,7 +416,7 @@ fn text_line_grapheme_iter() { #[test] fn text_line_grapheme_iter_with_lf() { - let tl = TextLine::new_from_str("Hello!\n"); + let tl = Line::new_from_str("Hello!\n"); let mut iter = tl.grapheme_iter(); assert!(iter.next() == Some("H")); @@ -370,7 +431,7 @@ fn text_line_grapheme_iter_with_lf() { #[test] fn text_line_grapheme_iter_with_crlf() { - let tl = TextLine::new_from_str("Hello!\r\n"); + let tl = Line::new_from_str("Hello!\r\n"); let mut iter = tl.grapheme_iter(); assert!(iter.next() == Some("H")); @@ -385,7 +446,7 @@ fn text_line_grapheme_iter_with_crlf() { #[test] fn text_line_grapheme_iter_at_index() { - let tl = TextLine::new_from_str("Hello!"); + let tl = Line::new_from_str("Hello!"); let mut iter = tl.grapheme_iter_at_index(2); assert!(iter.next() == Some("l")); @@ -397,7 +458,7 @@ fn text_line_grapheme_iter_at_index() { #[test] fn text_line_grapheme_iter_at_index_past_end() { - let tl = TextLine::new_from_str("Hello!"); + let tl = Line::new_from_str("Hello!"); let mut iter = tl.grapheme_iter_at_index(10); assert!(iter.next() == None); @@ -405,7 +466,7 @@ fn text_line_grapheme_iter_at_index_past_end() { #[test] fn text_line_grapheme_iter_at_index_at_lf() { - let tl = TextLine::new_from_str("Hello!\n"); + let tl = Line::new_from_str("Hello!\n"); let mut iter = tl.grapheme_iter_at_index(6); assert!(iter.next() == Some("\n")); diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 71a3401..0ad65a9 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -1,236 +1,278 @@ #![allow(dead_code)] -use std::fmt; use std; -use self::text_node::{TextNode, TextNodeData}; +use std::fmt; +use std::mem; +use std::cmp::{min, max}; + +use self::node::{BufferNode, BufferNodeData}; +use self::line::{Line, LineGraphemeIter}; + +//mod text_block; +//mod text_node; +mod line; +mod node; + -mod text_block; -mod text_node; -mod text_line; /// A text buffer -pub struct TextBuffer { - pub root: TextNode, -} +pub struct Buffer { + root: BufferNode, +}// -impl TextBuffer { - pub fn new() -> TextBuffer { - TextBuffer { - root: TextNode::new() + +impl Buffer { + pub fn new() -> Buffer { + Buffer { + root: BufferNode::new() } } pub fn len(&self) -> uint { - self.root.char_count + self.root.grapheme_count } - pub fn newline_count(&self) -> uint { - self.root.newline_count + pub fn line_count(&self) -> uint { + self.root.line_count } - - pub fn end_of_line(&self, pos: uint) -> uint { - self.root.end_of_line(pos) + + pub fn get_line<'a>(&'a self, index: uint) -> &'a Line { + if index >= self.line_count() { + panic!("get_line(): index out of bounds."); + } + + // NOTE: this can be done non-recursively, which would be more + // efficient. However, it seems likely to require unsafe code + // if done that way. + return self.root.get_line_recursive(index); + } + + + /// Removes the lines in line indices [line_a, line_b). + pub fn remove_lines(&mut self, line_a: uint, line_b: uint) { + // Nothing to do + if line_a == line_b { + return; + } + // Bounds error + else if line_a > line_b { + panic!("Buffer::remove_lines(): line_a must be less than or equal to line_b."); + } + // Bounds error + else if line_b > self.line_count() { + panic!("Buffer::remove_lines(): attempt to remove lines past the last line of text."); + } + // Complete removal of all lines + else if line_a == 0 && line_b == self.root.line_count { + let mut temp_node = BufferNode::new(); + mem::swap(&mut (self.root), &mut temp_node); + } + // All other cases + else { + self.root.remove_lines_recursive(line_a, line_b); + } } pub fn pos_2d_to_closest_1d(&self, pos: (uint, uint)) -> uint { - match self.root.pos_2d_to_closest_1d(0, pos) { - text_node::IndexOrOffset::Index(i) => i, - _ => self.len() - } + return self.root.pos_2d_to_closest_1d_recursive(pos); } pub fn pos_1d_to_closest_2d(&self, pos: uint) -> (uint, uint) { - self.root.pos_1d_to_closest_2d((0,0), pos) + return self.root.pos_1d_to_closest_2d_recursive(pos); } - /// Insert 'text' at char position 'pos'. - pub fn insert_text(&mut self, text: &str, pos: uint) { - self.root.insert_text(text, pos); - } + // /// Insert 'text' at char position 'pos'. + // pub fn insert_text(&mut self, text: &str, pos: uint) { + // self.root.insert_text(text, pos); + // } - - /// Remove the text between char positions 'pos_a' and 'pos_b'. - pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) { - self.root.remove_text(pos_a, pos_b); - } + // + // /// Remove the text between char positions 'pos_a' and 'pos_b'. + // pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) { + // self.root.remove_text(pos_a, pos_b); + // } /// Creates an iterator at the first character - pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> { - let mut node_stack: Vec<&'a TextNode> = Vec::new(); - let mut cur_node = &self.root; - - loop { - match cur_node.data { - TextNodeData::Leaf(_) => { - break; - }, - - TextNodeData::Branch(ref left, ref right) => { - node_stack.push(&(**right)); - cur_node = &(**left); - } - } - } - - TextBufferIter { - node_stack: node_stack, - cur_block: match cur_node.data { - TextNodeData::Leaf(ref tb) => tb.as_str().chars(), - _ => panic!("This should never happen.") - } - } - } + // pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> { + // let mut node_stack: Vec<&'a TextNode> = Vec::new(); + // let mut cur_node = &self.root; + // + // loop { + // match cur_node.data { + // TextNodeData::Leaf(_) => { + // break; + // }, + // + // TextNodeData::Branch(ref left, ref right) => { + // node_stack.push(&(**right)); + // cur_node = &(**left); + // } + // } + // } + // + // TextBufferIter { + // node_stack: node_stack, + // cur_block: match cur_node.data { + // TextNodeData::Leaf(ref tb) => tb.as_str().chars(), + // _ => panic!("This should never happen.") + // } + // } + // } + // + // + // /// Creates an iterator starting at the specified character index. + // /// If the index is past the end of the text, then the iterator will + // /// return None on next(). + // pub fn iter_at_char<'a>(&'a self, index: uint) -> TextBufferIter<'a> { + // let mut node_stack: Vec<&'a TextNode> = Vec::new(); + // let mut cur_node = &self.root; + // let mut char_i = index; + // + // loop { + // match cur_node.data { + // TextNodeData::Leaf(_) => { + // let mut char_iter = match cur_node.data { + // TextNodeData::Leaf(ref tb) => tb.as_str().chars(), + // _ => panic!("This should never happen.") + // }; + // + // while char_i > 0 { + // char_iter.next(); + // char_i -= 1; + // } + // + // return TextBufferIter { + // node_stack: node_stack, + // cur_block: char_iter, + // }; + // }, + // + // TextNodeData::Branch(ref left, ref right) => { + // if left.char_count > char_i { + // node_stack.push(&(**right)); + // cur_node = &(**left); + // } + // else { + // cur_node = &(**right); + // char_i -= left.char_count; + // } + // } + // } + // } + // } - - /// Creates an iterator starting at the specified character index. - /// If the index is past the end of the text, then the iterator will - /// return None on next(). - pub fn iter_at_char<'a>(&'a self, index: uint) -> TextBufferIter<'a> { - let mut node_stack: Vec<&'a TextNode> = Vec::new(); - let mut cur_node = &self.root; - let mut char_i = index; - - loop { - match cur_node.data { - TextNodeData::Leaf(_) => { - let mut char_iter = match cur_node.data { - TextNodeData::Leaf(ref tb) => tb.as_str().chars(), - _ => panic!("This should never happen.") - }; - - while char_i > 0 { - char_iter.next(); - char_i -= 1; - } - - return TextBufferIter { - node_stack: node_stack, - cur_block: char_iter, - }; - }, - - TextNodeData::Branch(ref left, ref right) => { - if left.char_count > char_i { - node_stack.push(&(**right)); - cur_node = &(**left); - } - else { - cur_node = &(**right); - char_i -= left.char_count; - } - } - } - } - } } -impl fmt::Show for TextBuffer { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.root.fmt(f) - } +// impl fmt::Show for Buffer { +// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +// self.root.fmt(f) +// } +// } + + + + + + + +/// An iterator over a text buffer's graphemes +pub struct BufferGraphemeIter<'a> { + node_stack: Vec<&'a BufferNode>, + cur_line: &'a Line, } - - -/// An iterator over a text buffer -pub struct TextBufferIter<'a> { - node_stack: Vec<&'a TextNode>, - cur_block: std::str::Chars<'a>, -} - - -impl<'a> TextBufferIter<'a> { - // Puts the iterator on the next line - pub fn next_line(&mut self) -> Option { - // TODO: more efficient implementation, taking advantage of rope - // structure. - for c in *self { - if c == '\n' { - return Option::Some(c); - } - } - - return Option::None; - } - - - // Skips the iterator n characters ahead - pub fn skip_chars(&mut self, n: uint) { - // TODO: more efficient implementation, taking advantage of rope - // structure. - for _ in range(0, n) { - if let Option::None = self.next() { - break; - } - } - } - - - // Skips the iterator n characters ahead, unless it hits a newline - // character. If it hits a newline character, returns true, otherwise, - // false. - pub fn skip_non_newline_chars(&mut self, n: uint) -> bool { - // TODO: more efficient implementation, taking advantage of rope - // structure. - for _ in range(0, n) { - match self.next() { - Option::Some(c) => { - if c == '\n' { - return true; - } - }, - - Option::None => { - break; - } - } - } - - return false; - } -} - - -impl<'a> Iterator for TextBufferIter<'a> { - fn next(&mut self) -> Option { - if let Option::Some(c) = self.cur_block.next() { - return Option::Some(c); - } - - loop { - if let Option::Some(node) = self.node_stack.pop() { - match node.data { - TextNodeData::Leaf(ref tb) => { - self.cur_block = tb.as_str().chars(); - - if let Option::Some(c) = self.cur_block.next() { - return Option::Some(c); - } - else { - continue; - } - }, - - TextNodeData::Branch(ref left, ref right) => { - self.node_stack.push(&(**right)); - self.node_stack.push(&(**left)); - continue; - } - } - } - else { - return Option::None; - } - } - } -} \ No newline at end of file +// impl<'a> TextBufferIter<'a> { +// // Puts the iterator on the next line +// pub fn next_line(&mut self) -> Option { +// // TODO: more efficient implementation, taking advantage of rope +// // structure. +// for c in *self { +// if c == '\n' { +// return Option::Some(c); +// } +// } +// +// return Option::None; +// } +// +// +// // Skips the iterator n characters ahead +// pub fn skip_chars(&mut self, n: uint) { +// // TODO: more efficient implementation, taking advantage of rope +// // structure. +// for _ in range(0, n) { +// if let Option::None = self.next() { +// break; +// } +// } +// } +// +// +// // Skips the iterator n characters ahead, unless it hits a newline +// // character. If it hits a newline character, returns true, otherwise, +// // false. +// pub fn skip_non_newline_chars(&mut self, n: uint) -> bool { +// // TODO: more efficient implementation, taking advantage of rope +// // structure. +// for _ in range(0, n) { +// match self.next() { +// Option::Some(c) => { +// if c == '\n' { +// return true; +// } +// }, +// +// Option::None => { +// break; +// } +// } +// } +// +// return false; +// } +// } +// +// +// impl<'a> Iterator for TextBufferIter<'a> { +// fn next(&mut self) -> Option { +// if let Option::Some(c) = self.cur_block.next() { +// return Option::Some(c); +// } +// +// loop { +// if let Option::Some(node) = self.node_stack.pop() { +// match node.data { +// TextNodeData::Leaf(ref tb) => { +// self.cur_block = tb.as_str().chars(); +// +// if let Option::Some(c) = self.cur_block.next() { +// return Option::Some(c); +// } +// else { +// continue; +// } +// }, +// +// TextNodeData::Branch(ref left, ref right) => { +// self.node_stack.push(&(**right)); +// self.node_stack.push(&(**left)); +// continue; +// } +// } +// } +// else { +// return Option::None; +// } +// } +// } +// } \ No newline at end of file diff --git a/src/buffer/node.rs b/src/buffer/node.rs new file mode 100644 index 0000000..6f35a2a --- /dev/null +++ b/src/buffer/node.rs @@ -0,0 +1,290 @@ +use std; +use std::fmt; +use std::mem; +use std::cmp::{min, max}; + +use super::line::{Line, LineGraphemeIter}; + +pub enum BufferNodeData { + Leaf(Line), + Branch(Box, Box), +} + +pub struct BufferNode { + pub data: BufferNodeData, + pub tree_height: uint, + + pub grapheme_count: uint, + pub line_count: uint, +} + +impl BufferNode { + pub fn new() -> BufferNode { + BufferNode { + data: BufferNodeData::Leaf(Line::new()), + tree_height: 1, + grapheme_count: 0, + line_count: 1, + } + } + + + fn update_height(&mut self) { + match self.data { + BufferNodeData::Leaf(_) => { + self.tree_height = 1; + }, + + BufferNodeData::Branch(ref left, ref right) => { + self.tree_height = max(left.tree_height, right.tree_height) + 1; + } + } + } + + + fn update_stats(&mut self) { + self.update_height(); + + match self.data { + BufferNodeData::Leaf(ref line) => { + self.grapheme_count = line.grapheme_count(); + self.line_count = 1; + }, + + BufferNodeData::Branch(ref left, ref right) => { + self.grapheme_count = left.grapheme_count + right.grapheme_count; + self.line_count = left.line_count + right.line_count; + } + } + } + + + /// Rotates the tree under the node left + fn rotate_left(&mut self) { + let mut temp = BufferNode::new(); + + if let BufferNodeData::Branch(_, ref mut right) = self.data { + mem::swap(&mut temp, &mut (**right)); + + if let BufferNodeData::Branch(ref mut left, _) = temp.data { + mem::swap(&mut (**left), &mut (**right)); + } + else { + panic!("rotate_left(): attempting to rotate node without branching right child."); + } + } + else { + panic!("rotate_left(): attempting to rotate leaf node."); + } + + if let BufferNodeData::Branch(ref mut left, _) = temp.data { + mem::swap(&mut (**left), self); + left.update_stats(); + } + + mem::swap(&mut temp, self); + self.update_stats(); + } + + + /// Rotates the tree under the node right + fn rotate_right(&mut self) { + let mut temp = BufferNode::new(); + + if let BufferNodeData::Branch(ref mut left, _) = self.data { + mem::swap(&mut temp, &mut (**left)); + + if let BufferNodeData::Branch(_, ref mut right) = temp.data { + mem::swap(&mut (**right), &mut (**left)); + } + else { + panic!("rotate_right(): attempting to rotate node without branching left child."); + } + } + else { + panic!("rotate_right(): attempting to rotate leaf node."); + } + + if let BufferNodeData::Branch(_, ref mut right) = temp.data { + mem::swap(&mut (**right), self); + right.update_stats(); + } + + mem::swap(&mut temp, self); + self.update_stats(); + } + + + /// Rebalances the tree under the node + fn rebalance(&mut self) { + loop { + let mut rot: int; + + if let BufferNodeData::Branch(ref mut left, ref mut right) = self.data { + let height_diff = (left.tree_height as int) - (right.tree_height as int); + + // Left side higher than right side + if height_diff > 1 { + let mut child_rot = false; + if let BufferNodeData::Branch(ref lc, ref rc) = left.data { + if lc.tree_height < rc.tree_height { + child_rot = true; + } + } + + if child_rot { + left.rotate_left(); + } + + rot = 1; + } + // Right side higher then left side + else if height_diff < -1 { + let mut child_rot = false; + if let BufferNodeData::Branch(ref lc, ref rc) = right.data { + if lc.tree_height > rc.tree_height { + child_rot = true; + } + } + + if child_rot { + right.rotate_right(); + } + + rot = -1; + } + // Balanced, stop + else { + break; + } + } + else { + // Leaf node, stop + break; + } + + if rot == 1 { + self.rotate_right(); + } + else if rot == -1 { + self.rotate_left(); + } + } + } + + + pub fn get_line_recursive<'a>(&'a self, index: uint) -> &'a Line { + match self.data { + BufferNodeData::Leaf(ref line) => { + if index != 0 { + panic!("get_line_recursive(): at leaf, but index is not zero. This should never happen!"); + } + return line; + }, + + BufferNodeData::Branch(ref left, ref right) => { + if index < left.line_count { + return left.get_line_recursive(index); + } + else { + return right.get_line_recursive(index - left.line_count); + } + } + } + } + + + pub fn pos_2d_to_closest_1d_recursive(&self, pos: (uint, uint)) -> uint { + match self.data { + BufferNodeData::Leaf(_) => { + if pos.0 != 0 { + panic!("pos_2d_to_closest_1d_recursive(): at leaf, but index is not zero. This should never happen!"); + } + return min(pos.1, self.grapheme_count); + }, + + BufferNodeData::Branch(ref left, ref right) => { + if pos.0 < left.line_count { + return left.pos_2d_to_closest_1d_recursive(pos); + } + else { + return left.grapheme_count + right.pos_2d_to_closest_1d_recursive((pos.0 - left.line_count, pos.1)); + } + } + } + } + + + pub fn pos_1d_to_closest_2d_recursive(&self, pos: uint) -> (uint, uint) { + match self.data { + BufferNodeData::Leaf(_) => { + return (0, min(pos, self.grapheme_count)); + }, + + BufferNodeData::Branch(ref left, ref right) => { + if pos < left.grapheme_count { + return left.pos_1d_to_closest_2d_recursive(pos); + } + else { + let (v, h) = right.pos_1d_to_closest_2d_recursive((pos - left.grapheme_count)); + return (v + left.line_count, h); + } + } + } + } + + + pub fn remove_lines_recursive(&mut self, line_a: uint, line_b: uint) { + let mut remove_left = false; + let mut remove_right = false; + let mut temp_node = BufferNode::new(); + + if let BufferNodeData::Branch(ref mut left, ref mut right) = self.data { + // Left node completely removed + if line_a == 0 && line_b >= left.line_count { + remove_left = true; + } + // Left node partially removed + else if line_a < left.line_count { + let a = line_a; + let b = min(left.line_count, line_b); + left.remove_lines_recursive(a, b); + } + + // Right node completely removed + if line_a <= left.line_count && line_b >= (left.line_count + right.line_count) { + remove_right = true; + } + // Right node partially removed + else if line_b > left.line_count { + let a = if line_a > left.line_count {line_a - left.line_count} else {0}; + let b = line_b - left.line_count; + right.remove_lines_recursive(a, b); + } + + // Set up for node removal + if remove_left && remove_right { + panic!("remove_lines_recursive(): attempting to completely remove both left and right nodes. This should never happen!"); + } + else if remove_left { + mem::swap(&mut temp_node, &mut (**right)); + } + else if remove_right { + mem::swap(&mut temp_node, &mut (**left)); + } + } + else { + panic!("remove_lines_recursive(): processing a leaf node directly. This should never happen!"); + } + + // Swap out node for non-removed node + if remove_left || remove_right { + mem::swap(&mut temp_node, self); + } + + self.update_stats(); + self.rebalance(); + } + + +} diff --git a/src/buffer/text_block.rs b/src/buffer/text_block.rs deleted file mode 100644 index 2f86efa..0000000 --- a/src/buffer/text_block.rs +++ /dev/null @@ -1,189 +0,0 @@ -#![allow(dead_code)] - -use std::mem; -use std::fmt; -use string_utils::{char_pos_to_byte_pos, char_count}; - -/// A block of text, contiguous in memory -pub struct TextBlock { - // The actual text data, in utf8 - pub data: Vec, - - // The visual width of each printable character. - // Characters with variable width (e.g. tab characters) - // have width None. - pub widths: Vec>, -} - -impl TextBlock { - /// Create a new empty text block. - pub fn new() -> TextBlock { - TextBlock { - data: Vec::::new(), - widths: Vec::>::new(), - } - } - - /// Create a new text block with the contents of 'text'. - pub fn new_from_str(text: &str) -> TextBlock { - let mut tb = TextBlock { - data: Vec::::with_capacity(text.len()), - widths: Vec::>::with_capacity(text.len()), - }; - - for b in text.bytes() { - tb.data.push(b); - } - - // TODO: handle fonts - for c in text.chars() { - if c == '\t' { - tb.widths.push(None); - } - else { - tb.widths.push(Some(1)); - } - } - - return tb; - } - - /// Return the length of the text block in bytes. - pub fn len(&self) -> uint { - self.data.len() - } - - /// Returns the total width of text block sans-variable-width characters - pub fn total_non_variable_width(&self) -> uint { - let mut width: uint = 0; - - for w in self.widths.iter() { - if let &Some(ww) = w { - width += ww as uint; - } - } - - return width; - } - - /// Returns the number of variable-width chars in the text block - pub fn variable_width_chars(&self) -> uint { - let mut count: uint = 0; - - for w in self.widths.iter() { - if let &None = w { - count += 1; - } - } - - return count; - } - - /// Insert 'text' at char position 'pos'. - pub fn insert_text(&mut self, text: &str, pos: uint) { - //====== TEXT DATA ====== - // Find insertion position in bytes - let byte_pos = char_pos_to_byte_pos(self.as_str(), pos); - - // Grow data size - self.data.grow(text.len(), 0); - - // Move old bytes forward - let mut from = self.data.len() - text.len(); - let mut to = self.data.len(); - while from > byte_pos { - from -= 1; - to -= 1; - - self.data[to] = self.data[from]; - } - - // Copy new bytes in - let mut i = byte_pos; - for b in text.bytes() { - self.data[i] = b; - i += 1 - } - - //====== WIDTHS ====== - // Grow widths size - let cc = char_count(text); - self.widths.grow(cc, None); - - // Move old widths forward - from = self.widths.len() - cc; - to = self.widths.len(); - while from > pos { - from -= 1; - to -= 1; - - self.widths[to] = self.widths[from]; - } - - // Copy new widths in - i = pos; - for c in text.chars() { - if c == '\t' { - self.widths[i] = None; - } - else { - self.widths[i] = Some(1); - } - i += 1 - } - } - - /// Remove the text between char positions 'pos_a' and 'pos_b'. - pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) { - // Bounds checks - if pos_a > pos_b { - panic!("TextBlock::remove_text(): pos_a must be less than or equal to pos_b."); - } - - //====== TEXT DATA ====== - // Find removal positions in bytes - let byte_pos_a = char_pos_to_byte_pos(self.as_str(), pos_a); - let byte_pos_b = char_pos_to_byte_pos(self.as_str(), pos_b); - - // Move bytes to fill in the gap left by the removed bytes - let mut from = byte_pos_b; - let mut to = byte_pos_a; - while from < self.data.len() { - self.data[to] = self.data[from]; - - from += 1; - to += 1; - } - - // Remove data from the end - let final_data_size = self.data.len() + byte_pos_a - byte_pos_b; - self.data.truncate(final_data_size); - - //====== WIDTHS ====== - from = pos_b; - to = pos_a; - while from < self.widths.len() { - self.widths[to] = self.widths[from]; - - from += 1; - to += 1; - } - - // Remove data from end - let final_widths_size = self.widths.len() + pos_a - pos_b; - self.data.truncate(final_widths_size); - } - - /// Returns an immutable string slice into the text block's memory - pub fn as_str<'a>(&'a self) -> &'a str { - unsafe { - mem::transmute(self.data.as_slice()) - } - } -} - -impl fmt::Show for TextBlock { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} \ No newline at end of file diff --git a/src/buffer/text_node.rs b/src/buffer/text_node.rs deleted file mode 100644 index 9cfc66c..0000000 --- a/src/buffer/text_node.rs +++ /dev/null @@ -1,602 +0,0 @@ -#![allow(dead_code)] - -use std::fmt; -use std::mem; -use std::cmp::{min, max}; - -use string_utils::{newline_count, char_count, char_and_newline_count}; -use super::text_block::TextBlock; - -const MIN_LEAF_SIZE: uint = 64; -const MAX_LEAF_SIZE: uint = MIN_LEAF_SIZE * 2; - -pub enum IndexOrOffset { - Index(uint), - Offset(uint) -} - - -/// A text rope node, using TextBlocks for its underlying text -/// storage. -// TODO: record number of graphines as well, to support utf8 properly -pub struct TextNode { - pub data: TextNodeData, - pub tree_height: uint, - - pub char_count: uint, - pub newline_count: uint, -} - -pub enum TextNodeData { - Leaf(TextBlock), - Branch(Box, Box) -} - - - -impl TextNode { - pub fn new() -> TextNode { - TextNode { - data: TextNodeData::Leaf(TextBlock::new()), - tree_height: 1, - char_count: 0, - newline_count: 0, - } - } - - pub fn new_from_str(text: &str) -> TextNode { - TextNode { - data: TextNodeData::Leaf(TextBlock::new_from_str(text)), - tree_height: 1, - char_count: char_count(text), - newline_count: newline_count(text), - } - } - - pub fn update_height(&mut self) { - match self.data { - TextNodeData::Leaf(_) => { - self.tree_height = 1; - }, - - TextNodeData::Branch(ref left, ref right) => { - self.tree_height = max(left.tree_height, right.tree_height) + 1; - } - } - } - - pub fn update_stats(&mut self) { - match self.data { - TextNodeData::Leaf(ref tb) => { - self.tree_height = 1; - let (cc, nlc) = char_and_newline_count(tb.as_str()); - self.char_count = cc; - self.newline_count = nlc; - }, - - TextNodeData::Branch(ref left, ref right) => { - self.tree_height = max(left.tree_height, right.tree_height) + 1; - self.char_count = left.char_count + right.char_count; - self.newline_count = left.newline_count + right.newline_count; - } - } - } - - pub fn rotate_left(&mut self) { - let mut temp = TextNode::new(); - - if let TextNodeData::Branch(_, ref mut right) = self.data { - mem::swap(&mut temp, &mut (**right)); - - if let TextNodeData::Branch(ref mut left, _) = temp.data { - mem::swap(&mut (**left), &mut (**right)); - } - else { - panic!("rotate_left(): attempting to rotate node without branching right child."); - } - } - else { - panic!("rotate_left(): attempting to rotate leaf node."); - } - - if let TextNodeData::Branch(ref mut left, _) = temp.data { - mem::swap(&mut (**left), self); - left.update_stats(); - } - - mem::swap(&mut temp, self); - self.update_stats(); - } - - pub fn rotate_right(&mut self) { - let mut temp = TextNode::new(); - - if let TextNodeData::Branch(ref mut left, _) = self.data { - mem::swap(&mut temp, &mut (**left)); - - if let TextNodeData::Branch(_, ref mut right) = temp.data { - mem::swap(&mut (**right), &mut (**left)); - } - else { - panic!("rotate_right(): attempting to rotate node without branching left child."); - } - } - else { - panic!("rotate_right(): attempting to rotate leaf node."); - } - - if let TextNodeData::Branch(_, ref mut right) = temp.data { - mem::swap(&mut (**right), self); - right.update_stats(); - } - - mem::swap(&mut temp, self); - self.update_stats(); - } - - pub fn rebalance(&mut self) { - loop { - let mut rot: int; - - if let TextNodeData::Branch(ref mut left, ref mut right) = self.data { - let height_diff = (left.tree_height as int) - (right.tree_height as int); - - // Left side higher than right side - if height_diff > 1 { - let mut child_rot = false; - if let TextNodeData::Branch(ref lc, ref rc) = left.data { - if lc.tree_height < rc.tree_height { - child_rot = true; - } - } - - if child_rot { - left.rotate_left(); - } - - rot = 1; - } - // Right side higher then left side - else if height_diff < -1 { - let mut child_rot = false; - if let TextNodeData::Branch(ref lc, ref rc) = right.data { - if lc.tree_height > rc.tree_height { - child_rot = true; - } - } - - if child_rot { - right.rotate_right(); - } - - rot = -1; - } - // Balanced, stop - else { - break; - } - } - else { - break; - } - - if rot == 1 { - self.rotate_right(); - } - else if rot == -1 { - self.rotate_left(); - } - } - } - - /// Recursively splits a leaf node into roughly equal-sized children, - /// being no larger than 'max_size'. - pub fn split(&mut self, max_size: uint) { - if let TextNodeData::Branch(_, _) = self.data { - panic!("TextNode::split(): attempt to split a non-leaf node."); - } - - if self.char_count > max_size { - // Split data into two new text blocks - let mut tn1 = box TextNode::new(); - let mut tn2 = box TextNode::new(); - if let TextNodeData::Leaf(ref mut tb) = self.data { - let pos = tb.len() / 2; - tn1 = box TextNode::new_from_str(tb.as_str().slice(0, pos)); - tn2 = box TextNode::new_from_str(tb.as_str().slice(pos, tb.len())); - } - - tn1.split(max_size); - tn2.split(max_size); - - // Swap the old and new data - let mut new_data = TextNodeData::Branch(tn1, tn2); - mem::swap(&mut self.data, &mut new_data); - - } - - self.rebalance(); - self.update_height(); - } - - /// Merges the data of a non-leaf node to make it a leaf node - pub fn merge(&mut self) { - if let TextNodeData::Branch(_, _) = self.data { - let mut s: String = String::from_str(""); - - if let TextNodeData::Branch(ref mut left, ref mut right) = self.data { - // Merge left and right children first, to make sure we're dealing - // with leafs - if let TextNodeData::Branch(_, _) = left.data { left.merge(); } - if let TextNodeData::Branch(_, _) = right.data { right.merge(); } - - // Push data into a string - if let TextNodeData::Leaf(ref tb) = left.data { - s.push_str(tb.as_str()); - } - if let TextNodeData::Leaf(ref tb) = right.data { - s.push_str(tb.as_str()); - } - } - - self.data = TextNodeData::Leaf(TextBlock::new_from_str(s.as_slice())); - self.rebalance(); - self.update_height(); - } - } - - /// Insert 'text' at position 'pos'. - pub fn insert_text(&mut self, text: &str, pos: uint) { - if pos > self.char_count { - panic!("TextNode::insert_text(): attempt to insert text after end of node text."); - } - - match self.data { - TextNodeData::Leaf(_) => { - if let TextNodeData::Leaf(ref mut tb) = self.data { - tb.insert_text(text, pos); - - let (cc, nlc) = char_and_newline_count(text); - self.char_count += cc; - self.newline_count += nlc; - } - - if self.char_count > MAX_LEAF_SIZE { - self.split(MAX_LEAF_SIZE); - } - }, - - TextNodeData::Branch(ref mut left, ref mut right) => { - if pos <= left.char_count { - left.insert_text(text, pos); - } - else { - right.insert_text(text, pos - left.char_count); - } - - self.char_count = left.char_count + right.char_count; - self.newline_count = left.newline_count + right.newline_count; - } - } - - self.rebalance(); - self.update_height(); - } - - /// Remove the text between byte positions 'pos_a' and 'pos_b'. - pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) { - // Bounds checks - if pos_a > pos_b { - panic!("TextNode::remove_text(): pos_a must be less than or equal to pos_b."); - } - if pos_b > self.char_count { - panic!("TextNode::remove_text(): attempt to remove text after end of node text."); - } - - match self.data { - TextNodeData::Leaf(ref mut tb) => { - tb.remove_text(pos_a, pos_b); - - let (cc, nlc) = char_and_newline_count(tb.as_str()); - self.char_count = cc; - self.newline_count = nlc; - }, - - TextNodeData::Branch(ref mut left, ref mut right) => { - let lbc = left.char_count; - - if pos_a < lbc { - left.remove_text(pos_a, min(pos_b, lbc)); - } - - if pos_b > lbc { - right.remove_text(pos_a - min(pos_a, lbc), pos_b - lbc); - } - - self.char_count = left.char_count + right.char_count; - self.newline_count = left.newline_count + right.newline_count; - } - } - - self.rebalance(); - self.update_height(); - - if self.char_count < MIN_LEAF_SIZE { - self.merge(); - self.rebalance(); - self.update_height(); - } - } - - /// Find the closest 1d text position that represents the given - /// 2d position well. - pub fn pos_2d_to_closest_1d(&self, offset: uint, pos: (uint, uint)) -> IndexOrOffset { - match self.data { - TextNodeData::Leaf(ref tb) => { - let mut iter = tb.as_str().chars(); - let mut i = 0; - let mut line = 0; - let mut col = offset; - - for c in iter { - // Check if we've hit or passed the target column on - // the target line. - if line == pos.0 && col >= pos.1 { - break; - } - - // Increment counters - if c == '\n' { - line += 1; - col = 0; - - // Check if we've passed the target line - if line > pos.0 { - break; - } - } - else { - col += 1; - } - - i += 1; - } - - // If we've reached the end of this text block but - // haven't reached the target position, return an - // offset of the amount of this line already consumed. - if pos.0 > line || (pos.0 == line && pos.1 > col) { - return IndexOrOffset::Offset(col); - } - - // Otherwise, we've found it! - return IndexOrOffset::Index(i); - }, - - TextNodeData::Branch(ref left, ref right) => { - // Left child - if pos.0 <= left.newline_count { - match left.pos_2d_to_closest_1d(offset, pos) { - IndexOrOffset::Index(il) => { - return IndexOrOffset::Index(il); - }, - - IndexOrOffset::Offset(il) => { - match right.pos_2d_to_closest_1d(il, (pos.0 - left.newline_count, pos.1)) { - IndexOrOffset::Index(ir) => { - return IndexOrOffset::Index(ir + left.char_count); - }, - - IndexOrOffset::Offset(ir) => { - return IndexOrOffset::Offset(ir); - } - } - } - } - } - // Right child - else { - match right.pos_2d_to_closest_1d(0, (pos.0 - left.newline_count, pos.1)) { - IndexOrOffset::Index(ir) => { - return IndexOrOffset::Index(ir + left.char_count); - }, - - IndexOrOffset::Offset(ir) => { - return IndexOrOffset::Offset(ir); - } - } - } - } - } - } - - - /// Find the closest 2d text position that represents the given - /// 1d position well. - pub fn pos_1d_to_closest_2d(&self, offset_pos: (uint, uint), pos: uint) -> (uint, uint) { - match self.data { - TextNodeData::Leaf(ref tb) => { - let mut iter = tb.as_str().chars(); - let mut line = offset_pos.0; - let mut col = offset_pos.1; - let mut i: uint = 0; - - for c in iter { - if i == pos { - break; - } - - if c == '\n' { - line += 1; - col = 0; - } - else { - col += 1; - } - - i += 1; - } - - return (line, col); - }, - - TextNodeData::Branch(ref left, ref right) => { - if left.char_count > pos { - return left.pos_1d_to_closest_2d(offset_pos, pos); - } - else { - let mut line = offset_pos.0; - let mut col = offset_pos.1; - - if left.newline_count > 0 { - line += left.newline_count; - col = 0; - } - - col += left.tail_len(); - - return right.pos_1d_to_closest_2d((line, col), pos - left.char_count); - } - } - } - } - - - /// Returns the number of newlines contained within the - /// character range [pos_a, pos_b) - pub fn newlines_in_range(&self, pos_a: uint, pos_b: uint) -> uint { - if pos_a > pos_b { - panic!("newlines_in_range(): pos_a must be less than or equal to pos_b."); - } - - if pos_a == pos_b { - return 0; - } - - match self.data { - TextNodeData::Leaf(ref tb) => { - let mut iter = tb.as_str().chars(); - let mut count: uint = 0; - let mut i: uint = 0; - - for c in iter { - if i >= pos_b { - break; - } - - if i >= pos_a && c == '\n' { - count += 1; - } - - i += 1; - } - - return count; - }, - - TextNodeData::Branch(ref left, ref right) => { - let mut count: uint = 0; - - // Left - if pos_a == 0 && pos_b >= left.char_count { - count += left.newline_count; - } - else if pos_a < left.char_count { - count += left.newlines_in_range(pos_a, pos_b); - } - - // Right - if pos_a <= left.char_count && pos_b >= self.char_count { - count += right.newline_count; - } - else if pos_a < self.char_count && pos_b >= left.char_count { - let pa = if pos_a > left.char_count {pos_a - left.char_count} else {0}; - count += right.newlines_in_range(pa, pos_b - left.char_count); - } - - return count; - } - } - } - - - /// Starting at pos, find the end of the line and return its position - pub fn end_of_line(&self, pos: uint) -> uint { - match self.data { - TextNodeData::Leaf(ref tb) => { - let mut iter = tb.as_str().chars(); - let mut i: uint = 0; - - for c in iter { - if i >= pos { - if c == '\n' { - break; - } - } - - i += 1; - } - - return i; - }, - - TextNodeData::Branch(ref left, ref right) => { - if (left.char_count - left.tail_len()) > pos { - return left.end_of_line(pos); - } - else { - return right.end_of_line(pos - left.char_count); - } - } - } - } - - - /// Returns the number of characters after the last newline in the node, - /// or the total character length of the node if there are no newlines. - pub fn tail_len(&self) -> uint { - match self.data { - TextNodeData::Leaf(ref tb) => { - let mut iter = tb.as_str().chars(); - let mut tlen = 0; - - for c in iter { - if c == '\n' { - tlen = 0; - } - else { - tlen += 1; - } - } - - return tlen; - }, - - TextNodeData::Branch(ref left, ref right) => { - if right.newline_count > 0 { - return right.tail_len(); - } - else { - return left.tail_len() + right.char_count; - } - } - } - } - - -} - -impl fmt::Show for TextNode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.data { - TextNodeData::Leaf(ref tb) => { - tb.fmt(f) - }, - - TextNodeData::Branch(ref left, ref right) => { - try!(left.fmt(f)); - right.fmt(f) - } - } - } -} \ No newline at end of file diff --git a/src/editor.rs b/src/editor.rs index e426720..66c62ab 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use buffer::TextBuffer; +use buffer::Buffer; use std::path::Path; use files::{load_file_to_buffer, save_buffer_to_file}; use string_utils::char_count; diff --git a/src/main.rs b/src/main.rs index 22a7b30..12a67e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,14 +4,14 @@ extern crate "rustc-serialize" as rustc_serialize; use std::path::Path; use docopt::Docopt; -use editor::Editor; -use term_ui::TermUI; +//use editor::Editor; +//use term_ui::TermUI; mod string_utils; mod buffer; -mod files; -mod editor; -mod term_ui; +//mod files; +//mod editor; +//mod term_ui; @@ -40,17 +40,17 @@ fn main() { // Get command-line arguments let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()); - // Load file, if specified - let editor = if let Option::Some(s) = args.arg_file { - Editor::new_from_file(&Path::new(s.as_slice())) - } - else { - Editor::new() - }; - - // Initialize and start UI - let mut ui = TermUI::new_from_editor(editor); - ui.ui_loop(); + // // Load file, if specified + // let editor = if let Option::Some(s) = args.arg_file { + // Editor::new_from_file(&Path::new(s.as_slice())) + // } + // else { + // Editor::new() + // }; + // + // // Initialize and start UI + // let mut ui = TermUI::new_from_editor(editor); + // ui.ui_loop(); //println!("{}", editor.buffer.root.tree_height); } diff --git a/src/string_utils.rs b/src/string_utils.rs index f02d058..ed18391 100644 --- a/src/string_utils.rs +++ b/src/string_utils.rs @@ -34,6 +34,14 @@ pub fn char_count(text: &str) -> uint { return count; } +pub fn grapheme_count(text: &str) -> uint { + let mut count = 0; + for _ in text.graphemes(true) { + count += 1; + } + return count; +} + pub fn char_and_newline_count(text: &str) -> (uint, uint) { let mut char_count = 0; let mut newline_count = 0;