Sped up file loading and saving.
This commit is contained in:
parent
02fdbcf027
commit
7099e4d0d3
|
@ -102,6 +102,92 @@ 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 {
|
||||
// Initialize Line
|
||||
let mut tl = Line {
|
||||
text: text.into_bytes(),
|
||||
ending: LineEnding::None,
|
||||
};
|
||||
|
||||
// Check for line ending
|
||||
let mut le_size: uint = 0;
|
||||
let text_size = tl.text.len();
|
||||
if tl.text.len() >= 3 {
|
||||
match unsafe{mem::transmute::<&[u8], &str>(tl.text.slice_from(text_size-3))} {
|
||||
// LS
|
||||
"\u{2028}" => {
|
||||
tl.ending = LineEnding::LS;
|
||||
le_size = 3;
|
||||
},
|
||||
|
||||
// PS
|
||||
"\u{2029}" => {
|
||||
tl.ending = LineEnding::PS;
|
||||
le_size = 3;
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
else if le_size == 0 && tl.text.len() >= 2 {
|
||||
match unsafe{mem::transmute::<&[u8], &str>(tl.text.slice_from(text_size-2))} {
|
||||
// CRLF
|
||||
"\u{000D}\u{000A}" => {
|
||||
tl.ending = LineEnding::CRLF;
|
||||
le_size = 2;
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
else if le_size == 0 && tl.text.len() >= 1 {
|
||||
match unsafe{mem::transmute::<&[u8], &str>(tl.text.slice_from(text_size-1))} {
|
||||
// LF or CRLF
|
||||
"\u{000A}" => {
|
||||
tl.ending = LineEnding::LF;
|
||||
le_size = 1;
|
||||
},
|
||||
|
||||
// VT
|
||||
"\u{000B}" => {
|
||||
tl.ending = LineEnding::VT;
|
||||
le_size = 1;
|
||||
},
|
||||
|
||||
// FF
|
||||
"\u{000C}" => {
|
||||
tl.ending = LineEnding::FF;
|
||||
le_size = 1;
|
||||
},
|
||||
|
||||
// CR
|
||||
"\u{000D}" => {
|
||||
tl.ending = LineEnding::CR;
|
||||
le_size = 1;
|
||||
},
|
||||
|
||||
// NEL
|
||||
"\u{0085}" => {
|
||||
tl.ending = LineEnding::NEL;
|
||||
le_size = 1;
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate off the line ending, if any
|
||||
let trunc_size = text_size - le_size;
|
||||
tl.text.truncate(trunc_size);
|
||||
|
||||
// Done!
|
||||
return tl;
|
||||
}
|
||||
|
||||
|
||||
/// Returns the total number of unicode graphemes in the line
|
||||
pub fn grapheme_count(&self) -> uint {
|
||||
let mut count = grapheme_count(self.as_str());
|
||||
|
@ -340,6 +426,10 @@ pub fn str_to_line_ending(g: &str) -> LineEnding {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn line_ending_to_str(ending: LineEnding) -> &'static str {
|
||||
LINE_ENDINGS[ending as uint]
|
||||
}
|
||||
|
||||
/// An array of string literals corresponding to the possible
|
||||
/// unicode line endings.
|
||||
pub const LINE_ENDINGS: [&'static str, ..9] = ["",
|
||||
|
|
|
@ -6,7 +6,7 @@ use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter};
|
|||
use self::line::{Line};
|
||||
use string_utils::{is_line_ending};
|
||||
|
||||
mod line;
|
||||
pub mod line;
|
||||
mod node;
|
||||
|
||||
|
||||
|
@ -50,6 +50,14 @@ impl Buffer {
|
|||
}
|
||||
|
||||
|
||||
/// Blindly appends a line to the end of the current text without
|
||||
/// doing any sanity checks. This is primarily for efficient
|
||||
/// file loading.
|
||||
pub fn append_line_unchecked(&mut self, line: Line) {
|
||||
self.root.append_line_unchecked_recursive(line);
|
||||
}
|
||||
|
||||
|
||||
/// 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
|
||||
|
|
|
@ -462,6 +462,27 @@ impl BufferNode {
|
|||
}
|
||||
|
||||
|
||||
pub fn append_line_unchecked_recursive(&mut self, line: Line) {
|
||||
let mut other_line = Line::new();
|
||||
|
||||
if let BufferNodeData::Branch(_, ref mut right) = self.data {
|
||||
right.append_line_unchecked_recursive(line);
|
||||
}
|
||||
else {
|
||||
if let BufferNodeData::Leaf(ref mut this_line) = self.data {
|
||||
mem::swap(this_line, &mut other_line);
|
||||
}
|
||||
|
||||
let new_node_a = box BufferNode::new_from_line(other_line);
|
||||
let new_node_b = box BufferNode::new_from_line(line);
|
||||
self.data = BufferNodeData::Branch(new_node_a, new_node_b);
|
||||
}
|
||||
|
||||
self.update_stats();
|
||||
self.rebalance();
|
||||
}
|
||||
|
||||
|
||||
/// Removes lines in line number range [line_a, line_b)
|
||||
pub fn remove_lines_recursive(&mut self, line_a: uint, line_b: uint) {
|
||||
let mut remove_left = false;
|
||||
|
|
29
src/files.rs
29
src/files.rs
|
@ -2,33 +2,40 @@ use std::io::{IoResult, BufferedReader, BufferedWriter};
|
|||
use std::io::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
use buffer::line::{Line, LineEnding, line_ending_to_str};
|
||||
use buffer::Buffer as TextBuffer;
|
||||
|
||||
pub fn load_file_to_buffer(path: &Path) -> IoResult<TextBuffer> {
|
||||
let mut tb = TextBuffer::new();
|
||||
let mut f = BufferedReader::new(try!(File::open(path)));
|
||||
let mut last_line_breaks = true;
|
||||
|
||||
loop {
|
||||
let line = f.read_line();
|
||||
if let Ok(ref s) = line {
|
||||
let tbl = tb.len();
|
||||
tb.insert_text(s.as_slice(), tbl);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
for line in f.lines() {
|
||||
let l = Line::new_from_string_unchecked(line.unwrap());
|
||||
last_line_breaks = l.ending != LineEnding::None;
|
||||
tb.append_line_unchecked(l);
|
||||
}
|
||||
|
||||
// If the last line had a line break, we need to add a final
|
||||
// blank line.
|
||||
if last_line_breaks {
|
||||
tb.append_line_unchecked(Line::new());
|
||||
}
|
||||
|
||||
// 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.grapheme_iter();
|
||||
let mut iter = tb.line_iter();
|
||||
let mut f = BufferedWriter::new(try!(File::create(path)));
|
||||
|
||||
for g in iter {
|
||||
let _ = f.write_str(g);
|
||||
for l in iter {
|
||||
let _ = f.write_str(l.as_str());
|
||||
let _ = f.write_str(line_ending_to_str(l.ending));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
|
|
Loading…
Reference in New Issue
Block a user