diff --git a/src/buffer/line.rs b/src/buffer/line.rs index 14e38a8..2142af7 100644 --- a/src/buffer/line.rs +++ b/src/buffer/line.rs @@ -103,16 +103,15 @@ impl Line { } - /// Creates a new Line from a string. - /// Does not check to see if the string has internal newlines. - /// This is primarily used for efficient loading of files. - pub fn new_from_string_unchecked(text: String) -> Line { + pub fn new_from_str_unchecked(text: &str) -> Line { // Initialize Line let mut tl = Line { - text: text.into_bytes(), + text: Vec::new(), ending: LineEnding::None, }; + tl.text.push_all(text.as_bytes()); + // Check for line ending let mut le_size: usize = 0; let text_size = tl.text.len(); @@ -191,6 +190,14 @@ impl Line { } + /// Creates a new Line from a string. + /// Does not check to see if the string has internal newlines. + /// This is primarily used for efficient loading of files. + pub fn new_from_string_unchecked(text: String) -> Line { + return Line::new_from_str_unchecked(text.as_slice()); + } + + /// Returns the total number of unicode graphemes in the line pub fn grapheme_count(&self) -> usize { let mut count = grapheme_count(self.as_str()); diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index ef64675..5d30951 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -1,6 +1,9 @@ #![allow(dead_code)] use std::mem; +use std::path::Path; +use std::old_io::fs::File; +use std::old_io::{IoResult, BufferedReader}; use self::line::Line; use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter}; @@ -22,6 +25,7 @@ mod undo_stack; /// A text buffer pub struct Buffer { text: BufferNode, + file_path: Option, undo_stack: UndoStack, pub formatter: T, } @@ -31,10 +35,60 @@ impl Buffer { pub fn new(formatter: T) -> Buffer { Buffer { text: BufferNode::new(&formatter), + file_path: None, undo_stack: UndoStack::new(), formatter: formatter, } } + + + pub fn new_from_file(formatter: T, path: &Path) -> IoResult> { + let mut f = BufferedReader::new(try!(File::open(path))); + + let mut buf = Buffer { + text: BufferNode::new(&formatter), + file_path: Some(path.clone()), + undo_stack: UndoStack::new(), + formatter: formatter, + }; + + let string = f.read_to_string().unwrap(); + let mut g_iter = string.as_slice().grapheme_indices(true); + let mut done = false; + let mut a = 0; + let mut b = 0; + + while !done { + let mut count = 0; + loop { + if let Some((i, g)) = g_iter.next() { + count += 1; + b = i + g.len(); + if is_line_ending(g) { + break; + } + } + else { + done = true; + break; + } + } + + if a != b { + let substr = &string[a..b]; + let line = Line::new_from_str_unchecked(substr); + let node = BufferNode::new_from_line_with_count_unchecked(&buf.formatter, line, count); + buf.append_leaf_node_unchecked(node); + } + + a = b; + } + + // Remove initial blank line + buf.remove_lines(0, 1); + + return Ok(buf); + } @@ -206,6 +260,11 @@ impl Buffer { } + fn append_leaf_node_unchecked(&mut self, node: BufferNode) { + self.text.append_leaf_node_unchecked_recursive(&self.formatter, node); + } + + /// Runs the formatter on all of the text. Should be run whenever the /// formatter has been changed. pub fn reformat(&mut self) { diff --git a/src/buffer/node.rs b/src/buffer/node.rs index 414d649..366b38c 100644 --- a/src/buffer/node.rs +++ b/src/buffer/node.rs @@ -49,6 +49,17 @@ impl BufferNode { } + pub fn new_from_line_with_count_unchecked(_: &T, line: Line, grapheme_count: usize) -> BufferNode { + BufferNode { + data: BufferNodeData::Leaf(line), + tree_height: 1, + grapheme_count: grapheme_count, + line_count: 1, + vis_dim: (1, grapheme_count), + } + } + + fn update_height(&mut self) { match self.data { BufferNodeData::Leaf(_) => { @@ -585,6 +596,21 @@ impl BufferNode { } + pub fn append_leaf_node_unchecked_recursive(&mut self, f: &T, node: BufferNode) { + if let BufferNodeData::Branch(_, ref mut right) = self.data { + right.append_leaf_node_unchecked_recursive(f, node); + } + else { + let mut new_left_node = BufferNode::new(f); + mem::swap(self, &mut new_left_node); + self.data = BufferNodeData::Branch(Box::new(new_left_node), Box::new(node)); + } + + self.update_stats(f); + self.rebalance(f); + } + + /// Removes lines in line number range [line_a, line_b) pub fn remove_lines_recursive(&mut self, f: &T, line_a: usize, line_b: usize) { let mut remove_left = false; diff --git a/src/editor/mod.rs b/src/editor/mod.rs index 606624f..0aefeb8 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -6,7 +6,7 @@ use buffer::line_formatter::LineFormatter; use buffer::line_formatter::RoundingBehavior::*; use std::path::Path; use std::cmp::{min, max}; -use files::{load_file_to_buffer, save_buffer_to_file}; +use files::{save_buffer_to_file}; use string_utils::grapheme_count; use self::cursor::CursorSet; @@ -47,7 +47,8 @@ impl Editor { } pub fn new_from_file(formatter: T, path: &Path) -> Editor { - let buf = match load_file_to_buffer(path, formatter) { + //let buf = match load_file_to_buffer(path, formatter) { + let buf = match Buffer::new_from_file(formatter, path) { Ok(b) => {b}, // TODO: handle un-openable file better _ => panic!("Could not open file!"), diff --git a/src/files.rs b/src/files.rs index 01f53c3..b06751d 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,26 +1,11 @@ -use std::old_io::{IoResult, BufferedReader, BufferedWriter}; +use std::old_io::{IoResult, BufferedWriter}; use std::old_io::fs::File; use std::path::Path; -use buffer::line::{Line, line_ending_to_str}; +use buffer::line::{line_ending_to_str}; use buffer::line_formatter::LineFormatter; use buffer::Buffer as TextBuffer; -pub fn load_file_to_buffer(path: &Path, lf: T) -> IoResult> { - let mut tb = TextBuffer::new(lf); - let mut f = BufferedReader::new(try!(File::open(path))); - - for line in f.lines() { - let l = Line::new_from_string_unchecked(line.unwrap()); - tb.append_line_unchecked(l); - } - - // Remove initial blank line - tb.remove_lines(0, 1); - - return Ok(tb); -} - pub fn save_buffer_to_file(tb: &TextBuffer, path: &Path) -> IoResult<()> { // TODO: make save atomic let mut iter = tb.line_iter(); @@ -32,4 +17,4 @@ pub fn save_buffer_to_file(tb: &TextBuffer, path: &Path) -> } return Ok(()); -} \ No newline at end of file +} diff --git a/todo.md b/todo.md index e0f89de..7a8df3e 100644 --- a/todo.md +++ b/todo.md @@ -48,8 +48,8 @@ - Start with the emacs approach, and you can always migrate to something more sophisticated later. -- Custom line iterator code for file loading, because rust's built-in one - only recognizes LF and CRLF. +//- Custom line iterator code for file loading, because rust's built-in one +// only recognizes LF and CRLF. - File loading is currently very slow. Investigate. - Both Emacs and Vim do line-wrapping extremely efficiently, even for very large files. Investigate how they do this.