From 9c302620e9edf7a479a0d5a40bf41aa2186930b6 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sat, 30 Dec 2017 23:10:56 -0800 Subject: [PATCH] Updating to use the new Ropey. This is mainly just for kicks and giggles, since it really won't work quite properly anyway, due to the lack of grapheme indexing. The next real thing is to... well, start from scratch. --- Cargo.lock | 13 +- Cargo.toml | 11 +- src/buffer/mod.rs | 313 +---- src/buffer/undo_stack.rs | 5 - src/editor/cursor.rs | 10 +- src/editor/mod.rs | 176 ++- src/formatter.rs | 127 +- src/main.rs | 18 +- src/string_utils.rs | 58 +- src/term_ui/formatter.rs | 509 +++++--- src/term_ui/mod.rs | 262 ++-- src/utils.rs | 1 - sub_crates/ropey/Cargo.toml | 8 - sub_crates/ropey/src/benches.rs | 266 ---- sub_crates/ropey/src/lib.rs | 1486 --------------------- sub_crates/ropey/src/string_utils.rs | 481 ------- sub_crates/ropey/src/tests.rs | 1772 -------------------------- 17 files changed, 729 insertions(+), 4787 deletions(-) delete mode 100644 sub_crates/ropey/Cargo.toml delete mode 100644 sub_crates/ropey/src/benches.rs delete mode 100644 sub_crates/ropey/src/lib.rs delete mode 100644 sub_crates/ropey/src/string_utils.rs delete mode 100644 sub_crates/ropey/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 341395f..2fb02e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = "0.0.1" dependencies = [ "docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "ropey 0.3.0", + "ropey 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustbox 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -253,8 +253,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ropey" -version = "0.3.0" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -275,6 +277,11 @@ name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "smallvec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.5.2" @@ -370,8 +377,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" +"checksum ropey 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6eb4b74f76bc72338d8aebb193e9575398703552530e329dae718e38e4bf2ca0" "checksum rustbox 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67db7a8f30d0cadb67687a8c25ee32aaa57bf9d6497fd0fc92584b267bad975a" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" "checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c" "checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0" "checksum termbox-sys 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "e32daa27881ea4b2ef36e4972d6cdefb241c19ac48d318381b688d1a631b8760" diff --git a/Cargo.toml b/Cargo.toml index d8c734f..c15a410 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,3 @@ -[workspace] -members = [ - "sub_crates/ropey" -] - [package] name = "Led" version = "0.0.1" @@ -14,12 +9,10 @@ name = "led" path = "src/main.rs" [dependencies] +ropey = "0.5" rustc-serialize = "0.3.0" unicode-segmentation = "1.2" unicode-width = "0.1" docopt = "0.6" encoding = "0.2" -rustbox = "0.8" - -[dependencies.ropey] -path = "sub_crates/ropey" \ No newline at end of file +rustbox = "0.8" \ No newline at end of file diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 28c5ac4..30bc041 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -1,20 +1,18 @@ #![allow(dead_code)] -use std::mem; -use std::cmp::min; use std::path::{Path, PathBuf}; use std::fs::File; use std::io; use std::io::{BufReader, BufWriter, Read, Write}; -use ropey::{Rope, RopeSlice, RopeGraphemeIter, RopeLineIter}; +use ropey; +use ropey::{Rope, RopeSlice}; use self::undo_stack::UndoStack; use self::undo_stack::Operation::*; -use string_utils::grapheme_count; +use string_utils::char_count; mod undo_stack; - // ============================================================= // Buffer // ============================================================= @@ -26,8 +24,6 @@ pub struct Buffer { undo_stack: UndoStack, } - - impl Buffer { pub fn new() -> Buffer { Buffer { @@ -37,7 +33,6 @@ impl Buffer { } } - pub fn new_from_str(s: &str) -> Buffer { Buffer { text: Rope::from_str(s), @@ -46,7 +41,6 @@ impl Buffer { } } - pub fn new_from_file(path: &Path) -> io::Result { let mut f = BufReader::new(try!(File::open(path))); let mut string = String::new(); @@ -61,127 +55,82 @@ impl Buffer { return Ok(buf); } - pub fn save_to_file(&self, path: &Path) -> io::Result<()> { let mut f = BufWriter::new(try!(File::create(path))); - for c in self.text.chunk_iter() { + for c in self.text.chunks() { let _ = f.write(c.as_bytes()); } return Ok(()); } - - - // ------------------------------------------------------------------------ // Functions for getting information about the buffer. // ------------------------------------------------------------------------ pub fn char_count(&self) -> usize { - self.text.char_count() + self.text.len_chars() } - pub fn grapheme_count(&self) -> usize { - self.text.grapheme_count() + // TODO: be correct + self.text.len_chars() } - pub fn line_count(&self) -> usize { - self.text.line_ending_count() + 1 + self.text.len_lines() } - - - // ------------------------------------------------------------------------ // Editing operations // ------------------------------------------------------------------------ /// Insert 'text' at grapheme position 'pos'. pub fn insert_text(&mut self, text: &str, pos: usize) { - let cpos = self.text.grapheme_index_to_char_index(pos); - self._insert_text(text, cpos); + self.text.insert(pos, text); - self.undo_stack.push(InsertText(text.to_string(), cpos)); + self.undo_stack.push(InsertText(text.to_string(), pos)); } - fn _insert_text(&mut self, text: &str, pos: usize) { - self.text.insert_text_at_char_index(text, pos); - } - - /// Remove the text before grapheme position 'pos' of length 'len'. pub fn remove_text_before(&mut self, pos: usize, len: usize) { if pos >= len { - let cpos_a = self.text.grapheme_index_to_char_index(pos); - let cpos_b = self.text.grapheme_index_to_char_index(pos - len); - let removed_text = self.string_from_range(cpos_b, cpos_a); + let removed_text = self.text.slice(pos - len, pos).to_string(); - self._remove_text(cpos_b, cpos_a); + self.text.remove(pos - len, pos); // Push operation to the undo stack - self.undo_stack.push(RemoveTextBefore(removed_text, cpos_b)); + self.undo_stack + .push(RemoveTextBefore(removed_text, pos - len)); } else { - panic!("Buffer::remove_text_before(): attempt to remove text before beginning of \ - buffer."); + panic!( + "Buffer::remove_text_before(): attempt to remove text before beginning of \ + buffer." + ); } } /// Remove the text after grapheme position 'pos' of length 'len'. pub fn remove_text_after(&mut self, pos: usize, len: usize) { - let cpos_a = self.text.grapheme_index_to_char_index(pos); - let cpos_b = self.text.grapheme_index_to_char_index(pos + len); + let removed_text = self.text.slice(pos, pos + len).to_string(); - let removed_text = self.string_from_range(cpos_a, cpos_b); - - self._remove_text(cpos_a, cpos_b); + self.text.remove(pos, pos + len); // Push operation to the undo stack - self.undo_stack.push(RemoveTextAfter(removed_text, cpos_a)); + self.undo_stack.push(RemoveTextAfter(removed_text, pos)); } - fn _remove_text(&mut self, pos_a: usize, pos_b: usize) { - // Nothing to do - if pos_a == pos_b { - return; - } - // Bounds error - else if pos_a > pos_b { - panic!("Buffer::_remove_text(): pos_a must be less than or equal to pos_b."); - } - // Bounds error - else if pos_b > self.char_count() { - panic!("Buffer::_remove_text(): attempt to remove text past the end of buffer."); - } - // Complete removal of all text - else if pos_a == 0 && pos_b == self.text.char_count() { - let mut temp_node = Rope::new(); - mem::swap(&mut (self.text), &mut temp_node); - } - // All other cases - else { - self.text.remove_text_between_char_indices(pos_a, pos_b); - } - } - - /// Moves the text in [pos_a, pos_b) to begin at index pos_to. /// /// Note that pos_to is the desired index that the text will start at /// _after_ the operation, not the index before the operation. This is a /// subtle but important distinction. pub fn move_text(&mut self, pos_a: usize, pos_b: usize, pos_to: usize) { - let cpos_a = self.text.grapheme_index_to_char_index(pos_a); - let cpos_b = self.text.grapheme_index_to_char_index(pos_b); - let cpos_to = self.text.grapheme_index_to_char_index(pos_to); - - self._move_text(cpos_a, cpos_b, cpos_to); + self._move_text(pos_a, pos_b, pos_to); // Push operation to the undo stack - self.undo_stack.push(MoveText(cpos_a, cpos_b, cpos_to)); + self.undo_stack.push(MoveText(pos_a, pos_b, pos_to)); } fn _move_text(&mut self, pos_a: usize, pos_b: usize, pos_to: usize) { @@ -194,28 +143,27 @@ impl Buffer { panic!("Buffer::_move_text(): pos_a must be less than or equal to pos_b."); } // Bounds error - else if pos_b > self.grapheme_count() { + else if pos_b > self.text.len_chars() { panic!("Buffer::_move_text(): specified text range is beyond end of buffer."); } // Bounds error - else if pos_to > (self.grapheme_count() - (pos_b - pos_a)) { + else if pos_to > (self.text.len_chars() - (pos_b - pos_a)) { panic!("Buffer::_move_text(): specified text destination is beyond end of buffer."); } // Nothing to do, because entire text specified - else if pos_a == 0 && pos_b == self.char_count() { + else if pos_a == 0 && pos_b == self.text.len_chars() { return; } // All other cases else { // TODO: a more efficient implementation that directly // manipulates the node tree. - let s = self.string_from_range(pos_a, pos_b); - self._remove_text(pos_a, pos_b); - self._insert_text(&s[..], pos_to); + let s = self.text.slice(pos_a, pos_b).to_string(); + self.text.remove(pos_a, pos_b); + self.text.insert(pos_to, &s); } } - /// Removes the lines in line indices [line_a, line_b). /// TODO: undo pub fn remove_lines(&mut self, line_a: usize, line_b: usize) { @@ -231,36 +179,15 @@ impl Buffer { 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.text.line_ending_count() + 1) { - let mut temp_node = Rope::new(); - mem::swap(&mut (self.text), &mut temp_node); - } // All other cases else { - let a = if line_a > 0 { - self.text.line_index_to_char_index(line_a) - 1 - } else { - 0 - }; + let a = self.text.line_to_char(line_a); + let b = self.text.line_to_char(line_b); - let b = if line_b < self.line_count() { - if line_a > 0 { - self.text.line_index_to_char_index(line_b) - 1 - } else { - self.text.line_index_to_char_index(line_b) - } - } else { - self.text.char_count() - }; - - self.text.remove_text_between_char_indices(a, b); + self.text.remove(a, b); } } - - - // ------------------------------------------------------------------------ // Undo/redo functionality // ------------------------------------------------------------------------ @@ -271,19 +198,19 @@ impl Buffer { if let Some(op) = self.undo_stack.prev() { match op { InsertText(ref s, p) => { - let size = grapheme_count(&s[..]); - self._remove_text(p, p + size); + let size = char_count(s); + self.text.remove(p, p + size); return Some(p); } RemoveTextBefore(ref s, p) => { - let size = grapheme_count(&s[..]); - self._insert_text(&s[..], p); + let size = char_count(s); + self.text.insert(p, s); return Some(p + size); } RemoveTextAfter(ref s, p) => { - self._insert_text(&s[..], p); + self.text.insert(p, s); return Some(p); } @@ -302,21 +229,20 @@ impl Buffer { return None; } - /// Redoes the last undone operation, and returns a cursor position that /// the cursor should jump to, if any. pub fn redo(&mut self) -> Option { if let Some(op) = self.undo_stack.next() { match op { InsertText(ref s, p) => { - let size = grapheme_count(&s[..]); - self._insert_text(&s[..], p); + let size = char_count(s); + self.text.insert(p, s); return Some(p + size); } RemoveTextBefore(ref s, p) | RemoveTextAfter(ref s, p) => { - let size = grapheme_count(&s[..]); - self._remove_text(p, p + size); + let size = char_count(s); + self.text.remove(p, p + size); return Some(p); } @@ -334,37 +260,23 @@ impl Buffer { return None; } - - // ------------------------------------------------------------------------ // Position conversions // ------------------------------------------------------------------------ - /// Converts a grapheme index into a line number and grapheme-column + /// Converts a char index into a line number and char-column /// number. /// /// If the index is off the end of the text, returns the line and column /// number of the last valid text position. pub fn index_to_line_col(&self, pos: usize) -> (usize, usize) { - // Convert to char index - let cpos = if pos < self.text.grapheme_count() { - self.text.grapheme_index_to_char_index(pos) - } else { - self.text.char_count() - }; + let line = self.text.char_to_line(pos); + let line_pos = self.text.line_to_char(line); - let line = self.text.char_index_to_line_index(cpos); - let line_pos = self.text.line_index_to_char_index(line); - - // Convert back from char index - let gp = self.text.char_index_to_grapheme_index(cpos); - let gline_pos = self.text.char_index_to_grapheme_index(line_pos); - - return (line, gp - gline_pos); + return (line, pos - line_pos); } - - /// Converts a line number and grapheme-column number into a grapheme + /// Converts a line number and char-column number into a char /// index. /// /// If the column number given is beyond the end of the line, returns the @@ -372,112 +284,63 @@ impl Buffer { /// beyond the end of the buffer, returns the index of the buffer's last /// valid position. pub fn line_col_to_index(&self, pos: (usize, usize)) -> usize { - if pos.0 <= self.text.line_ending_count() { - let temp1 = self.text.line_index_to_char_index(pos.0); - let l_begin_pos = self.text.char_index_to_grapheme_index(temp1); - - let l_end_pos = if pos.0 < self.text.line_ending_count() { - let temp2 = self.text.line_index_to_char_index(pos.0 + 1); - self.text.char_index_to_grapheme_index(temp2) - 1 - } else { - self.text.grapheme_count() - }; - - return min(l_begin_pos + pos.1, l_end_pos); + if pos.0 < self.text.len_lines() { + let l_begin_pos = self.text.line_to_char(pos.0); + return (l_begin_pos + pos.1).min(self.text.len_chars()); } else { - return self.text.grapheme_count(); + return self.text.len_chars(); } } - // ------------------------------------------------------------------------ // Text reading functions // ------------------------------------------------------------------------ pub fn get_grapheme<'a>(&'a self, index: usize) -> &'a str { - if index >= self.grapheme_count() { - panic!("Buffer::get_grapheme(): index past last grapheme."); - } else { - return self.text.grapheme_at_index(index); - } + self.text + .slice(index, index + 1) + .graphemes() + .nth(0) + .unwrap() } - pub fn get_line<'a>(&'a self, index: usize) -> RopeSlice<'a> { - if index >= self.line_count() { - panic!("get_line(): index out of bounds."); - } - - let a = self.text.line_index_to_char_index(index); - let b = if (index + 1) < self.line_count() { - self.text.line_index_to_char_index(index + 1) - } else { - self.text.char_count() - }; - - return self.text.slice(a, b); + self.text.line(index) } - /// Creates a String from the buffer text in grapheme range [pos_a, posb). fn string_from_range(&self, pos_a: usize, pos_b: usize) -> String { - // Bounds checks - if pos_b < pos_a { - panic!("Buffer::string_from_range(): pos_a must be less than or equal to pos_b."); - } else if pos_b > self.grapheme_count() { - panic!("Buffer::string_from_range(): specified range is past end of buffer text."); - } - - let mut s = String::with_capacity(pos_b - pos_a); - - let mut i = 0; - let i_end = pos_b - pos_a; - - for g in self.text.grapheme_iter_at_index(pos_a) { - if i == i_end { - break; - } - - s.push_str(g); - - i += 1; - } - - return s; + self.text.slice(pos_a, pos_b).to_string() } - - // ------------------------------------------------------------------------ // Iterator creators // ------------------------------------------------------------------------ /// Creates an iterator at the first character - pub fn grapheme_iter<'a>(&'a self) -> RopeGraphemeIter<'a> { - self.text.grapheme_iter() + pub fn grapheme_iter<'a>(&'a self) -> ropey::iter::Graphemes<'a> { + self.text.graphemes() } - /// Creates an iterator starting at the specified grapheme index. /// If the index is past the end of the text, then the iterator will /// return None on next(). - pub fn grapheme_iter_at_index<'a>(&'a self, index: usize) -> RopeGraphemeIter<'a> { - self.text.grapheme_iter_at_index(index) + pub fn grapheme_iter_at_index<'a>(&'a self, index: usize) -> ropey::iter::Graphemes<'a> { + let len = self.text.len_chars(); + self.text.slice(index, len).graphemes() } - - pub fn line_iter<'a>(&'a self) -> RopeLineIter<'a> { - self.text.line_iter() + pub fn line_iter<'a>(&'a self) -> ropey::iter::Lines<'a> { + self.text.lines() } - - pub fn line_iter_at_index<'a>(&'a self, index: usize) -> RopeLineIter<'a> { - self.text.line_iter_at_index(index) + pub fn line_iter_at_index<'a>(&'a self, line_idx: usize) -> ropey::iter::Lines<'a> { + let start = self.text.line_to_char(line_idx); + let len = self.text.len_chars(); + self.text.slice(start, len).lines() } } - - // ================================================================ // TESTS // ================================================================ @@ -509,7 +372,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn insert_text_with_newlines() { let mut buf = Buffer::new(); @@ -534,7 +396,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn insert_text_in_non_empty_buffer_1() { let mut buf = Buffer::new(); @@ -566,7 +427,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn insert_text_in_non_empty_buffer_2() { let mut buf = Buffer::new(); @@ -598,7 +458,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn insert_text_in_non_empty_buffer_3() { let mut buf = Buffer::new(); @@ -629,7 +488,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn insert_text_in_non_empty_buffer_4() { let mut buf = Buffer::new(); @@ -660,7 +518,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn insert_text_in_non_empty_buffer_5() { let mut buf = Buffer::new(); @@ -692,7 +549,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn insert_text_in_non_empty_buffer_6() { let mut buf = Buffer::new(); @@ -724,7 +580,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn insert_text_in_non_empty_buffer_7() { let mut buf = Buffer::new(); @@ -760,7 +615,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_1() { let mut buf = Buffer::new(); @@ -804,7 +658,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_2() { let mut buf = Buffer::new(); @@ -839,7 +692,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_3() { let mut buf = Buffer::new(); @@ -874,7 +726,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_4() { let mut buf = Buffer::new(); @@ -915,7 +766,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_5() { let mut buf = Buffer::new(); @@ -950,7 +800,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_6() { let mut buf = Buffer::new(); @@ -971,7 +820,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_7() { let mut buf = Buffer::new(); @@ -994,7 +842,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_8() { let mut buf = Buffer::new(); @@ -1016,7 +863,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_9() { let mut buf = Buffer::new(); @@ -1042,7 +888,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_10() { let mut buf = Buffer::new(); @@ -1064,7 +909,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_text_11() { let mut buf = Buffer::new(); @@ -1091,7 +935,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn move_text_1() { let mut buf = Buffer::new(); @@ -1136,7 +979,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn move_text_2() { let mut buf = Buffer::new(); @@ -1181,7 +1023,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn move_text_3() { let mut buf = Buffer::new(); @@ -1226,7 +1067,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn move_text_4() { let mut buf = Buffer::new(); @@ -1271,7 +1111,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn move_text_5() { let mut buf = Buffer::new(); @@ -1316,7 +1155,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_lines_1() { let mut buf = Buffer::new(); @@ -1347,7 +1185,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_lines_2() { let mut buf = Buffer::new(); @@ -1378,7 +1215,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_lines_3() { let mut buf = Buffer::new(); @@ -1411,7 +1247,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_lines_4() { let mut buf = Buffer::new(); @@ -1444,7 +1279,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_lines_5() { let mut buf = Buffer::new(); @@ -1462,7 +1296,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn remove_lines_6() { let mut buf = Buffer::new(); @@ -1480,7 +1313,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn line_col_to_index_1() { let mut buf = Buffer::new(); @@ -1491,7 +1323,6 @@ mod tests { assert!(pos == 12); } - #[test] fn line_col_to_index_2() { let mut buf = Buffer::new(); @@ -1512,7 +1343,6 @@ mod tests { assert!(pos == 29); } - #[test] fn line_col_to_index_4() { let mut buf = Buffer::new(); @@ -1530,7 +1360,6 @@ mod tests { assert_eq!(buf.line_col_to_index((2, 1)), 13); } - #[test] fn index_to_line_col_1() { let mut buf = Buffer::new(); @@ -1541,7 +1370,6 @@ mod tests { assert!(pos == (1, 2)); } - #[test] fn index_to_line_col_2() { let mut buf = Buffer::new(); @@ -1565,7 +1393,6 @@ mod tests { assert_eq!(buf.index_to_line_col(14), (2, 0)); } - #[test] fn string_from_range_1() { let mut buf = Buffer::new(); @@ -1576,7 +1403,6 @@ mod tests { assert!(&s[..] == "i\nthere\npeo"); } - #[test] fn string_from_range_2() { let mut buf = Buffer::new(); @@ -1587,7 +1413,6 @@ mod tests { assert!(&s[..] == "Hi\nthere\npeople\nof\nthe\nworld!"); } - #[test] fn grapheme_iter_at_index_1() { let mut buf = Buffer::new(); @@ -1611,7 +1436,6 @@ mod tests { assert!(None == iter.next()); } - #[test] fn grapheme_iter_at_index_2() { let mut buf = Buffer::new(); @@ -1622,5 +1446,4 @@ mod tests { assert!(None == iter.next()); } - } diff --git a/src/buffer/undo_stack.rs b/src/buffer/undo_stack.rs index 474f8a9..bd646c2 100644 --- a/src/buffer/undo_stack.rs +++ b/src/buffer/undo_stack.rs @@ -1,6 +1,5 @@ use std::collections::LinkedList; - /// A text editing operation #[derive(Clone)] pub enum Operation { @@ -11,7 +10,6 @@ pub enum Operation { CompositeOp(Vec), } - /// An undo/redo stack of text editing operations pub struct UndoStack { stack_a: LinkedList, @@ -26,13 +24,11 @@ impl UndoStack { } } - pub fn push(&mut self, op: Operation) { self.stack_a.push_back(op); self.stack_b.clear(); } - pub fn prev(&mut self) -> Option { if let Some(op) = self.stack_a.pop_back() { self.stack_b.push_back(op.clone()); @@ -42,7 +38,6 @@ impl UndoStack { } } - pub fn next(&mut self) -> Option { if let Some(op) = self.stack_b.pop_back() { self.stack_a.push_back(op.clone()); diff --git a/src/editor/cursor.rs b/src/editor/cursor.rs index 314b2d4..593670d 100644 --- a/src/editor/cursor.rs +++ b/src/editor/cursor.rs @@ -16,7 +16,7 @@ use formatter::LineFormatter; #[derive(Copy, Clone)] pub struct Cursor { pub range: (usize, usize), // start, end - pub vis_start: usize, // start + pub vis_start: usize, // start } impl Cursor { @@ -32,17 +32,17 @@ impl Cursor { } } - /// A collection of cursors, managed to always be in a consistent /// state for multi-cursor editing. pub struct CursorSet { cursors: Vec, } - impl CursorSet { pub fn new() -> CursorSet { - CursorSet { cursors: vec![Cursor::new()] } + CursorSet { + cursors: vec![Cursor::new()], + } } pub fn add_cursor(&mut self, cursor: Cursor) { @@ -87,7 +87,6 @@ impl CursorSet { } } - impl Index for CursorSet { type Output = Cursor; @@ -96,7 +95,6 @@ impl Index for CursorSet { } } - impl IndexMut for CursorSet { fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut Cursor { &mut (self.cursors[_index]) diff --git a/src/editor/mod.rs b/src/editor/mod.rs index 284dab5..18fd2a7 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -4,14 +4,13 @@ use buffer::Buffer; use formatter::LineFormatter; use formatter::RoundingBehavior::*; use std::path::{Path, PathBuf}; -use std::cmp::{min, max}; +use std::cmp::{max, min}; use string_utils::{grapheme_count, str_to_line_ending, LineEnding}; use utils::digit_count; use self::cursor::CursorSet; mod cursor; - pub struct Editor { pub buffer: Buffer, pub formatter: T, @@ -33,7 +32,6 @@ pub struct Editor { pub cursors: CursorSet, } - impl Editor { /// Create a new blank editor pub fn new(formatter: T) -> Editor { @@ -52,7 +50,6 @@ impl Editor { } } - pub fn new_from_file(formatter: T, path: &Path) -> Editor { let buf = match Buffer::new_from_file(path) { Ok(b) => b, @@ -87,7 +84,6 @@ impl Editor { return ed; } - pub fn save_if_dirty(&mut self) { if self.dirty && self.file_path != PathBuf::new() { let _ = self.buffer.save_to_file(&self.file_path); @@ -95,7 +91,6 @@ impl Editor { } } - pub fn auto_detect_line_ending(&mut self) { let mut line_ending_histogram: [usize; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; @@ -103,8 +98,8 @@ impl Editor { let mut line_i: usize = 0; for line in self.buffer.line_iter() { // Get the line ending - let ending = if line.grapheme_count() > 0 { - let g = line.grapheme_at_index(line.grapheme_count() - 1); + let ending = if line.len_chars() > 0 { + let g = line.slice(line.len_chars() - 1, line.len_chars()).graphemes().nth(0).unwrap(); str_to_line_ending(g) } else { LineEnding::None @@ -172,18 +167,17 @@ impl Editor { } } - pub fn auto_detect_indentation_style(&mut self) { let mut tab_blocks: usize = 0; let mut space_blocks: usize = 0; let mut space_histogram: [usize; 9] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; - let mut last_indent = (false, 0usize); // (was_tabs, indent_count) + let mut last_indent = (false, 0usize); // (was_tabs, indent_count) // Collect statistics let mut line_i: usize = 0; for line in self.buffer.line_iter() { - let mut g_iter = line.grapheme_iter(); + let mut g_iter = line.graphemes(); match g_iter.next() { Some("\t") => { // Count leading tabs @@ -263,13 +257,11 @@ impl Editor { } } - pub fn update_dim(&mut self, h: usize, w: usize) { self.editor_dim = (h, w); self.update_view_dim(); } - pub fn update_view_dim(&mut self) { // TODO: generalize for non-terminal UI. Maybe this isn't where it // belongs, in fact. But for now, this is the easiest place to put @@ -277,11 +269,12 @@ impl Editor { let line_count_digits = digit_count(self.buffer.line_count() as u32, 10) as usize; // Minus 1 vertically for the header, minus one more than the digits in // the line count for the gutter. - self.view_dim = (self.editor_dim.0 - 1, - self.editor_dim.1 - line_count_digits - 1); + self.view_dim = ( + self.editor_dim.0 - 1, + self.editor_dim.1 - line_count_digits - 1, + ); } - pub fn undo(&mut self) { // TODO: handle multiple cursors properly if let Some(pos) = self.buffer.undo() { @@ -298,7 +291,6 @@ impl Editor { } } - pub fn redo(&mut self) { // TODO: handle multiple cursors properly if let Some(pos) = self.buffer.redo() { @@ -315,7 +307,6 @@ impl Editor { } } - /// Moves the editor's view the minimum amount to show the cursor pub fn move_view_to_cursor(&mut self) { // TODO: account for the horizontal offset of the editor view. @@ -325,27 +316,32 @@ impl Editor { // the closest cursor. // Find the first and last grapheme index visible within the editor. - let g_first = self.formatter - .index_set_horizontal_v2d(&self.buffer, self.view_pos.0, 0, Floor); - let mut g_last = self.formatter.index_offset_vertical_v2d(&self.buffer, - g_first, - self.view_dim.0 as isize, - (Floor, Floor)); - g_last = self.formatter - .index_set_horizontal_v2d(&self.buffer, g_last, self.view_dim.1, Floor); + let g_first = + self.formatter + .index_set_horizontal_v2d(&self.buffer, self.view_pos.0, 0, Floor); + let mut g_last = self.formatter.index_offset_vertical_v2d( + &self.buffer, + g_first, + self.view_dim.0 as isize, + (Floor, Floor), + ); + g_last = + self.formatter + .index_set_horizontal_v2d(&self.buffer, g_last, self.view_dim.1, Floor); // Adjust the view depending on where the cursor is if self.cursors[0].range.0 < g_first { self.view_pos.0 = self.cursors[0].range.0; } else if self.cursors[0].range.0 > g_last { - self.view_pos.0 = self.formatter.index_offset_vertical_v2d(&self.buffer, - self.cursors[0].range.0, - -(self.view_dim.0 as isize), - (Floor, Floor)); + self.view_pos.0 = self.formatter.index_offset_vertical_v2d( + &self.buffer, + self.cursors[0].range.0, + -(self.view_dim.0 as isize), + (Floor, Floor), + ); } } - pub fn insert_text_at_cursor(&mut self, text: &str) { self.cursors.make_consistent(); @@ -370,7 +366,6 @@ impl Editor { self.move_view_to_cursor(); } - pub fn insert_tab_at_cursor(&mut self) { self.cursors.make_consistent(); @@ -383,16 +378,17 @@ impl Editor { c.range.1 += offset; // Figure out how many spaces to insert - let vis_pos = self.formatter.index_to_horizontal_v2d(&self.buffer, c.range.0); + let vis_pos = self.formatter + .index_to_horizontal_v2d(&self.buffer, c.range.0); // TODO: handle tab settings - let next_tab_stop = ((vis_pos / self.soft_tab_width as usize) + 1) * - self.soft_tab_width as usize; + let next_tab_stop = + ((vis_pos / self.soft_tab_width as usize) + 1) * self.soft_tab_width as usize; let space_count = min(next_tab_stop - vis_pos, 8); - // Insert spaces - let space_strs = ["", " ", " ", " ", " ", " ", " ", " ", - " "]; + let space_strs = [ + "", " ", " ", " ", " ", " ", " ", " ", " " + ]; self.buffer.insert_text(space_strs[space_count], c.range.0); self.dirty = true; @@ -412,24 +408,17 @@ impl Editor { } } - pub fn backspace_at_cursor(&mut self) { self.remove_text_behind_cursor(1); } - pub fn insert_text_at_grapheme(&mut self, text: &str, pos: usize) { self.dirty = true; let buf_len = self.buffer.grapheme_count(); - self.buffer.insert_text(text, - if pos < buf_len { - pos - } else { - buf_len - }); + self.buffer + .insert_text(text, if pos < buf_len { pos } else { buf_len }); } - pub fn remove_text_behind_cursor(&mut self, grapheme_count: usize) { self.cursors.make_consistent(); @@ -466,7 +455,6 @@ impl Editor { self.move_view_to_cursor(); } - pub fn remove_text_in_front_of_cursor(&mut self, grapheme_count: usize) { self.cursors.make_consistent(); @@ -506,7 +494,6 @@ impl Editor { self.move_view_to_cursor(); } - pub fn remove_text_inside_cursor(&mut self) { self.cursors.make_consistent(); @@ -521,7 +508,8 @@ impl Editor { if c.range.0 < c.range.1 { let len = c.range.1 - c.range.0; - self.buffer.remove_text_before(c.range.0, c.range.1 - c.range.0); + self.buffer + .remove_text_before(c.range.0, c.range.1 - c.range.0); self.dirty = true; // Move cursor @@ -540,7 +528,6 @@ impl Editor { self.move_view_to_cursor(); } - pub fn cursor_to_beginning_of_buffer(&mut self) { self.cursors = CursorSet::new(); @@ -551,7 +538,6 @@ impl Editor { self.move_view_to_cursor(); } - pub fn cursor_to_end_of_buffer(&mut self) { let end = self.buffer.grapheme_count(); @@ -563,7 +549,6 @@ impl Editor { self.move_view_to_cursor(); } - pub fn cursor_left(&mut self, n: usize) { for c in self.cursors.iter_mut() { if c.range.0 >= n { @@ -580,7 +565,6 @@ impl Editor { self.move_view_to_cursor(); } - pub fn cursor_right(&mut self, n: usize) { for c in self.cursors.iter_mut() { c.range.1 += n; @@ -597,22 +581,25 @@ impl Editor { self.move_view_to_cursor(); } - pub fn cursor_up(&mut self, n: usize) { for c in self.cursors.iter_mut() { let vmove = -1 * (n * self.formatter.single_line_height()) as isize; - let mut temp_index = self.formatter.index_offset_vertical_v2d(&self.buffer, - c.range.0, - vmove, - (Round, Round)); + let mut temp_index = self.formatter.index_offset_vertical_v2d( + &self.buffer, + c.range.0, + vmove, + (Round, Round), + ); if temp_index == 0 { c.update_vis_start(&(self.buffer), &(self.formatter)); } else { - temp_index = self.formatter.index_set_horizontal_v2d(&self.buffer, - temp_index, - c.vis_start, - Round); + temp_index = self.formatter.index_set_horizontal_v2d( + &self.buffer, + temp_index, + c.vis_start, + Round, + ); } c.range.0 = temp_index; @@ -623,22 +610,25 @@ impl Editor { self.move_view_to_cursor(); } - pub fn cursor_down(&mut self, n: usize) { for c in self.cursors.iter_mut() { let vmove = (n * self.formatter.single_line_height()) as isize; - let mut temp_index = self.formatter.index_offset_vertical_v2d(&self.buffer, - c.range.0, - vmove, - (Round, Round)); + let mut temp_index = self.formatter.index_offset_vertical_v2d( + &self.buffer, + c.range.0, + vmove, + (Round, Round), + ); if temp_index == self.buffer.grapheme_count() { c.update_vis_start(&(self.buffer), &(self.formatter)); } else { - temp_index = self.formatter.index_set_horizontal_v2d(&self.buffer, - temp_index, - c.vis_start, - Round); + temp_index = self.formatter.index_set_horizontal_v2d( + &self.buffer, + temp_index, + c.vis_start, + Round, + ); } c.range.0 = temp_index; @@ -649,14 +639,15 @@ impl Editor { self.move_view_to_cursor(); } - pub fn page_up(&mut self) { - let move_amount = self.view_dim.0 - - max((self.view_dim.0 / 8), self.formatter.single_line_height()); - self.view_pos.0 = self.formatter.index_offset_vertical_v2d(&self.buffer, - self.view_pos.0, - -1 * move_amount as isize, - (Round, Round)); + let move_amount = + self.view_dim.0 - max((self.view_dim.0 / 8), self.formatter.single_line_height()); + self.view_pos.0 = self.formatter.index_offset_vertical_v2d( + &self.buffer, + self.view_pos.0, + -1 * move_amount as isize, + (Round, Round), + ); self.cursor_up(move_amount); @@ -664,14 +655,15 @@ impl Editor { self.move_view_to_cursor(); } - pub fn page_down(&mut self) { - let move_amount = self.view_dim.0 - - max((self.view_dim.0 / 8), self.formatter.single_line_height()); - self.view_pos.0 = self.formatter.index_offset_vertical_v2d(&self.buffer, - self.view_pos.0, - move_amount as isize, - (Round, Round)); + let move_amount = + self.view_dim.0 - max((self.view_dim.0 / 8), self.formatter.single_line_height()); + self.view_pos.0 = self.formatter.index_offset_vertical_v2d( + &self.buffer, + self.view_pos.0, + move_amount as isize, + (Round, Round), + ); self.cursor_down(move_amount); @@ -679,15 +671,15 @@ impl Editor { self.move_view_to_cursor(); } - pub fn jump_to_line(&mut self, n: usize) { let pos = self.buffer.line_col_to_index((n, 0)); self.cursors.truncate(1); - self.cursors[0].range.0 = self.formatter.index_set_horizontal_v2d(&self.buffer, - pos, - self.cursors[0] - .vis_start, - Round); + self.cursors[0].range.0 = self.formatter.index_set_horizontal_v2d( + &self.buffer, + pos, + self.cursors[0].vis_start, + Round, + ); self.cursors[0].range.1 = self.cursors[0].range.0; // Adjust view diff --git a/src/formatter.rs b/src/formatter.rs index 666226c..6f7eb9e 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -9,7 +9,6 @@ use buffer::Buffer; // lines. pub const LINE_BLOCK_LENGTH: usize = 4096; - #[derive(Copy, Clone, PartialEq)] pub enum RoundingBehavior { Round, @@ -17,31 +16,32 @@ pub enum RoundingBehavior { Ceiling, } - pub trait LineFormatter { fn single_line_height(&self) -> usize; /// Returns the 2d visual dimensions of the given text when formatted /// by the formatter. /// The text to be formatted is passed as a grapheme iterator. - fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize) where T: Iterator; - + fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize) + where + T: Iterator; /// Converts a grapheme index within a text into a visual 2d position. /// The text to be formatted is passed as a grapheme iterator. fn index_to_v2d<'a, T>(&'a self, g_iter: T, index: usize) -> (usize, usize) - where T: Iterator; - + where + T: Iterator; /// Converts a visual 2d position into a grapheme index within a text. /// The text to be formatted is passed as a grapheme iterator. - fn v2d_to_index<'a, T>(&'a self, - g_iter: T, - v2d: (usize, usize), - rounding: (RoundingBehavior, RoundingBehavior)) - -> usize - where T: Iterator; - + fn v2d_to_index<'a, T>( + &'a self, + g_iter: T, + v2d: (usize, usize), + rounding: (RoundingBehavior, RoundingBehavior), + ) -> usize + where + T: Iterator; fn index_to_horizontal_v2d(&self, buf: &Buffer, index: usize) -> usize { let (line_i, col_i) = buf.index_to_line_col(index); @@ -52,20 +52,20 @@ pub trait LineFormatter { // Get an iter into the right block let a = line_block * LINE_BLOCK_LENGTH; - let b = min(line.grapheme_count(), (line_block + 1) * LINE_BLOCK_LENGTH); - let g_iter = line.grapheme_iter_between_indices(a, b); + let b = min(line.len_chars(), (line_block + 1) * LINE_BLOCK_LENGTH); + let g_iter = line.slice(a, b).graphemes(); return self.index_to_v2d(g_iter, col_i_adjusted).1; } - /// Takes a grapheme index and a visual vertical offset, and returns the grapheme /// index after that visual offset is applied. - fn index_offset_vertical_v2d(&self, - buf: &Buffer, - index: usize, - offset: isize, - rounding: (RoundingBehavior, RoundingBehavior)) - -> usize { + fn index_offset_vertical_v2d( + &self, + buf: &Buffer, + index: usize, + offset: isize, + rounding: (RoundingBehavior, RoundingBehavior), + ) -> usize { // TODO: handle rounding modes // TODO: do this with bidirectional line iterator @@ -76,12 +76,13 @@ pub trait LineFormatter { let (line_block, col_i_adjusted) = block_index_and_offset(col_i); let mut line = buf.get_line(line_i); - let (mut y, x) = - self.index_to_v2d(line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, - min(line.grapheme_count(), - (line_block + 1) * - LINE_BLOCK_LENGTH)), - col_i_adjusted); + let (mut y, x) = self.index_to_v2d( + line.slice( + line_block * LINE_BLOCK_LENGTH, + min(line.len_chars(), (line_block + 1) * LINE_BLOCK_LENGTH), + ).graphemes(), + col_i_adjusted, + ); // First, find the right line while keeping track of the vertical offset let mut new_y = y as isize + offset; @@ -89,19 +90,17 @@ pub trait LineFormatter { let mut block_index: usize = line_block; loop { line = buf.get_line(line_i); - let (h, _) = - self.dimensions(line.grapheme_iter_between_indices(block_index * - LINE_BLOCK_LENGTH, - min(line.grapheme_count(), - (block_index + 1) * - LINE_BLOCK_LENGTH))); + let (h, _) = self.dimensions(line.slice( + block_index * LINE_BLOCK_LENGTH, + min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH), + ).graphemes()); if new_y >= 0 && new_y < h as isize { y = new_y as usize; break; } else { if new_y > 0 { - let is_last_block = block_index >= last_block_index(line.grapheme_count()); + let is_last_block = block_index >= last_block_index(line.len_chars()); // Check for off-the-end if is_last_block && (line_i + 1) >= buf.line_count() { @@ -124,11 +123,14 @@ pub trait LineFormatter { if block_index == 0 { line_i -= 1; line = buf.get_line(line_i); - block_index = last_block_index(line.grapheme_count()); + block_index = last_block_index(line.len_chars()); } else { block_index -= 1; } - let (h, _) = self.dimensions(line.grapheme_iter_between_indices(block_index * LINE_BLOCK_LENGTH, min(line.grapheme_count(), (block_index+1) * LINE_BLOCK_LENGTH))); + let (h, _) = self.dimensions(line.slice( + block_index * LINE_BLOCK_LENGTH, + min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH), + ).graphemes()); new_y += h as isize; } else { unreachable!(); @@ -138,52 +140,57 @@ pub trait LineFormatter { // Next, convert the resulting coordinates back into buffer-wide // coordinates. - let block_slice = line.slice(block_index * LINE_BLOCK_LENGTH, - min(line.grapheme_count(), - (block_index + 1) * LINE_BLOCK_LENGTH)); - let block_col_i = min(self.v2d_to_index(block_slice.grapheme_iter(), (y, x), rounding), - LINE_BLOCK_LENGTH - 1); + let block_slice = line.slice( + block_index * LINE_BLOCK_LENGTH, + min(line.len_chars(), (block_index + 1) * LINE_BLOCK_LENGTH), + ); + let block_col_i = min( + self.v2d_to_index(block_slice.graphemes(), (y, x), rounding), + LINE_BLOCK_LENGTH - 1, + ); col_i = (block_index * LINE_BLOCK_LENGTH) + block_col_i; return buf.line_col_to_index((line_i, col_i)); } - /// Takes a grapheme index and a desired visual horizontal position, and /// returns a grapheme index on the same visual line as the given index, /// but offset to have the desired horizontal position. - fn index_set_horizontal_v2d(&self, - buf: &Buffer, - index: usize, - horizontal: usize, - rounding: RoundingBehavior) - -> usize { + fn index_set_horizontal_v2d( + &self, + buf: &Buffer, + index: usize, + horizontal: usize, + rounding: RoundingBehavior, + ) -> usize { let (line_i, col_i) = buf.index_to_line_col(index); let line = buf.get_line(line_i); // Find the right block in the line, and the index within that block let (line_block, col_i_adjusted) = block_index_and_offset(col_i); let start_index = line_block * LINE_BLOCK_LENGTH; - let end_index = min(line.grapheme_count(), start_index + LINE_BLOCK_LENGTH); + let end_index = min(line.len_chars(), start_index + LINE_BLOCK_LENGTH); // Calculate the horizontal position - let (v, _) = self.index_to_v2d(line.grapheme_iter_between_indices(start_index, end_index), - col_i_adjusted); - let block_col_i = self.v2d_to_index(line.grapheme_iter_between_indices(start_index, - end_index), - (v, horizontal), - (RoundingBehavior::Floor, rounding)); + let (v, _) = self.index_to_v2d( + line.slice(start_index, end_index).graphemes(), + col_i_adjusted, + ); + let block_col_i = self.v2d_to_index( + line.slice(start_index, end_index).graphemes(), + (v, horizontal), + (RoundingBehavior::Floor, rounding), + ); let mut new_col_i = start_index + min(block_col_i, LINE_BLOCK_LENGTH - 1); // Make sure we're not pushing the index off the end of the line - if (line_i + 1) < buf.line_count() && new_col_i >= line.grapheme_count() && - line.grapheme_count() > 0 { - new_col_i = line.grapheme_count() - 1; + if (line_i + 1) < buf.line_count() && new_col_i >= line.len_chars() && line.len_chars() > 0 + { + new_col_i = line.len_chars() - 1; } return (index + new_col_i) - col_i; } - } pub fn block_index_and_offset(index: usize) -> (usize, usize) { diff --git a/src/main.rs b/src/main.rs index 6a3ae00..0213bcf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,13 @@ // #![feature(test)] // extern crate test; -extern crate rustbox; extern crate docopt; +extern crate encoding; +extern crate ropey; +extern crate rustbox; extern crate rustc_serialize; extern crate unicode_segmentation; extern crate unicode_width; -extern crate encoding; -extern crate ropey; // extern crate freetype; // extern crate sdl2; @@ -28,9 +28,6 @@ mod term_ui; // mod font; // mod gui; - - - // Usage documentation string static USAGE: &'static str = " Usage: led [options] [] @@ -41,7 +38,6 @@ Options: -h, --help Show this message "; - // Struct for storing command-line arguments #[derive(RustcDecodable, Debug)] struct Args { @@ -50,13 +46,11 @@ struct Args { flag_help: bool, } - - - fn main() { // Get command-line arguments - let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()); - + let args: Args = Docopt::new(USAGE) + .and_then(|d| d.decode()) + .unwrap_or_else(|e| e.exit()); // Initialize and start UI if args.flag_gui { diff --git a/src/string_utils.rs b/src/string_utils.rs index 0006ecf..2dc36bb 100644 --- a/src/string_utils.rs +++ b/src/string_utils.rs @@ -4,17 +4,10 @@ use std::iter::repeat; use unicode_segmentation::UnicodeSegmentation; - pub fn is_line_ending(text: &str) -> bool { match text { - "\u{000D}\u{000A}" | - "\u{000A}" | - "\u{000B}" | - "\u{000C}" | - "\u{000D}" | - "\u{0085}" | - "\u{2028}" | - "\u{2029}" => true, + "\u{000D}\u{000A}" | "\u{000A}" | "\u{000B}" | "\u{000C}" | "\u{000D}" | "\u{0085}" + | "\u{2028}" | "\u{2029}" => true, _ => false, } @@ -164,7 +157,6 @@ pub fn insert_text_at_grapheme_index(s: &mut String, text: &str, pos: usize) { // TODO: use copy_memory() let mut i = byte_pos; for g in UnicodeSegmentation::graphemes(text, true) { - for b in g.bytes() { byte_vec[i] = b; i += 1 @@ -175,8 +167,10 @@ pub fn insert_text_at_grapheme_index(s: &mut String, text: &str, pos: usize) { /// Removes the text between the given grapheme indices in the given string. pub fn remove_text_between_grapheme_indices(s: &mut String, pos_a: usize, pos_b: usize) { // Bounds checks - assert!(pos_a <= pos_b, - "remove_text_between_grapheme_indices(): pos_a must be less than or equal to pos_b."); + assert!( + pos_a <= pos_b, + "remove_text_between_grapheme_indices(): pos_a must be less than or equal to pos_b." + ); if pos_a == pos_b { return; @@ -225,25 +219,19 @@ pub fn split_string_at_grapheme_index(s1: &mut String, pos: usize) -> String { return s2; } - - - - - - /// Represents one of the valid Unicode line endings. /// Also acts as an index into `LINE_ENDINGS`. #[derive(PartialEq, Copy, Clone)] pub enum LineEnding { None = 0, // No line ending CRLF = 1, // CarriageReturn followed by LineFeed - LF = 2, // U+000A -- LineFeed - VT = 3, // U+000B -- VerticalTab - FF = 4, // U+000C -- FormFeed - CR = 5, // U+000D -- CarriageReturn - NEL = 6, // U+0085 -- NextLine - LS = 7, // U+2028 -- Line Separator - PS = 8, // U+2029 -- ParagraphSeparator + LF = 2, // U+000A -- LineFeed + VT = 3, // U+000B -- VerticalTab + FF = 4, // U+000C -- FormFeed + CR = 5, // U+000D -- CarriageReturn + NEL = 6, // U+0085 -- NextLine + LS = 7, // U+2028 -- Line Separator + PS = 8, // U+2029 -- ParagraphSeparator } pub fn str_to_line_ending(g: &str) -> LineEnding { @@ -305,12 +293,14 @@ pub fn line_ending_to_str(ending: LineEnding) -> &'static str { /// An array of string literals corresponding to the possible /// unicode line endings. -pub const LINE_ENDINGS: [&'static str; 9] = ["", - "\u{000D}\u{000A}", - "\u{000A}", - "\u{000B}", - "\u{000C}", - "\u{000D}", - "\u{0085}", - "\u{2028}", - "\u{2029}"]; +pub const LINE_ENDINGS: [&'static str; 9] = [ + "", + "\u{000D}\u{000A}", + "\u{000A}", + "\u{000B}", + "\u{000C}", + "\u{000D}", + "\u{0085}", + "\u{2028}", + "\u{2029}", +]; diff --git a/src/term_ui/formatter.rs b/src/term_ui/formatter.rs index 8203210..cc4272e 100644 --- a/src/term_ui/formatter.rs +++ b/src/term_ui/formatter.rs @@ -21,7 +21,6 @@ pub struct ConsoleLineFormatter { pub wrap_additional_indent: usize, } - impl ConsoleLineFormatter { pub fn new(tab_width: u8) -> ConsoleLineFormatter { ConsoleLineFormatter { @@ -47,7 +46,8 @@ impl ConsoleLineFormatter { } pub fn iter<'a, T>(&'a self, g_iter: T) -> ConsoleLineFormatterVisIter<'a, T> - where T: Iterator + where + T: Iterator, { ConsoleLineFormatterVisIter::<'a, T> { grapheme_iter: g_iter, @@ -61,15 +61,14 @@ impl ConsoleLineFormatter { } } - impl LineFormatter for ConsoleLineFormatter { fn single_line_height(&self) -> usize { return 1; } - fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize) - where T: Iterator + where + T: Iterator, { let mut dim: (usize, usize) = (0, 0); @@ -82,9 +81,9 @@ impl LineFormatter for ConsoleLineFormatter { return dim; } - fn index_to_v2d<'a, T>(&'a self, g_iter: T, index: usize) -> (usize, usize) - where T: Iterator + where + T: Iterator, { let mut pos = (0, 0); let mut i = 0; @@ -103,13 +102,14 @@ impl LineFormatter for ConsoleLineFormatter { return (pos.0, pos.1 + last_width); } - - fn v2d_to_index<'a, T>(&'a self, - g_iter: T, - v2d: (usize, usize), - _: (RoundingBehavior, RoundingBehavior)) - -> usize - where T: Iterator + fn v2d_to_index<'a, T>( + &'a self, + g_iter: T, + v2d: (usize, usize), + _: (RoundingBehavior, RoundingBehavior), + ) -> usize + where + T: Iterator, { // TODO: handle rounding modes let mut i = 0; @@ -129,13 +129,13 @@ impl LineFormatter for ConsoleLineFormatter { } } - // =================================================================== // An iterator that iterates over the graphemes in a line in a // manner consistent with the ConsoleFormatter. // =================================================================== pub struct ConsoleLineFormatterVisIter<'a, T> - where T: Iterator +where + T: Iterator, { grapheme_iter: T, f: &'a ConsoleLineFormatter, @@ -148,9 +148,9 @@ pub struct ConsoleLineFormatterVisIter<'a, T> word_i: usize, } - impl<'a, T> ConsoleLineFormatterVisIter<'a, T> - where T: Iterator +where + T: Iterator, { fn next_nowrap(&mut self, g: &'a str) -> Option<(&'a str, (usize, usize), usize)> { let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize); @@ -160,11 +160,11 @@ impl<'a, T> ConsoleLineFormatterVisIter<'a, T> return Some((g, pos, width)); } - - fn next_charwrap(&mut self, - g: &'a str, - wrap_width: usize) - -> Option<(&'a str, (usize, usize), usize)> { + fn next_charwrap( + &mut self, + g: &'a str, + wrap_width: usize, + ) -> Option<(&'a str, (usize, usize), usize)> { let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize); if (self.pos.1 + width) > wrap_width { @@ -174,16 +174,24 @@ impl<'a, T> ConsoleLineFormatterVisIter<'a, T> } if self.f.maintain_indent { - let pos = (self.pos.0 + self.f.single_line_height(), - self.indent + self.f.wrap_additional_indent); - self.pos = (self.pos.0 + self.f.single_line_height(), - self.indent + self.f.wrap_additional_indent + width); + let pos = ( + self.pos.0 + self.f.single_line_height(), + self.indent + self.f.wrap_additional_indent, + ); + self.pos = ( + self.pos.0 + self.f.single_line_height(), + self.indent + self.f.wrap_additional_indent + width, + ); return Some((g, pos, width)); } else { - let pos = (self.pos.0 + self.f.single_line_height(), - self.f.wrap_additional_indent); - self.pos = (self.pos.0 + self.f.single_line_height(), - self.f.wrap_additional_indent + width); + let pos = ( + self.pos.0 + self.f.single_line_height(), + self.f.wrap_additional_indent, + ); + self.pos = ( + self.pos.0 + self.f.single_line_height(), + self.f.wrap_additional_indent + width, + ); return Some((g, pos, width)); } } else { @@ -202,10 +210,9 @@ impl<'a, T> ConsoleLineFormatterVisIter<'a, T> } } - - impl<'a, T> Iterator for ConsoleLineFormatterVisIter<'a, T> - where T: Iterator +where + T: Iterator, { type Item = (&'a str, (usize, usize), usize); @@ -234,9 +241,11 @@ impl<'a, T> Iterator for ConsoleLineFormatterVisIter<'a, T> self.word_buf.truncate(0); while let Some(g) = self.grapheme_iter.next() { self.word_buf.push(g); - let width = grapheme_vis_width_at_vis_pos(g, - self.pos.1 + word_width, - self.f.tab_width as usize); + let width = grapheme_vis_width_at_vis_pos( + g, + self.pos.1 + word_width, + self.f.tab_width as usize, + ); word_width += width; if is_whitespace(g) { break; @@ -257,11 +266,15 @@ impl<'a, T> Iterator for ConsoleLineFormatterVisIter<'a, T> } if self.f.maintain_indent { - self.pos = (self.pos.0 + self.f.single_line_height(), - self.indent + self.f.wrap_additional_indent); + self.pos = ( + self.pos.0 + self.f.single_line_height(), + self.indent + self.f.wrap_additional_indent, + ); } else { - self.pos = (self.pos.0 + self.f.single_line_height(), - self.f.wrap_additional_indent); + self.pos = ( + self.pos.0 + self.f.single_line_height(), + self.f.wrap_additional_indent, + ); } } @@ -277,8 +290,6 @@ impl<'a, T> Iterator for ConsoleLineFormatterVisIter<'a, T> } } - - // =================================================================== // Helper functions // =================================================================== @@ -302,18 +313,15 @@ fn grapheme_vis_width_at_vis_pos(g: &str, pos: usize, tab_width: usize) -> usize } } - - #[cfg(test)] mod tests { #![allow(unused_imports)] use unicode_segmentation::UnicodeSegmentation; use super::*; use formatter::{LineFormatter, LINE_BLOCK_LENGTH}; - use formatter::RoundingBehavior::{Round, Floor, Ceiling}; + use formatter::RoundingBehavior::{Ceiling, Floor, Round}; use buffer::Buffer; - #[test] fn dimensions_1() { let text = "Hello there, stranger!"; // 22 graphemes long @@ -324,11 +332,12 @@ mod tests { f.wrap_additional_indent = 0; f.set_wrap_width(80); - assert_eq!(f.dimensions(UnicodeSegmentation::graphemes(text, true)), - (1, 22)); + assert_eq!( + f.dimensions(UnicodeSegmentation::graphemes(text, true)), + (1, 22) + ); } - #[test] fn dimensions_2() { let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long @@ -339,11 +348,12 @@ mod tests { f.wrap_additional_indent = 0; f.set_wrap_width(12); - assert_eq!(f.dimensions(UnicodeSegmentation::graphemes(text, true)), - (5, 12)); + assert_eq!( + f.dimensions(UnicodeSegmentation::graphemes(text, true)), + (5, 12) + ); } - #[test] fn index_to_v2d_1() { let text = "Hello there, stranger!"; // 22 graphemes long @@ -354,17 +364,24 @@ mod tests { f.wrap_additional_indent = 0; f.set_wrap_width(80); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0), - (0, 0)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5), - (0, 5)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 22), - (0, 22)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23), - (0, 22)); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0), + (0, 0) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5), + (0, 5) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 22), + (0, 22) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23), + (0, 22) + ); } - #[test] fn index_to_v2d_2() { let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long @@ -375,46 +392,77 @@ mod tests { f.wrap_additional_indent = 0; f.set_wrap_width(12); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0), - (0, 0)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5), - (0, 5)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 11), - (0, 11)); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0), + (0, 0) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5), + (0, 5) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 11), + (0, 11) + ); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 12), - (1, 0)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 15), - (1, 3)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23), - (1, 11)); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 12), + (1, 0) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 15), + (1, 3) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23), + (1, 11) + ); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 24), - (2, 0)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 28), - (2, 4)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 35), - (2, 11)); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 24), + (2, 0) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 28), + (2, 4) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 35), + (2, 11) + ); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 36), - (3, 0)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 43), - (3, 7)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 47), - (3, 11)); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 36), + (3, 0) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 43), + (3, 7) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 47), + (3, 11) + ); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 48), - (4, 0)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 50), - (4, 2)); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 56), - (4, 8)); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 48), + (4, 0) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 50), + (4, 2) + ); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 56), + (4, 8) + ); - assert_eq!(f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 57), - (4, 8)); + assert_eq!( + f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 57), + (4, 8) + ); } - #[test] fn v2d_to_index_1() { let text = "Hello there, stranger!"; // 22 graphemes long @@ -425,33 +473,56 @@ mod tests { f.wrap_additional_indent = 0; f.set_wrap_width(80); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (0, 0), - (Floor, Floor)), - 0); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (0, 5), - (Floor, Floor)), - 5); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (0, 22), - (Floor, Floor)), - 22); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (0, 23), - (Floor, Floor)), - 22); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (1, 0), - (Floor, Floor)), - 22); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (1, 1), - (Floor, Floor)), - 22); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (0, 0), + (Floor, Floor) + ), + 0 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (0, 5), + (Floor, Floor) + ), + 5 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (0, 22), + (Floor, Floor) + ), + 22 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (0, 23), + (Floor, Floor) + ), + 22 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (1, 0), + (Floor, Floor) + ), + 22 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (1, 1), + (Floor, Floor) + ), + 22 + ); } - #[test] fn v2d_to_index_2() { let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long @@ -462,77 +533,140 @@ mod tests { f.wrap_additional_indent = 0; f.set_wrap_width(12); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (0, 0), - (Floor, Floor)), - 0); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (0, 11), - (Floor, Floor)), - 11); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (0, 12), - (Floor, Floor)), - 11); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (0, 0), + (Floor, Floor) + ), + 0 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (0, 11), + (Floor, Floor) + ), + 11 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (0, 12), + (Floor, Floor) + ), + 11 + ); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (1, 0), - (Floor, Floor)), - 12); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (1, 11), - (Floor, Floor)), - 23); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (1, 12), - (Floor, Floor)), - 23); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (1, 0), + (Floor, Floor) + ), + 12 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (1, 11), + (Floor, Floor) + ), + 23 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (1, 12), + (Floor, Floor) + ), + 23 + ); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (2, 0), - (Floor, Floor)), - 24); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (2, 11), - (Floor, Floor)), - 35); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (2, 12), - (Floor, Floor)), - 35); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (2, 0), + (Floor, Floor) + ), + 24 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (2, 11), + (Floor, Floor) + ), + 35 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (2, 12), + (Floor, Floor) + ), + 35 + ); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (3, 0), - (Floor, Floor)), - 36); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (3, 11), - (Floor, Floor)), - 47); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (3, 12), - (Floor, Floor)), - 47); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (3, 0), + (Floor, Floor) + ), + 36 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (3, 11), + (Floor, Floor) + ), + 47 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (3, 12), + (Floor, Floor) + ), + 47 + ); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (4, 0), - (Floor, Floor)), - 48); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (4, 7), - (Floor, Floor)), - 55); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (4, 8), - (Floor, Floor)), - 56); - assert_eq!(f.v2d_to_index(UnicodeSegmentation::graphemes(text, true), - (4, 9), - (Floor, Floor)), - 56); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (4, 0), + (Floor, Floor) + ), + 48 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (4, 7), + (Floor, Floor) + ), + 55 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (4, 8), + (Floor, Floor) + ), + 56 + ); + assert_eq!( + f.v2d_to_index( + UnicodeSegmentation::graphemes(text, true), + (4, 9), + (Floor, Floor) + ), + 56 + ); } - #[test] fn index_to_horizontal_v2d_1() { let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long @@ -550,7 +684,6 @@ mod tests { assert_eq!(f.index_to_horizontal_v2d(&b, 56), 32); } - #[test] fn index_to_horizontal_v2d_2() { let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long @@ -578,7 +711,6 @@ mod tests { assert_eq!(f.index_to_horizontal_v2d(&b, 56), 8); } - #[test] fn index_set_horizontal_v2d_1() { let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long @@ -614,7 +746,6 @@ mod tests { assert_eq!(f.index_set_horizontal_v2d(&b, 55, 33, Floor), 55); } - #[test] fn index_set_horizontal_v2d_2() { let b = Buffer::new_from_str("Hello there, stranger! How are you doing this fine day?"); // 55 graphemes long @@ -650,7 +781,6 @@ mod tests { assert_eq!(f.index_set_horizontal_v2d(&b, 23, 12, Floor), 23); } - #[test] fn index_offset_vertical_v2d_1() { let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long @@ -678,7 +808,6 @@ mod tests { assert_eq!(f.index_offset_vertical_v2d(&b, 54, -1, (Floor, Floor)), 22); } - #[test] fn index_offset_vertical_v2d_2() { let b = Buffer::new_from_str("Hello there, stranger! How are you doing this fine day?"); // 55 graphemes long diff --git a/src/term_ui/mod.rs b/src/term_ui/mod.rs index 8802528..fd1db4f 100644 --- a/src/term_ui/mod.rs +++ b/src/term_ui/mod.rs @@ -4,7 +4,7 @@ use rustbox; use rustbox::Color; use editor::Editor; use std::time::Duration; -use formatter::{LineFormatter, LINE_BLOCK_LENGTH, block_index_and_offset}; +use formatter::{block_index_and_offset, LineFormatter, LINE_BLOCK_LENGTH}; use std::char; use std::default::Default; use std::cmp::min; @@ -34,7 +34,6 @@ const K_CTRL_S: u16 = 19; const K_CTRL_Y: u16 = 25; const K_CTRL_Z: u16 = 26; - pub struct TermUI { rb: rustbox::RustBox, editor: Editor, @@ -42,7 +41,6 @@ pub struct TermUI { height: usize, } - impl TermUI { pub fn new() -> TermUI { let rb = match rustbox::RustBox::init(rustbox::InitOptions { @@ -102,7 +100,6 @@ impl TermUI { self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1)); self.rb.present(); - // Handle events. We block on the first event, so that the // program doesn't loop like crazy, but then continue pulling // events in a non-blocking way until we run out of events @@ -216,7 +213,6 @@ impl TermUI { } } - fn go_to_line_ui_loop(&mut self) { let foreground = Color::Black; let background = Color::Cyan; @@ -233,18 +229,21 @@ impl TermUI { self.rb.clear(); self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1)); for i in 0..self.width { - self.rb.print(i, 0, rustbox::RB_NORMAL, foreground, background, " "); + self.rb + .print(i, 0, rustbox::RB_NORMAL, foreground, background, " "); } - self.rb.print(1, 0, rustbox::RB_NORMAL, foreground, background, prefix); - self.rb.print(prefix.len() + 1, - 0, - rustbox::RB_NORMAL, - foreground, - background, - &line[..]); + self.rb + .print(1, 0, rustbox::RB_NORMAL, foreground, background, prefix); + self.rb.print( + prefix.len() + 1, + 0, + rustbox::RB_NORMAL, + foreground, + background, + &line[..], + ); self.rb.present(); - // Handle events. We block on the first event, so that the // program doesn't loop like crazy, but then continue pulling // events in a non-blocking way until we run out of events @@ -295,7 +294,6 @@ impl TermUI { e = self.rb.peek_event(Duration::from_millis(0), true); // Get next event (if any) } - // Cancel if flag is set if cancel { break; @@ -316,50 +314,52 @@ impl TermUI { } } - - fn draw_editor(&self, - editor: &Editor, - c1: (usize, usize), - c2: (usize, usize)) { + fn draw_editor( + &self, + editor: &Editor, + c1: (usize, usize), + c2: (usize, usize), + ) { let foreground = Color::Black; let background = Color::Cyan; // Fill in top row with info line color for i in c1.1..(c2.1 + 1) { - self.rb.print(i, c1.0, rustbox::RB_NORMAL, foreground, background, " "); + self.rb + .print(i, c1.0, rustbox::RB_NORMAL, foreground, background, " "); } // Filename and dirty marker let filename = editor.file_path.display(); - let dirty_char = if editor.dirty { - "*" - } else { - "" - }; + let dirty_char = if editor.dirty { "*" } else { "" }; let name = format!("{}{}", filename, dirty_char); - self.rb.print(c1.1 + 1, - c1.0, - rustbox::RB_NORMAL, - foreground, - background, - &name[..]); + self.rb.print( + c1.1 + 1, + c1.0, + rustbox::RB_NORMAL, + foreground, + background, + &name[..], + ); // Percentage position in document // TODO: use view instead of cursor for calculation if there is more // than one cursor. let percentage: usize = if editor.buffer.grapheme_count() > 0 { - (((editor.cursors[0].range.0 as f32) / (editor.buffer.grapheme_count() as f32)) * - 100.0) as usize + (((editor.cursors[0].range.0 as f32) / (editor.buffer.grapheme_count() as f32)) * 100.0) + as usize } else { 100 }; let pstring = format!("{}%", percentage); - self.rb.print(c2.1 - pstring.len(), - c1.0, - rustbox::RB_NORMAL, - foreground, - background, - &pstring[..]); + self.rb.print( + c2.1 - pstring.len(), + c1.0, + rustbox::RB_NORMAL, + foreground, + background, + &pstring[..], + ); // Text encoding info and tab style let nl = match editor.line_ending_type { @@ -373,40 +373,48 @@ impl TermUI { LineEnding::LS => "LS", LineEnding::PS => "PS", }; - let soft_tabs_str = if editor.soft_tabs { - "spaces" - } else { - "tabs" - }; - let info_line = format!("UTF8:{} {}:{}", - nl, - soft_tabs_str, - editor.soft_tab_width as usize); - self.rb.print(c2.1 - 30, - c1.0, - rustbox::RB_NORMAL, - foreground, - background, - &info_line[..]); + let soft_tabs_str = if editor.soft_tabs { "spaces" } else { "tabs" }; + let info_line = format!( + "UTF8:{} {}:{}", + nl, soft_tabs_str, editor.soft_tab_width as usize + ); + self.rb.print( + c2.1 - 30, + c1.0, + rustbox::RB_NORMAL, + foreground, + background, + &info_line[..], + ); // Draw main text editing area self.draw_editor_text(editor, (c1.0 + 1, c1.1), c2); } - - fn draw_editor_text(&self, - editor: &Editor, - c1: (usize, usize), - c2: (usize, usize)) { + fn draw_editor_text( + &self, + editor: &Editor, + c1: (usize, usize), + c2: (usize, usize), + ) { // Calculate all the starting info let gutter_width = editor.editor_dim.1 - editor.view_dim.1; let (line_index, col_i) = editor.buffer.index_to_line_col(editor.view_pos.0); let (mut line_block_index, _) = block_index_and_offset(col_i); - let mut grapheme_index = editor.buffer.line_col_to_index((line_index, - line_block_index * - LINE_BLOCK_LENGTH)); + let mut grapheme_index = editor + .buffer + .line_col_to_index((line_index, line_block_index * LINE_BLOCK_LENGTH)); let temp_line = editor.buffer.get_line(line_index); - let (vis_line_offset, _) = editor.formatter.index_to_v2d(temp_line.grapheme_iter_between_indices(line_block_index*LINE_BLOCK_LENGTH, min(temp_line.grapheme_count(), (line_block_index+1)*LINE_BLOCK_LENGTH)), editor.view_pos.0 - grapheme_index); + let (vis_line_offset, _) = editor.formatter.index_to_v2d( + temp_line.slice( + line_block_index * LINE_BLOCK_LENGTH, + min( + temp_line.len_chars(), + (line_block_index + 1) * LINE_BLOCK_LENGTH, + ), + ).graphemes(), + editor.view_pos.0 - grapheme_index, + ); let mut screen_line = c1.0 as isize - vis_line_offset as isize; let screen_col = c1.1 as isize + gutter_width as isize; @@ -414,7 +422,8 @@ impl TermUI { // Fill in the gutter with the appropriate background for y in c1.0..(c2.0 + 1) { for x in c1.1..(c1.1 + gutter_width - 1) { - self.rb.print(x, y, rustbox::RB_NORMAL, Color::White, Color::Blue, " "); + self.rb + .print(x, y, rustbox::RB_NORMAL, Color::White, Color::Blue, " "); } } @@ -425,12 +434,14 @@ impl TermUI { let lnx = c1.1 + (gutter_width - 1 - digit_count(line_num as u32, 10) as usize); let lny = screen_line as usize; if lny >= c1.0 && lny <= c2.0 { - self.rb.print(lnx, - lny, - rustbox::RB_NORMAL, - Color::White, - Color::Blue, - &format!("{}", line_num)[..]); + self.rb.print( + lnx, + lny, + rustbox::RB_NORMAL, + Color::White, + Color::Blue, + &format!("{}", line_num)[..], + ); } } @@ -439,8 +450,10 @@ impl TermUI { let mut line_g_index: usize = 0; let mut last_pos_y = 0; let mut lines_traversed: usize = 0; - let mut g_iter = editor.formatter.iter(line.grapheme_iter_at_index(line_block_index * - LINE_BLOCK_LENGTH)); + let line_len = line.len_chars(); + let mut g_iter = editor + .formatter + .iter(line.slice(line_block_index * LINE_BLOCK_LENGTH, line_len).graphemes()); loop { if let Some((g, (pos_y, pos_x), width)) = g_iter.next() { @@ -472,49 +485,59 @@ impl TermUI { // Actually print the character if is_line_ending(g) { if at_cursor { - self.rb.print(px as usize, - py as usize, - rustbox::RB_NORMAL, - Color::Black, - Color::White, - " "); + self.rb.print( + px as usize, + py as usize, + rustbox::RB_NORMAL, + Color::Black, + Color::White, + " ", + ); } } else if g == "\t" { for i in 0..width { let tpx = px as usize + i; if tpx <= c2.1 { - self.rb.print(tpx as usize, - py as usize, - rustbox::RB_NORMAL, - Color::White, - Color::Black, - " "); + self.rb.print( + tpx as usize, + py as usize, + rustbox::RB_NORMAL, + Color::White, + Color::Black, + " ", + ); } } if at_cursor { - self.rb.print(px as usize, - py as usize, - rustbox::RB_NORMAL, - Color::Black, - Color::White, - " "); + self.rb.print( + px as usize, + py as usize, + rustbox::RB_NORMAL, + Color::Black, + Color::White, + " ", + ); } } else { if at_cursor { - self.rb.print(px as usize, - py as usize, - rustbox::RB_NORMAL, - Color::Black, - Color::White, - g); + self.rb.print( + px as usize, + py as usize, + rustbox::RB_NORMAL, + Color::Black, + Color::White, + g, + ); } else { - self.rb.print(px as usize, - py as usize, - rustbox::RB_NORMAL, - Color::White, - Color::Black, - g); + self.rb.print( + px as usize, + py as usize, + rustbox::RB_NORMAL, + Color::White, + Color::Black, + g, + ); } } } @@ -528,8 +551,10 @@ impl TermUI { if line_g_index >= LINE_BLOCK_LENGTH { line_block_index += 1; line_g_index = 0; - g_iter = editor.formatter.iter(line.grapheme_iter_at_index(line_block_index * - LINE_BLOCK_LENGTH)); + let line_len = line.len_chars(); + g_iter = editor + .formatter + .iter(line.slice(line_block_index * LINE_BLOCK_LENGTH, line_len).graphemes()); lines_traversed += 1; } } @@ -539,7 +564,6 @@ impl TermUI { line_num += 1; } - // If we get here, it means we reached the end of the text buffer // without going off the bottom of the screen. So draw the cursor // at the end if needed. @@ -554,21 +578,23 @@ impl TermUI { if at_cursor { // Calculate the cell coordinates at which to draw the cursor - let pos_x = editor.formatter.index_to_horizontal_v2d(&self.editor.buffer, - self.editor - .buffer - .grapheme_count()); + let pos_x = editor + .formatter + .index_to_horizontal_v2d(&self.editor.buffer, self.editor.buffer.grapheme_count()); let px = pos_x as isize + screen_col - editor.view_pos.1 as isize; let py = screen_line - 1; - if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) && - (py <= c2.0 as isize) { - self.rb.print(px as usize, - py as usize, - rustbox::RB_NORMAL, - Color::Black, - Color::White, - " "); + if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) + && (py <= c2.0 as isize) + { + self.rb.print( + px as usize, + py as usize, + rustbox::RB_NORMAL, + Color::Black, + Color::White, + " ", + ); } } } diff --git a/src/utils.rs b/src/utils.rs index 9e68735..7f9d9bc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -9,7 +9,6 @@ pub fn digit_count(mut n: u32, b: u32) -> u32 { } } - #[cfg(test)] mod tests { use super::*; diff --git a/sub_crates/ropey/Cargo.toml b/sub_crates/ropey/Cargo.toml deleted file mode 100644 index b2096d6..0000000 --- a/sub_crates/ropey/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "ropey" -version = "0.3.0" -authors = ["Nathan Vegdahl "] -license = "MIT" - -[dependencies] -unicode-segmentation = "1.2" \ No newline at end of file diff --git a/sub_crates/ropey/src/benches.rs b/sub_crates/ropey/src/benches.rs deleted file mode 100644 index 5edad17..0000000 --- a/sub_crates/ropey/src/benches.rs +++ /dev/null @@ -1,266 +0,0 @@ -#![cfg(test)] -/* -use super::*; -use test::Bencher; - - -#[bench] -fn new_from_str_1(b: &mut Bencher) { - b.iter(|| { - let _ = Rope::from_str(" -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - }); -} - - -#[bench] -fn new_from_str_2(b: &mut Bencher) { - b.iter(|| { - let _ = Rope::from_str(" -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - }); -} - - -#[bench] -fn new_from_str_3(b: &mut Bencher) { - b.iter(|| { - let _ = Rope::from_str(" -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - }); -} - - -#[bench] -fn new_from_str_4(b: &mut Bencher) { - b.iter(|| { - let _ = Rope::from_str(" -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - }); -} - - -#[bench] -fn insert_text_bench_1(b: &mut Bencher) { - b.iter(|| { - let mut rope = Rope::new(); - for _ in 0..200 { - rope.insert_text_at_char_index("Hi", 0); - } - }); -} - - -#[bench] -fn insert_text_bench_2(b: &mut Bencher) { - b.iter(|| { - let mut rope = Rope::new(); - for i in 0..200 { - rope.insert_text_at_char_index("Hi", i/2); - } - }); -} - - -#[bench] -fn insert_text_bench_3(b: &mut Bencher) { - b.iter(|| { - let mut rope = Rope::new(); - for i in 0..200 { - rope.insert_text_at_char_index("Hi", i); - } - }); -} - - -#[bench] -fn insert_large_text_bench_1(b: &mut Bencher) { - let s = String::from_utf8(vec!['c' as u8; 3457]).unwrap(); - b.iter(|| { - let mut rope = Rope::from_str("Hello there!"); - rope.insert_text_at_char_index(&s[..], 0); - }); -} - - -#[bench] -fn insert_large_text_bench_2(b: &mut Bencher) { - let s = String::from_utf8(vec!['c' as u8; 3457]).unwrap(); - b.iter(|| { - let mut rope = Rope::from_str("Hello there!"); - rope.insert_text_at_char_index(&s[..], 3); - }); -} - - -#[bench] -fn insert_large_text_bench_3(b: &mut Bencher) { - let s = String::from_utf8(vec!['c' as u8; 3457]).unwrap(); - b.iter(|| { - let mut rope = Rope::from_str("Hello there!"); - rope.insert_text_at_char_index(&s[..], 12); - }); -} - - -#[bench] -fn remove_text_bench_1(b: &mut Bencher) { - b.iter(|| { - let mut rope = Rope::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - for _ in 0..200 { - rope.remove_text_between_char_indices(0, 2); - } - }); -} - - -#[bench] -fn remove_text_bench_2(b: &mut Bencher) { - b.iter(|| { - let mut rope = Rope::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - for i in 0..200 { - rope.remove_text_between_char_indices((200-i)-1, (200-i)+1); - } - }); -} - - -#[bench] -fn remove_text_bench_3(b: &mut Bencher) { - b.iter(|| { - let mut rope = Rope::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - for i in 0..200 { - rope.remove_text_between_char_indices(400-(i*2)-2, 400-(i*2)); - } - }); -} - - -#[bench] -fn append_1(b: &mut Bencher) { - b.iter(|| { - let mut left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 3617]).unwrap())[..]); - let right = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 3617]).unwrap())[..]); - left.append(right); - }); -} - - -#[bench] -fn append_2(b: &mut Bencher) { - b.iter(|| { - let mut left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 263]).unwrap())[..]); - let right = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 3617]).unwrap())[..]); - left.append(right); - }); -} - - -#[bench] -fn append_3(b: &mut Bencher) { - b.iter(|| { - let mut left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 3617]).unwrap())[..]); - let right = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 263]).unwrap())[..]); - left.append(right); - }); -} - - -#[bench] -fn split_at_char_index_1(b: &mut Bencher) { - b.iter(|| { - let mut left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 7649]).unwrap())[..]); - let _ = left.split_at_char_index(3617); - }); -} - - -#[bench] -fn split_at_char_index_2(b: &mut Bencher) { - b.iter(|| { - let mut left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 7649]).unwrap())[..]); - let _ = left.split_at_char_index(263); - }); -} -*/ \ No newline at end of file diff --git a/sub_crates/ropey/src/lib.rs b/sub_crates/ropey/src/lib.rs deleted file mode 100644 index 734eed6..0000000 --- a/sub_crates/ropey/src/lib.rs +++ /dev/null @@ -1,1486 +0,0 @@ -#![allow(dead_code)] -//#![feature(test)] -//#![feature(unicode)] - -//extern crate test; -extern crate unicode_segmentation; - -mod string_utils; -mod tests; -mod benches; - -use std::cmp::{min, max}; -use std::mem; -use std::str::Chars; -use unicode_segmentation::{UnicodeSegmentation, Graphemes}; -use string_utils::{ - char_count, - char_grapheme_line_ending_count, - grapheme_count_is_less_than, - graphemes_are_mergeable, - char_pos_to_byte_pos, - char_pos_to_grapheme_pos, - grapheme_pos_to_char_pos, - insert_text_at_char_index, - remove_text_between_char_indices, - split_string_at_char_index, - split_string_at_grapheme_index, - is_line_ending, -}; - - -pub const MIN_NODE_SIZE: usize = 64; -pub const MAX_NODE_SIZE: usize = MIN_NODE_SIZE * 2; - - -/// A rope data structure for storing text in a format that is efficient -/// for insertion and removal even for extremely large strings. -#[derive(Debug)] -pub struct Rope { - data: RopeData, - char_count_: usize, - grapheme_count_: usize, - line_ending_count_: usize, - tree_height: u32, -} - - -#[derive(Debug)] -enum RopeData { - Leaf(String), - Branch(Box, Box), -} - - -impl Rope { - /// Creates a new empty rope - pub fn new() -> Rope { - Rope { - data: RopeData::Leaf(String::new()), - char_count_: 0, - grapheme_count_: 0, - line_ending_count_: 0, - tree_height: 1, - } - } - - - /// Creates a new rope from a string slice - pub fn from_str(s: &str) -> Rope { - let mut rope_stack: Vec = Vec::new(); - - let mut s1 = s; - loop { - // Get the next chunk of the string to add - let mut byte_i = 0; - let mut le_count = 0; - let mut c_count = 0; - let mut g_count = 0; - for (bi, g) in UnicodeSegmentation::grapheme_indices(s1, true) { - byte_i = bi + g.len(); - g_count += 1; - c_count += char_count(g); - if is_line_ending(g) { - le_count += 1; - } - if g_count >= MAX_NODE_SIZE { - break; - } - } - if g_count == 0 { - break; - } - let chunk = &s1[..byte_i]; - - // Add chunk - rope_stack.push(Rope { - data: RopeData::Leaf(chunk.to_string()), - char_count_: c_count, - grapheme_count_: g_count, - line_ending_count_: le_count, - tree_height: 1, - }); - - // Do merges - loop { - let rsl = rope_stack.len(); - if rsl > 1 && rope_stack[rsl-2].tree_height <= rope_stack[rsl-1].tree_height { - let right = Box::new(rope_stack.pop().unwrap()); - let left = Box::new(rope_stack.pop().unwrap()); - let h = max(left.tree_height, right.tree_height) + 1; - let lc = left.line_ending_count_ + right.line_ending_count_; - let gc = left.grapheme_count_ + right.grapheme_count_; - let cc = left.char_count_ + right.char_count_; - rope_stack.push(Rope { - data: RopeData::Branch(left, right), - char_count_: cc, - grapheme_count_: gc, - line_ending_count_: lc, - tree_height: h, - }); - } - else { - break; - } - } - - s1 = &s1[byte_i..]; - } - - - // Handle possible final unmerged case - let rope = if rope_stack.len() == 0 { - Rope::new() - } - else { - while rope_stack.len() > 1 { - let right = rope_stack.pop().unwrap(); - let mut left = rope_stack.pop().unwrap(); - left.append_right(right); - rope_stack.push(left); - } - rope_stack.pop().unwrap() - }; - - return rope; - } - - /// Creates a new rope from a string, consuming the string - pub fn from_string(s: String) -> Rope { - // TODO: special case short strings? - Rope::from_str(&s[..]) - } - - pub fn char_count(&self) -> usize { - return self.char_count_; - } - - pub fn grapheme_count(&self) -> usize { - return self.grapheme_count_; - } - - pub fn line_ending_count(&self) -> usize { - return self.line_ending_count_; - } - - - /// Returns the number of graphemes between char indices pos_a and pos_b. - /// This is not as simple as a subtraction of char_index_to_grapheme_index() - /// calls, because the char indices may split graphemes. - /// Runs in O(log N) time. - pub fn grapheme_count_in_char_range(&self, pos_a: usize, pos_b: usize) -> usize { - assert!(pos_a <= pos_b, "Rope::grapheme_count_in_char_range(): pos_a must be less than or equal to pos_b."); - assert!(pos_b <= self.char_count(), "Rope::grapheme_count_in_char_range(): attempted to get grapheme count beyond the end of the text."); - - let ga = self.char_index_to_grapheme_index(pos_a); - let gb = self.char_index_to_grapheme_index(pos_b); - let cb = self.grapheme_index_to_char_index(gb); - - if pos_b == cb { - return gb - ga; - } - else { - return 1 + gb - ga; - } - } - - - /// Returns the index of the grapheme that the given char index is a - /// part of. - pub fn char_index_to_grapheme_index(&self, pos: usize) -> usize { - assert!(pos <= self.char_count(), "Rope::char_index_to_grapheme_index(): attempted to index beyond the end of the text."); - - match self.data { - RopeData::Leaf(ref text) => { - return char_pos_to_grapheme_pos(text, pos); - }, - - RopeData::Branch(ref left, ref right) => { - if pos < left.char_count_ { - return left.char_index_to_grapheme_index(pos); - } - else { - return left.grapheme_count_ + right.char_index_to_grapheme_index(pos - left.char_count_); - } - }, - } - - unreachable!() - } - - - /// Returns the beginning char index of the given grapheme index. - pub fn grapheme_index_to_char_index(&self, pos: usize) -> usize { - assert!(pos <= self.grapheme_count(), "Rope::grapheme_index_to_char_index(): attempted to index beyond the end of the text."); - - match self.data { - RopeData::Leaf(ref text) => { - return grapheme_pos_to_char_pos(text, pos); - }, - - RopeData::Branch(ref left, ref right) => { - if pos < left.grapheme_count_ { - return left.grapheme_index_to_char_index(pos); - } - else { - return left.char_count_ + right.grapheme_index_to_char_index(pos - left.grapheme_count_); - } - }, - } - - unreachable!() - } - - - /// Returns the index of the line that the given char index is on. - pub fn char_index_to_line_index(&self, pos: usize) -> usize { - assert!(pos <= self.char_count(), "Rope::char_index_to_line_index(): attempted to index beyond the end of the text."); - - match self.data { - RopeData::Leaf(ref text) => { - let mut ci = 0; - let mut lei = 0; - for g in UnicodeSegmentation::graphemes(&text[..], true) { - if ci == pos { - break; - } - ci += char_count(g); - if ci > pos { - break; - } - if is_line_ending(g) { - lei += 1; - } - } - return lei; - }, - - RopeData::Branch(ref left, ref right) => { - if pos < left.char_count_ { - return left.char_index_to_line_index(pos); - } - else { - return right.char_index_to_line_index(pos - left.char_count_) + left.line_ending_count_; - } - }, - } - } - - - /// Returns the char index at the start of the given line index. - pub fn line_index_to_char_index(&self, li: usize) -> usize { - assert!(li <= self.line_ending_count(), "Rope::line_index_to_char_index(): attempted to index beyond the end of the text."); - - // Special case for the beginning of the rope - if li == 0 { - return 0; - } - - // General cases - match self.data { - RopeData::Leaf(ref text) => { - let mut ci = 0; - let mut lei = 0; - for g in UnicodeSegmentation::graphemes(&text[..], true) { - ci += char_count(g); - if is_line_ending(g) { - lei += 1; - } - if lei == li { - break; - } - } - return ci; - }, - - RopeData::Branch(ref left, ref right) => { - if li <= left.line_ending_count_ { - return left.line_index_to_char_index(li); - } - else { - return right.line_index_to_char_index(li - left.line_ending_count_) + left.char_count_; - } - }, - } - } - - - pub fn char_at_index(&self, index: usize) -> char { - assert!(index < self.char_count(), "Rope::char_at_index(): attempted to fetch char that is outside the bounds of the text."); - - match self.data { - RopeData::Leaf(ref text) => { - let mut i: usize = 0; - for c in text.chars() { - if i == index { - return c; - } - i += 1; - } - unreachable!(); - }, - - RopeData::Branch(ref left, ref right) => { - if index < left.char_count() { - return left.char_at_index(index); - } - else { - return right.char_at_index(index - left.char_count()); - } - }, - } - } - - - pub fn grapheme_at_index<'a>(&'a self, index: usize) -> &'a str { - assert!(index < self.grapheme_count(), "Rope::grapheme_at_index(): attempted to fetch grapheme that is outside the bounds of the text."); - - match self.data { - RopeData::Leaf(ref text) => { - let mut i: usize = 0; - for g in UnicodeSegmentation::graphemes(&text[..], true) { - if i == index { - return g; - } - i += 1; - } - unreachable!(); - }, - - RopeData::Branch(ref left, ref right) => { - if index < left.grapheme_count() { - return left.grapheme_at_index(index); - } - else { - return right.grapheme_at_index(index - left.grapheme_count()); - } - }, - } - } - - - /// Inserts the given text at the given char index. - /// For small lengths of 'text' runs in O(log N) time. - /// For large lengths of 'text', dunno. But it seems to perform - /// sub-linearly, at least. - pub fn insert_text_at_char_index(&mut self, text: &str, pos: usize) { - assert!(pos <= self.char_count(), "Rope::insert_text_at_char_index(): attempted to insert text at a position beyond the end of the text."); - - // Insert text - let cc = self.char_count_; - self.insert_text_at_char_index_without_seam_check(text, pos); - let cc2 = self.char_count_; - - // Repair possible grapheme seams - self.repair_grapheme_seam(pos); - self.repair_grapheme_seam(pos + cc2 - cc); - } - - - /// Removes the text between the given char indices. - /// For small distances between pos_a and pos_b runs in O(log N) time. - /// For large distances, dunno. If it becomes a performance bottleneck, - /// can special-case that to two splits and an append, which are all - /// sublinear. - pub fn remove_text_between_char_indices(&mut self, pos_a: usize, pos_b: usize) { - assert!(pos_a <= pos_b, "Rope::remove_text_between_char_indices(): pos_a must be less than or equal to pos_b."); - assert!(pos_b <= self.char_count(), "Rope::remove_text_between_char_indices(): attempted to remove text beyond the end of the text."); - - self.remove_text_between_char_indices_without_seam_check(pos_a, pos_b); - self.repair_grapheme_seam(pos_a); - } - - - /// Splits a rope into two pieces from the given char index. - /// The first piece remains in this rope, the second piece is returned - /// as a new rope. - /// I _think_ this runs in O(log N) time, but this needs more analysis to - /// be sure. It is at least sublinear. - pub fn split_at_char_index(&mut self, pos: usize) -> Rope { - assert!(pos <= self.char_count(), "Rope::split_at_char_index(): attempted to split text at a position beyond the end of the text."); - - let mut left = Rope::new(); - let mut right = Rope::new(); - - self.split_recursive(pos, &mut left, &mut right); - - mem::swap(self, &mut left); - return right; - } - - - /// Appends another rope to the end of this one, consuming the other rope. - /// Runs in O(log N) time. - pub fn append(&mut self, rope: Rope) { - let cc = self.char_count_; - self.append_without_seam_check(rope); - self.repair_grapheme_seam(cc); - } - - - /// Makes a copy of the rope as a string. - /// Runs in O(N) time. - pub fn to_string(&self) -> String { - let mut s = String::new(); - - for chunk in self.chunk_iter() { - s.push_str(chunk); - } - - return s; - } - - - /// Creates a chunk iterator for the rope - pub fn chunk_iter<'a>(&'a self) -> RopeChunkIter<'a> { - self.chunk_iter_at_char_index(0).1 - } - - - /// Creates a chunk iter starting at the chunk containing the given - /// char index. Returns the chunk iter and its starting char index. - pub fn chunk_iter_at_char_index<'a>(&'a self, index: usize) -> (usize, RopeChunkIter<'a>) { - assert!(index <= self.char_count(), "Rope::chunk_iter_at_char_index(): attempted to create an iterator starting beyond the end of the text."); - - let mut node_stack: Vec<&'a Rope> = Vec::new(); - let mut cur_node = self; - let mut char_i = index; - - // Find the right rope node, and populate the stack at the same time - loop { - match cur_node.data { - RopeData::Leaf(_) => { - node_stack.push(cur_node); - break; - }, - - RopeData::Branch(ref left, ref right) => { - if char_i < left.char_count_ { - node_stack.push(&(**right)); - cur_node = &(**left); - } - else { - cur_node = &(**right); - char_i -= left.char_count_; - } - } - } - } - - (index - char_i, RopeChunkIter {node_stack: node_stack}) - } - - - /// Creates an iterator at the first char of the rope - pub fn char_iter<'a>(&'a self) -> RopeCharIter<'a> { - self.char_iter_at_index(0) - } - - - /// Creates an iterator starting at the given char index - pub fn char_iter_at_index<'a>(&'a self, index: usize) -> RopeCharIter<'a> { - assert!(index <= self.char_count(), "Rope::char_iter_at_index(): attempted to create an iterator starting beyond the end of the text."); - - let (char_i, mut chunk_iter) = self.chunk_iter_at_char_index(index); - - // Create the char iter for the current node - let mut citer = if let Some(text) = chunk_iter.next() { - (&text[..]).chars() - } - else { - unreachable!() - }; - - // Get to the right spot in the iter - for _ in char_i..index { - citer.next(); - } - - // Create the rope grapheme iter - return RopeCharIter { - chunk_iter: chunk_iter, - cur_chunk: citer, - length: None, - }; - } - - - /// Creates an iterator that starts at pos_a and stops just before pos_b. - pub fn char_iter_between_indices<'a>(&'a self, pos_a: usize, pos_b: usize) -> RopeCharIter<'a> { - assert!(pos_a <= pos_b, "Rope::char_iter_between_indices(): pos_a must be less than or equal to pos_b."); - assert!(pos_b <= self.char_count(), "Rope::char_iter_between_indices(): attempted to create an iterator starting beyond the end of the text."); - - let mut iter = self.char_iter_at_index(pos_a); - iter.length = Some(pos_b - pos_a); - return iter; - } - - - /// Creates an iterator at the first grapheme of the rope - pub fn grapheme_iter<'a>(&'a self) -> RopeGraphemeIter<'a> { - self.grapheme_iter_at_index(0) - } - - - /// Creates an iterator at the given grapheme index - pub fn grapheme_iter_at_index<'a>(&'a self, index: usize) -> RopeGraphemeIter<'a> { - assert!(index <= self.grapheme_count(), "Rope::grapheme_iter_at_index(): attempted to create an iterator starting beyond the end of the text."); - - let cindex = self.grapheme_index_to_char_index(index); - return self.grapheme_iter_at_char_index(cindex); - } - - - /// Creates an iterator that starts a pos_a and stops just before pos_b. - pub fn grapheme_iter_between_indices<'a>(&'a self, pos_a: usize, pos_b: usize) -> RopeGraphemeIter<'a> { - assert!(pos_a <= pos_b, "Rope::grapheme_iter_between_indices(): pos_a must be less than or equal to pos_b."); - assert!(pos_b <= self.grapheme_count(), "Rope::grapheme_iter_between_indices(): attempted to create an iterator starting beyond the end of the text."); - - let mut iter = self.grapheme_iter_at_index(pos_a); - let cpos_a = self.grapheme_index_to_char_index(pos_a); - let cpos_b = self.grapheme_index_to_char_index(pos_b); - iter.length = Some(cpos_b - cpos_a); - return iter; - } - - - /// Creates an iterator over the lines in the rope. - pub fn line_iter<'a>(&'a self) -> RopeLineIter<'a> { - RopeLineIter { - rope: self, - li: 0, - } - } - - - /// Creates an iterator over the lines in the rope, starting at the given - /// line index. - pub fn line_iter_at_index<'a>(&'a self, index: usize) -> RopeLineIter<'a> { - assert!(index <= (self.line_ending_count()+1), "Rope::line_iter_at_index(): attempted to create an iterator starting beyond the end of the text."); - - RopeLineIter { - rope: self, - li: index, - } - } - - - // Creates a slice into the Rope, between char indices pos_a and pos_b. - pub fn slice<'a>(&'a self, pos_a: usize, pos_b: usize) -> RopeSlice<'a> { - assert!(pos_a <= pos_b, "Rope::slice(): pos_a must be less than or equal to pos_b."); - assert!(pos_b <= self.char_count(), "Rope::slice(): attempted to create a slice extending beyond the end of the text."); - - let a = pos_a; - let b = min(self.char_count_, pos_b); - - RopeSlice { - rope: self, - start: a, - end: b, - } - } - - - // Creates a graphviz document of the Rope's structure, and returns - // it as a string. For debugging purposes. - pub fn to_graphviz(&self) -> String { - let mut text = "digraph {\n".to_string(); - self.to_graphviz_recursive(&mut text, "s".to_string()); - text.push_str("}\n"); - return text; - } - - - //================================================================ - // Private utility functions - //================================================================ - - - fn to_graphviz_recursive(&self, text: &mut String, name: String) { - match self.data { - RopeData::Leaf(_) => { - text.push_str(&(format!("{} [label=\"cc={}\\ngc={}\\nlec={}\"];\n", name, self.char_count_, self.grapheme_count_, self.line_ending_count_))[..]); - }, - - RopeData::Branch(ref left, ref right) => { - let mut lname = name.clone(); - let mut rname = name.clone(); - lname.push('l'); - rname.push('r'); - text.push_str(&(format!("{} [shape=box, label=\"h={}\\ncc={}\\ngc={}\\nlec={}\"];\n", name, self.tree_height, self.char_count_, self.grapheme_count_, self.line_ending_count_))[..]); - text.push_str(&(format!("{} -> {{ {} {} }};\n", name, lname, rname))[..]); - left.to_graphviz_recursive(text, lname); - right.to_graphviz_recursive(text, rname); - } - } - } - - - fn is_leaf(&self) -> bool { - if let RopeData::Leaf(_) = self.data { - true - } - else { - false - } - } - - - /// Non-recursively updates the stats of a node - fn update_stats(&mut self) { - match self.data { - RopeData::Leaf(ref text) => { - let (cc, gc, lec) = char_grapheme_line_ending_count(text); - self.char_count_ = cc; - self.grapheme_count_ = gc; - self.line_ending_count_ = lec; - self.tree_height = 1; - }, - - RopeData::Branch(ref left, ref right) => { - self.char_count_ = left.char_count_ + right.char_count_; - self.grapheme_count_ = left.grapheme_count_ + right.grapheme_count_; - self.line_ending_count_ = left.line_ending_count_ + right.line_ending_count_; - self.tree_height = max(left.tree_height, right.tree_height) + 1; - } - } - } - - - fn split_recursive(&mut self, pos: usize, left: &mut Rope, right: &mut Rope) { - match self.data { - RopeData::Leaf(ref text) => { - // Split the text into two new nodes - let mut l_text = text.clone(); - let r_text = split_string_at_char_index(&mut l_text, pos); - let new_rope_l = Rope::from_string(l_text); - let mut new_rope_r = Rope::from_string(r_text); - - // Append the nodes to their respective sides - left.append_without_seam_check(new_rope_l); - mem::swap(right, &mut new_rope_r); - right.append_without_seam_check(new_rope_r); - }, - - RopeData::Branch(ref mut left_b, ref mut right_b) => { - let mut l = Rope::new(); - let mut r = Rope::new(); - mem::swap(&mut **left_b, &mut l); - mem::swap(&mut **right_b, &mut r); - - // Split is on left side - if pos < l.char_count_ { - // Append the right split to the right side - mem::swap(right, &mut r); - right.append_without_seam_check(r); - - // Recurse - if let RopeData::Branch(_, ref mut new_left) = left.data { - if let RopeData::Branch(ref mut new_right, _) = right.data { - l.split_recursive(pos, new_left, new_right); - } - else { - l.split_recursive(pos, new_left, right); - } - } - else { - if let RopeData::Branch(ref mut new_right, _) = right.data { - l.split_recursive(pos, left, new_right); - } - else { - l.split_recursive(pos, left, right); - } - } - } - // Split is on right side - else { - // Append the left split to the left side - let new_pos = pos - l.char_count_; - left.append_without_seam_check(l); - - // Recurse - if let RopeData::Branch(_, ref mut new_left) = left.data { - if let RopeData::Branch(ref mut new_right, _) = right.data { - r.split_recursive(new_pos, new_left, new_right); - } - else { - r.split_recursive(new_pos, new_left, right); - } - } - else { - if let RopeData::Branch(ref mut new_right, _) = right.data { - r.split_recursive(new_pos, left, new_right); - } - else { - r.split_recursive(new_pos, left, right); - } - } - } - }, - - } - - left.rebalance(); - right.rebalance(); - } - - - fn append_without_seam_check(&mut self, rope: Rope) { - if self.grapheme_count_ == 0 { - let mut r = rope; - mem::swap(self, &mut r); - } - else if rope.grapheme_count_ == 0 { - return; - } - else if self.tree_height > rope.tree_height { - self.append_right(rope); - } - else { - let mut rope = rope; - mem::swap(self, &mut rope); - self.append_left(rope); - } - } - - - fn append_right(&mut self, rope: Rope) { - if self.tree_height <= rope.tree_height || self.is_leaf() { - let mut temp_rope = Box::new(Rope::new()); - mem::swap(self, &mut (*temp_rope)); - self.data = RopeData::Branch(temp_rope, Box::new(rope)); - } - else if let RopeData::Branch(_, ref mut right) = self.data { - right.append_right(rope); - } - - self.update_stats(); - self.rebalance(); - } - - - fn append_left(&mut self, rope: Rope) { - if self.tree_height <= rope.tree_height || self.is_leaf() { - let mut temp_rope = Box::new(Rope::new()); - mem::swap(self, &mut (*temp_rope)); - self.data = RopeData::Branch(Box::new(rope), temp_rope); - } - else if let RopeData::Branch(ref mut left, _) = self.data { - left.append_left(rope); - } - - self.update_stats(); - self.rebalance(); - } - - - /// Inserts the given text at the given char index. - /// This is done without a seam check because it is recursive and - /// would otherwise do a seam check at every recursive function call. - /// Rope::insert_text_at_char_index() calls this, and then does the seam - /// checks afterwards. - fn insert_text_at_char_index_without_seam_check(&mut self, text: &str, pos: usize) { - let mut leaf_insert = false; - - match self.data { - // Find node for text to be inserted into - RopeData::Branch(ref mut left, ref mut right) => { - if pos < left.char_count_ { - left.insert_text_at_char_index(text, pos); - } - else { - right.insert_text_at_char_index(text, pos - left.char_count_); - } - }, - - // Insert the text - RopeData::Leaf(ref mut s_text) => { - if grapheme_count_is_less_than(text, MAX_NODE_SIZE - self.grapheme_count_) { - // Simple case - insert_text_at_char_index(s_text, text, pos); - } - else { - // Special cases - leaf_insert = true; - } - }, - } - - // The special cases of inserting at a leaf node. - // These have to be done outside of the match statement because - // of the borrow checker, but logically they take place in the - // RopeData::Leaf branch of the match statement above. - if leaf_insert { - // TODO: these special cases are currently prone to causing leaf - // fragmentation. Find ways to reduce that. - if pos == 0 { - let mut new_rope = Rope::new(); - mem::swap(self, &mut new_rope); - self.data = RopeData::Branch(Box::new(Rope::from_str(text)), Box::new(new_rope)); - } - else if pos == self.char_count_ { - let mut new_rope = Rope::new(); - mem::swap(self, &mut new_rope); - self.data = RopeData::Branch(Box::new(new_rope), Box::new(Rope::from_str(text))); - } - else { - // Split the leaf node at the insertion point - let mut node_l = Rope::new(); - let node_r = self.split_at_char_index(pos); - mem::swap(self, &mut node_l); - - // Set the inserted text as the main node - *self = Rope::from_str(text); - - // Append the left and right split nodes to either side of - // the main node. - self.append_left(node_l); - self.append_right(node_r); - } - } - - self.update_stats(); - self.rebalance(); - } - - - /// Removes the text between the given char indices. - /// This is done without a seam check so that it can be used inside - /// repair_grapheme_seam() without risk of unintended recursion. - fn remove_text_between_char_indices_without_seam_check(&mut self, pos_a: usize, pos_b: usize) { - // Bounds checks - if pos_a > pos_b { - panic!("Rope::remove_text_between_char_indices(): pos_a must be less than or equal to pos_b."); - } - if pos_b > self.char_count_ { - panic!("Rope::remove_text_between_char_indices(): attempt to remove text after end of node text."); - } - - match self.data { - RopeData::Leaf(ref mut text) => { - remove_text_between_char_indices(text, pos_a, pos_b); - }, - - RopeData::Branch(ref mut left, ref mut right) => { - let lcc = left.char_count_; - - if pos_a < lcc { - left.remove_text_between_char_indices(pos_a, min(pos_b, lcc)); - } - - if pos_b > lcc { - right.remove_text_between_char_indices(pos_a - min(pos_a, lcc), pos_b - lcc); - } - } - } - - self.update_stats(); - self.merge_if_too_small(); - self.rebalance(); - } - - - /// Splits a leaf node into pieces if it's too large - // TODO: find a way to do this that's more algorithmically efficient - // if lots of splits need to happen. This version ends up re-scanning - // the text quite a lot, as well as doing quite a few unnecessary - // allocations. - fn split_if_too_large(&mut self) { - if self.grapheme_count_ > MAX_NODE_SIZE && self.is_leaf() { - - // Calculate split position and how large the left and right - // sides are going to be - let split_pos = self.grapheme_count_ / 2; - let new_gc_l = split_pos; - let new_gc_r = self.grapheme_count_ - split_pos; - - // Do the split - let mut nl = Box::new(Rope::new()); - let mut nr = Box::new(Rope::new()); - mem::swap(self, &mut (*nl)); - if let RopeData::Leaf(ref mut text) = nl.data { - nr.data = RopeData::Leaf(split_string_at_grapheme_index(text, split_pos)); - text.shrink_to_fit(); - } - - // Recursively split - nl.grapheme_count_ = new_gc_l; - nr.grapheme_count_ = new_gc_r; - nl.split_if_too_large(); - nr.split_if_too_large(); - - // Update the new left and right node's stats - nl.update_stats(); - nr.update_stats(); - - // Create the new branch node with the new left and right nodes - self.data = RopeData::Branch(nl, nr); - self.update_stats(); - } - } - - - /// Merges a non-leaf node into a leaf node if it's too small - fn merge_if_too_small(&mut self) { - if self.grapheme_count_ < MIN_NODE_SIZE && !self.is_leaf() { - let mut merged_text = String::new(); - - if let RopeData::Branch(ref mut left, ref mut right) = self.data { - // First, recursively merge the children - left.merge_if_too_small(); - right.merge_if_too_small(); - - // Then put their text into merged_text - if let RopeData::Leaf(ref mut text) = left.data { - mem::swap(&mut merged_text, text); - } - if let RopeData::Leaf(ref mut text) = right.data { - merged_text.push_str(&text[..]); - } - } - - // Make this a leaf node with merged_text as its data - self.data = RopeData::Leaf(merged_text); - self.tree_height = 1; - // Don't need to update grapheme count, because it should be the - // same as before. - } - } - - - /// Rotates the tree under the node left - fn rotate_left(&mut self) { - let mut temp = Rope::new(); - - if let RopeData::Branch(_, ref mut right) = self.data { - mem::swap(&mut temp, &mut (**right)); - - if let RopeData::Branch(ref mut left, _) = temp.data { - mem::swap(&mut (**left), &mut (**right)); - } - else { - panic!("Rope::rotate_left(): attempting to rotate node without branching right child."); - } - } - else { - panic!("Rope::rotate_left(): attempting to rotate leaf node."); - } - - if let RopeData::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 = Rope::new(); - - if let RopeData::Branch(ref mut left, _) = self.data { - mem::swap(&mut temp, &mut (**left)); - - if let RopeData::Branch(_, ref mut right) = temp.data { - mem::swap(&mut (**right), &mut (**left)); - } - else { - panic!("Rope::rotate_right(): attempting to rotate node without branching left child."); - } - } - else { - panic!("Rope::rotate_right(): attempting to rotate leaf node."); - } - - if let RopeData::Branch(_, ref mut right) = temp.data { - mem::swap(&mut (**right), self); - right.update_stats(); - } - - mem::swap(&mut temp, self); - self.update_stats(); - } - - - /// Balances the tree under this node. Assumes that both the left and - /// right sub-trees are themselves aleady balanced. - /// Runs in time linear to the difference in height between the two - /// sub-trees. Thus worst-case is O(log N) time, and best-case is O(1) - /// time. - fn rebalance(&mut self) { - let mut rot: isize = 0; - - if let RopeData::Branch(ref mut left, ref mut right) = self.data { - let height_diff = (left.tree_height as isize) - (right.tree_height as isize); - - // Left side higher than right side - if height_diff > 1 { - let mut child_rot = false; - if let RopeData::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 RopeData::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; - } - } - - if rot == 1 { - self.rotate_right(); - if let RopeData::Branch(_, ref mut right) = self.data { - right.rebalance(); - } - } - else if rot == -1 { - self.rotate_left(); - if let RopeData::Branch(ref mut left, _) = self.data { - left.rebalance(); - } - } - - self.update_stats(); - } - - - /// Creates a grapheme iterator startin at the given char index. - /// If the given char index starts in the middle of a grapheme, - /// the grapheme is split and the part of the grapheme after the - /// the char index is returned as the first grapheme. - fn grapheme_iter_at_char_index<'a>(&'a self, index: usize) -> RopeGraphemeIter<'a> { - let (char_i, mut chunk_iter) = self.chunk_iter_at_char_index(index); - - // Get the chunk string - if let Some(text) = chunk_iter.next() { - // Create the grapheme iter for the current node - let byte_i = char_pos_to_byte_pos(text, index - char_i); - let giter = UnicodeSegmentation::graphemes(&text[byte_i..], true); - - // Create the rope grapheme iter - return RopeGraphemeIter { - chunk_iter: chunk_iter, - cur_chunk: giter, - length: None, - }; - } - else { - // No chunks, which means no text - return RopeGraphemeIter { - chunk_iter: chunk_iter, - cur_chunk: UnicodeSegmentation::graphemes("", true), - length: None, - }; - }; - } - - - /// Returns whether the given char index lies on a leaf node boundary. - fn is_leaf_boundary(&self, index: usize) -> bool { - if index == 0 || index == self.char_count_ { - return true; - } - else { - match self.data { - RopeData::Leaf(_) => { - return false; - }, - - RopeData::Branch(ref left, ref right) => { - if index < left.char_count_ { - return left.is_leaf_boundary(index); - } - else { - return right.is_leaf_boundary(index - left.char_count_); - } - } - } - } - } - - - fn append_to_leaf(&mut self, text: &str, index: usize) { - match self.data { - RopeData::Leaf(ref mut l_text) => { - l_text.push_str(text); - }, - - RopeData::Branch(ref mut left, ref mut right) => { - if index <= left.char_count_ { - left.append_to_leaf(text, index); - } - else { - right.append_to_leaf(text, index - left.char_count_); - } - } - } - - self.update_stats(); - } - - - /// Repairs an erroneous grapheme separation that can occur at - /// leaf node boundaries. The index given is the char index of the - /// possible seam. - fn repair_grapheme_seam(&mut self, index: usize) { - if index == 0 || index == self.char_count_ { - return; - } - - let gi = self.char_index_to_grapheme_index(index); - - if self.is_leaf_boundary(index) && graphemes_are_mergeable(self.grapheme_at_index(gi-1), self.grapheme_at_index(gi)) { - let c1 = self.grapheme_index_to_char_index(gi); - let c2 = self.grapheme_index_to_char_index(gi + 1); - - // Get the grapheme on the right - let mut s = String::new(); - s.push_str(self.grapheme_at_index(gi)); - - // Append it to the left - self.append_to_leaf(&s[..], index); - - // Remove the duplicate - self.remove_text_between_char_indices_without_seam_check(c2, c2 + (c2 - c1)); - } - } - - - /// Tests if the rope adheres to the AVL balancing invariants. - fn is_balanced(&self) -> bool { - match self.data { - RopeData::Leaf(_) => { - return true; - }, - - RopeData::Branch(ref left, ref right) => { - let mut diff = left.tree_height as isize - right.tree_height as isize; - diff = if diff < 0 {-diff} else {diff}; - return (diff < 2) && left.is_balanced() && right.is_balanced(); - } - } - } -} - - - - -//============================================================= -// Rope iterators -//============================================================= - -/// An iterator over a rope's string chunks -pub struct RopeChunkIter<'a> { - node_stack: Vec<&'a Rope>, -} - -impl<'a> Iterator for RopeChunkIter<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option<&'a str> { - if let Some(next_chunk) = self.node_stack.pop() { - loop { - if let Option::Some(node) = self.node_stack.pop() { - match node.data { - RopeData::Leaf(_) => { - self.node_stack.push(node); - break; - }, - - RopeData::Branch(ref left, ref right) => { - self.node_stack.push(&(**right)); - self.node_stack.push(&(**left)); - continue; - } - } - } - else { - break; - } - } - - if let RopeData::Leaf(ref text) = next_chunk.data { - return Some(&text[..]); - } - else { - unreachable!(); - } - } - else { - return None; - } - } -} - - -// An iterator over a rope's chars -pub struct RopeCharIter<'a> { - chunk_iter: RopeChunkIter<'a>, - cur_chunk: Chars<'a>, - length: Option, -} - - -impl<'a> Iterator for RopeCharIter<'a> { - type Item = char; - - fn next(&mut self) -> Option { - if let Some(ref mut l) = self.length { - if *l == 0 { - return None; - } - } - - loop { - if let Some(c) = self.cur_chunk.next() { - if let Some(ref mut l) = self.length { - *l -= 1; - } - return Some(c); - } - else { - if let Some(s) = self.chunk_iter.next() { - self.cur_chunk = s.chars(); - continue; - } - else { - return None; - } - } - } - } -} - - -/// An iterator over a rope's graphemes -pub struct RopeGraphemeIter<'a> { - chunk_iter: RopeChunkIter<'a>, - cur_chunk: Graphemes<'a>, - length: Option, // Length in chars, not graphemes -} - - -impl<'a> Iterator for RopeGraphemeIter<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option<&'a str> { - if let Some(ref mut l) = self.length { - if *l == 0 { - return None; - } - } - - loop { - if let Some(g) = self.cur_chunk.next() { - if let Some(ref mut l) = self.length { - let cc = char_count(g); - if *l >= cc { - *l -= char_count(g); - return Some(g); - } - else { - let bc = char_pos_to_byte_pos(g, *l); - *l = 0; - return Some(&g[..bc]); - } - } - else { - return Some(g); - } - } - else { - if let Some(s) = self.chunk_iter.next() { - self.cur_chunk = UnicodeSegmentation::graphemes(s, true); - continue; - } - else { - return None; - } - } - } - } -} - - - -/// An iterator over a rope's lines, returned as RopeSlice's -pub struct RopeLineIter<'a> { - rope: &'a Rope, - li: usize, -} - - -impl<'a> Iterator for RopeLineIter<'a> { - type Item = RopeSlice<'a>; - - fn next(&mut self) -> Option> { - if self.li > self.rope.line_ending_count() { - return None; - } - else { - let a = self.rope.line_index_to_char_index(self.li); - let b = if self.li < self.rope.line_ending_count() { - self.rope.line_index_to_char_index(self.li+1) - } - else { - self.rope.char_count() - }; - - self.li += 1; - - return Some(self.rope.slice(a, b)); - } - } -} - - - - -//============================================================= -// Rope slice -//============================================================= - -/// An immutable slice into a Rope -pub struct RopeSlice<'a> { - rope: &'a Rope, - start: usize, - end: usize, -} - - -impl<'a> RopeSlice<'a> { - pub fn char_count(&self) -> usize { - self.end - self.start - } - - - pub fn grapheme_count(&self) -> usize { - self.rope.grapheme_count_in_char_range(self.start, self.end) - } - - - pub fn char_iter(&self) -> RopeCharIter<'a> { - self.rope.char_iter_between_indices(self.start, self.end) - } - - pub fn char_iter_at_index(&self, pos: usize) -> RopeCharIter<'a> { - assert!(pos <= self.char_count(), "RopeSlice::char_iter_at_index(): attempted to create iter starting beyond the end of the slice."); - - let a = self.start + pos; - - self.rope.char_iter_between_indices(a, self.end) - } - - pub fn char_iter_between_indices(&self, pos_a: usize, pos_b: usize) -> RopeCharIter<'a> { - assert!(pos_a <= pos_b, "RopeSlice::char_iter_between_indices(): pos_a must be less than or equal to pos_b."); - assert!(pos_b <= self.char_count(), "RopeSlice::char_iter_between_indices(): attempted to create iter extending beyond the end of the slice."); - - let a = self.start + pos_a; - let b = self.start + pos_b; - - self.rope.char_iter_between_indices(a, b) - } - - - pub fn grapheme_iter(&self) -> RopeGraphemeIter<'a> { - self.grapheme_iter_at_index(0) - } - - pub fn grapheme_iter_at_index(&self, pos: usize) -> RopeGraphemeIter<'a> { - assert!(pos <= self.grapheme_count(), "RopeSlice::grapheme_iter_at_index(): attempted to create iter starting beyond the end of the slice."); - - let gs = self.rope.char_index_to_grapheme_index(self.start); - let ca = self.rope.grapheme_index_to_char_index(gs + pos); - - let a = min(self.end, max(self.start, ca)); - - let mut giter = self.rope.grapheme_iter_at_char_index(a); - giter.length = Some(self.end - a); - - return giter; - } - - pub fn grapheme_iter_between_indices(&self, pos_a: usize, pos_b: usize) -> RopeGraphemeIter<'a> { - assert!(pos_a <= pos_b, "RopeSlice::grapheme_iter_between_indices(): pos_a must be less than or equal to pos_b."); - assert!(pos_b <= self.grapheme_count(), "RopeSlice::grapheme_iter_between_indices(): attempted to create iter extending beyond the end of the slice."); - - let gs = self.rope.char_index_to_grapheme_index(self.start); - let ca = self.rope.grapheme_index_to_char_index(gs + pos_a); - let cb = self.rope.grapheme_index_to_char_index(gs + pos_b); - - let mut giter = self.rope.grapheme_iter_at_char_index(ca); - giter.length = Some(cb - ca); - - return giter; - } - - - pub fn char_at_index(&self, index: usize) -> char { - assert!(index < self.char_count(), "RopeSlice::char_at_index(): attempted to index beyond the end of the slice."); - - self.rope.char_at_index(self.start+index) - } - - pub fn grapheme_at_index(&self, index: usize) -> &'a str { - assert!(index < self.grapheme_count(), "RopeSlice::grapheme_at_index(): attempted to index beyond the end of the slice."); - - let gs = self.rope.char_index_to_grapheme_index(self.start); - let gi = gs + index; - let cs = self.rope.grapheme_index_to_char_index(gi); - let ce = self.rope.grapheme_index_to_char_index(gi+1); - - let g = self.rope.grapheme_at_index(gi); - - if cs >= self.start && ce <= self.end { - // Easy case - return g; - } - else { - // Hard case: partial graphemes - let shave_a = if cs < self.start { self.start - cs} else { 0 }; - let shave_b = if ce > self.end { ce - self.end } else { 0 }; - - let cc = char_count(g); - - let a = char_pos_to_byte_pos(g, shave_a); - let b = char_pos_to_byte_pos(g, cc - shave_b); - - return &g[a..b]; - } - } - - - pub fn slice(&self, pos_a: usize, pos_b: usize) -> RopeSlice<'a> { - assert!(pos_a <= pos_b, "RopeSlice::slice(): pos_a must be less than or equal to pos_b."); - assert!(pos_b <= self.char_count(), "RopeSlice::slice(): attempted to create slice extending beyond the end of this slice."); - - let a = self.start + pos_a; - let b = self.start + pos_b; - - RopeSlice { - rope: self.rope, - start: a, - end: b, - } - } -} diff --git a/sub_crates/ropey/src/string_utils.rs b/sub_crates/ropey/src/string_utils.rs deleted file mode 100644 index e76acf9..0000000 --- a/sub_crates/ropey/src/string_utils.rs +++ /dev/null @@ -1,481 +0,0 @@ -#![allow(dead_code)] -//! Misc helpful utility functions for TextBuffer related stuff. - -use std::str::CharIndices; -use std::iter::repeat; -use unicode_segmentation::UnicodeSegmentation; - - -pub fn is_line_ending(text: &str) -> bool { - match text { - "\u{000D}\u{000A}" - | "\u{000A}" - | "\u{000B}" - | "\u{000C}" - | "\u{000D}" - | "\u{0085}" - | "\u{2028}" - | "\u{2029}" => true, - - _ => false - } -} - -pub fn line_ending_count(text: &str) -> usize { - let mut count = 0; - for g in UnicodeSegmentation::graphemes(text, true) { - if is_line_ending(g) { - count += 1; - } - } - return count; -} - -pub fn char_count(text: &str) -> usize { - let mut count = 0; - for _ in text.chars() { - count += 1; - } - return count; -} - -pub fn grapheme_count(text: &str) -> usize { - let mut count = 0; - for _ in UnicodeSegmentation::graphemes(text, true) { - count += 1; - } - return count; -} - -pub fn grapheme_count_is_less_than(text: &str, n: usize) -> bool { - let mut count = 0; - for _ in UnicodeSegmentation::graphemes(text, true) { - count += 1; - if count >= n { - return false; - } - } - - return true; -} - -pub fn char_grapheme_line_ending_count(text: &str) -> (usize, usize, usize) { - let mut cc = 0; - let mut gc = 0; - let mut lec = 0; - - for g in UnicodeSegmentation::graphemes(text, true) { - cc += char_count(g); - gc += 1; - if is_line_ending(g) { - lec += 1; - } - } - - return (cc, gc, lec); -} - -pub fn graphemes_are_mergeable(s1: &str, s2: &str) -> bool { - let mut s = String::with_capacity(s1.len() + s2.len()); - s.push_str(s1); - s.push_str(s2); - - return grapheme_count(&s[..]) == 1; -} - -pub fn char_pos_to_byte_pos(text: &str, pos: usize) -> usize { - let mut i: usize = 0; - - for (offset, _) in text.char_indices() { - if i == pos { - return offset; - } - i += 1; - } - - if i == pos { - return text.len(); - } - - panic!("char_pos_to_byte_pos(): char position off the end of the string."); -} - -pub fn grapheme_pos_to_byte_pos(text: &str, pos: usize) -> usize { - let mut i: usize = 0; - - for (offset, _) in UnicodeSegmentation::grapheme_indices(text, true) { - if i == pos { - return offset; - } - i += 1; - } - - if i == pos { - return text.len(); - } - - panic!("grapheme_pos_to_byte_pos(): grapheme position off the end of the string."); -} - -pub fn char_pos_to_grapheme_pos(text: &str, pos: usize) -> usize { - let mut i = 0usize; - let mut cc = 0usize; - - for g in UnicodeSegmentation::graphemes(text, true) { - if cc == pos { - return i; - } - - cc += char_count(g); - - if cc > pos { - return i; - } - - i += 1; - } - - if cc == pos { - return i; - } - - panic!("char_pos_to_grapheme_pos(): char position off the end of the string."); -} - -pub fn grapheme_pos_to_char_pos(text: &str, pos: usize) -> usize { - let mut i = 0usize; - let mut cc = 0usize; - - for g in UnicodeSegmentation::graphemes(text, true) { - if i == pos { - return cc; - } - - cc += char_count(g); - i += 1; - } - - if i == pos { - return cc; - } - - panic!("grapheme_pos_to_char_pos(): grapheme position off the end of the string."); -} - -/// Inserts the given text into the given string at the given grapheme index. -pub fn insert_text_at_char_index(s: &mut String, text: &str, pos: usize) { - // Find insertion position in bytes - let byte_pos = char_pos_to_byte_pos(&s[..], pos); - - // Get byte vec of string - let byte_vec = unsafe { s.as_mut_vec() }; - - // Grow data size - byte_vec.extend(repeat(0).take(text.len())); - - // Move old bytes forward - // TODO: use copy_memory()...? - let mut from = byte_vec.len() - text.len(); - let mut to = byte_vec.len(); - while from > byte_pos { - from -= 1; - to -= 1; - - byte_vec[to] = byte_vec[from]; - } - - // Copy new bytes in - // TODO: use copy_memory() - let mut i = byte_pos; - for b in text.bytes() { - byte_vec[i] = b; - i += 1 - } -} - -/// Inserts the given text into the given string at the given grapheme index. -pub fn insert_text_at_grapheme_index(s: &mut String, text: &str, pos: usize) { - // Find insertion position in bytes - let byte_pos = grapheme_pos_to_byte_pos(&s[..], pos); - - // Get byte vec of string - let byte_vec = unsafe { s.as_mut_vec() }; - - // Grow data size - byte_vec.extend(repeat(0).take(text.len())); - - // Move old bytes forward - // TODO: use copy_memory()...? - let mut from = byte_vec.len() - text.len(); - let mut to = byte_vec.len(); - while from > byte_pos { - from -= 1; - to -= 1; - - byte_vec[to] = byte_vec[from]; - } - - // Copy new bytes in - // TODO: use copy_memory() - let mut i = byte_pos; - for g in UnicodeSegmentation::graphemes(text, true) { - - for b in g.bytes() { - byte_vec[i] = b; - i += 1 - } - } -} - -/// Removes the text between the given char indices in the given string. -pub fn remove_text_between_char_indices(s: &mut String, pos_a: usize, pos_b: usize) { - // Bounds checks - assert!(pos_a <= pos_b, "remove_text_between_char_indices(): pos_a must be less than or equal to pos_b."); - - if pos_a == pos_b { - return; - } - - // Find removal positions in bytes - // TODO: get both of these in a single pass - let byte_pos_a = char_pos_to_byte_pos(&s[..], pos_a); - let byte_pos_b = char_pos_to_byte_pos(&s[..], pos_b); - - // Get byte vec of string - let byte_vec = unsafe { s.as_mut_vec() }; - - // 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 < byte_vec.len() { - byte_vec[to] = byte_vec[from]; - - from += 1; - to += 1; - } - - // Remove data from the end - let final_text_size = byte_vec.len() + byte_pos_a - byte_pos_b; - byte_vec.truncate(final_text_size); -} - -/// Removes the text between the given grapheme indices in the given string. -pub fn remove_text_between_grapheme_indices(s: &mut String, pos_a: usize, pos_b: usize) { - // Bounds checks - assert!(pos_a <= pos_b, "remove_text_between_grapheme_indices(): pos_a must be less than or equal to pos_b."); - - if pos_a == pos_b { - return; - } - - // Find removal positions in bytes - // TODO: get both of these in a single pass - let byte_pos_a = grapheme_pos_to_byte_pos(&s[..], pos_a); - let byte_pos_b = grapheme_pos_to_byte_pos(&s[..], pos_b); - - // Get byte vec of string - let byte_vec = unsafe { s.as_mut_vec() }; - - // 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 < byte_vec.len() { - byte_vec[to] = byte_vec[from]; - - from += 1; - to += 1; - } - - // Remove data from the end - let final_text_size = byte_vec.len() + byte_pos_a - byte_pos_b; - byte_vec.truncate(final_text_size); -} - -/// Splits a string into two strings at the char index given. -/// The first section of the split is stored in the original string, -/// while the second section of the split is returned as a new string. -pub fn split_string_at_char_index(s1: &mut String, pos: usize) -> String { - let mut s2 = String::new(); - - // Code block to contain the borrow of s2 - { - let byte_pos = char_pos_to_byte_pos(&s1[..], pos); - - let byte_vec_1 = unsafe { s1.as_mut_vec() }; - let byte_vec_2 = unsafe { s2.as_mut_vec() }; - - byte_vec_2.extend((&byte_vec_1[byte_pos..]).iter().map(|&i| i)); - byte_vec_1.truncate(byte_pos); - } - - return s2; -} - -/// Splits a string into two strings at the grapheme index given. -/// The first section of the split is stored in the original string, -/// while the second section of the split is returned as a new string. -pub fn split_string_at_grapheme_index(s1: &mut String, pos: usize) -> String { - let mut s2 = String::new(); - - // Code block to contain the borrow of s2 - { - let byte_pos = grapheme_pos_to_byte_pos(&s1[..], pos); - - let byte_vec_1 = unsafe { s1.as_mut_vec() }; - let byte_vec_2 = unsafe { s2.as_mut_vec() }; - - byte_vec_2.extend((&byte_vec_1[byte_pos..]).iter().map(|&i| i)); - byte_vec_1.truncate(byte_pos); - } - - return s2; -} - - -/// A grapheme iterator that only recognizes CRLF as a composite grapheme. -/// This is only temporary, a stand-in for the proper Graphemes iterator -/// from stdlib which is currently marked unstable and thus is unavailable -/// in stable Rust builds. When Graphemes makes its way back into stdlib -/// or is split into another library, replace this with it. -pub struct TempGraphemeIndices<'a> { - s: &'a str, - chars: CharIndices<'a>, - i1: usize, - i2: usize, -} - -impl<'a> TempGraphemeIndices<'a> { - fn push_i2(&mut self) { - if let Some((i, _)) = self.chars.next() { - self.i2 = i; - } - else { - self.i2 = self.s.len(); - } - } -} - -impl<'a> Iterator for TempGraphemeIndices<'a> { - type Item = (usize, &'a str); - - fn next(&mut self) -> Option<(usize, &'a str)> { - // Advance the iterator - if self.i1 == self.i2 { - self.push_i2(); - } - - // If we're at the end, we're done - if self.i1 == self.s.len() { - return None; - } - - - match &(self.s)[self.i1 .. self.i2] { - "\u{000D}" => { - let ii = self.i1; - let i3 = self.i2; - self.push_i2(); - if &(self.s)[self.i1 .. self.i2] == "\u{000D}\u{000A}" { - let s = &(self.s)[self.i1 .. self.i2]; - self.i1 = self.i2; - return Some((ii, s)); - } - else { - let ii = self.i1; - let s = &(self.s)[self.i1 .. i3]; - self.i1 = i3; - return Some((ii, s)); - } - }, - - _ => { - let ii = self.i1; - let s = &(self.s)[self.i1 .. self.i2]; - self.i1 = self.i2; - return Some((ii, s)); - }, - } - } -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn graphemes_are_mergeable_1() { - assert!(graphemes_are_mergeable("\u{000D}", "\u{000A}")); - assert!(!graphemes_are_mergeable("\u{000A}", "\u{000D}")); - assert!(!graphemes_are_mergeable("a", "b")); - } - - #[test] - fn char_pos_to_grapheme_pos_1() { - let s = "Hello\u{000D}\u{000A}there!"; - - assert_eq!(char_pos_to_grapheme_pos(s, 0), 0); - assert_eq!(char_pos_to_grapheme_pos(s, 5), 5); - assert_eq!(char_pos_to_grapheme_pos(s, 6), 5); - assert_eq!(char_pos_to_grapheme_pos(s, 7), 6); - assert_eq!(char_pos_to_grapheme_pos(s, 13), 12); - } - - #[test] - fn char_pos_to_grapheme_pos_2() { - let s = "a"; - - assert_eq!(char_pos_to_grapheme_pos(s, 0), 0); - assert_eq!(char_pos_to_grapheme_pos(s, 1), 1); - } - - #[test] - fn char_pos_to_grapheme_pos_3() { - let s = "\u{000D}\u{000A}"; - - assert_eq!(char_pos_to_grapheme_pos(s, 0), 0); - assert_eq!(char_pos_to_grapheme_pos(s, 1), 0); - assert_eq!(char_pos_to_grapheme_pos(s, 2), 1); - } - - #[test] - fn char_pos_to_grapheme_pos_4() { - let s = ""; - - assert_eq!(char_pos_to_grapheme_pos(s, 0), 0); - } - - #[test] - fn grapheme_pos_to_char_pos_1() { - let s = "Hello\u{000D}\u{000A}there!"; - - assert_eq!(grapheme_pos_to_char_pos(s, 0), 0); - assert_eq!(grapheme_pos_to_char_pos(s, 5), 5); - assert_eq!(grapheme_pos_to_char_pos(s, 6), 7); - assert_eq!(grapheme_pos_to_char_pos(s, 8), 9); - assert_eq!(grapheme_pos_to_char_pos(s, 12), 13); - } - - #[test] - fn grapheme_pos_to_char_pos_2() { - let s = "a"; - - assert_eq!(grapheme_pos_to_char_pos(s, 0), 0); - assert_eq!(grapheme_pos_to_char_pos(s, 1), 1); - } - - #[test] - fn grapheme_pos_to_char_pos_3() { - let s = "\u{000D}\u{000A}"; - - assert_eq!(grapheme_pos_to_char_pos(s, 0), 0); - assert_eq!(grapheme_pos_to_char_pos(s, 1), 2); - } - - #[test] - fn grapheme_pos_to_char_pos_4() { - let s = ""; - - assert_eq!(grapheme_pos_to_char_pos(s, 0), 0); - } -} \ No newline at end of file diff --git a/sub_crates/ropey/src/tests.rs b/sub_crates/ropey/src/tests.rs deleted file mode 100644 index 359bb25..0000000 --- a/sub_crates/ropey/src/tests.rs +++ /dev/null @@ -1,1772 +0,0 @@ -#![cfg(test)] -#![allow(unused_imports)] - -use std::iter; -use string_utils::{remove_text_between_char_indices}; -use super::{Rope, RopeData, RopeGraphemeIter, MAX_NODE_SIZE}; -//use std::old_path::Path; -//use std::old_io::fs::File; -//use std::old_io::BufferedWriter; - - -#[test] -fn new_1() { - let rope = Rope::new(); - let mut iter = rope.grapheme_iter(); - - assert_eq!(rope.char_count(), 0); - assert_eq!(rope.grapheme_count(), 0); - assert_eq!(rope.line_ending_count(), 0); - - assert_eq!(None, iter.next()); -} - - -#[test] -fn new_2() { - let rope = Rope::from_str("Hello world!"); - let mut iter = rope.grapheme_iter(); - - assert_eq!(rope.char_count(), 12); - assert_eq!(rope.grapheme_count(), 12); - assert_eq!(rope.line_ending_count(), 0); - - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn new_3() { - let s = "Hello world!".to_string(); - let rope = Rope::from_string(s); - let mut iter = rope.grapheme_iter(); - - assert_eq!(rope.char_count(), 12); - assert_eq!(rope.grapheme_count(), 12); - assert_eq!(rope.line_ending_count(), 0); - - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn new_4() { - let rope = Rope::from_str(&(String::from_utf8(vec!['c' as u8; 1 + MAX_NODE_SIZE * 53]).unwrap())[..]); - - assert_eq!(rope.char_count(), 1 + MAX_NODE_SIZE * 53); - assert_eq!(rope.grapheme_count(), 1 + MAX_NODE_SIZE * 53); - assert_eq!(rope.line_ending_count(), 0); - - assert!(rope.is_balanced()); -} - - -#[test] -fn counts() { - let rope = Rope::from_str("Hello\u{000D}\u{000A}world!"); - - assert_eq!(rope.char_count(), 13); - assert_eq!(rope.grapheme_count(), 12); - assert_eq!(rope.line_ending_count(), 1); - assert_eq!(rope.grapheme_count_in_char_range(0, 13), 12); -} - - -#[test] -fn grapheme_count_in_char_range() { - let rope = Rope::from_str("Hello\u{000D}\u{000A}world!"); - - assert_eq!(rope.grapheme_count_in_char_range(5, 13), 7); - assert_eq!(rope.grapheme_count_in_char_range(6, 13), 7); - assert_eq!(rope.grapheme_count_in_char_range(7, 13), 6); - - assert_eq!(rope.grapheme_count_in_char_range(0, 7), 6); - assert_eq!(rope.grapheme_count_in_char_range(0, 6), 6); - assert_eq!(rope.grapheme_count_in_char_range(0, 5), 5); - - assert_eq!(rope.grapheme_count_in_char_range(5, 7), 1); - assert_eq!(rope.grapheme_count_in_char_range(5, 6), 1); - assert_eq!(rope.grapheme_count_in_char_range(6, 7), 1); -} - - -#[test] -fn char_at_index() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - assert_eq!(rope.char_count(), 15); - assert_eq!(rope.grapheme_count(), 14); - assert_eq!(rope.line_ending_count(), 1); - - assert_eq!('H', rope.char_at_index(0)); - assert_eq!('界', rope.char_at_index(4)); - assert_eq!('\u{000D}', rope.char_at_index(7)); - assert_eq!('\u{000A}', rope.char_at_index(8)); - assert_eq!('w', rope.char_at_index(9)); - assert_eq!('!', rope.char_at_index(14)); -} - - -#[test] -fn grapheme_at_index() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - assert_eq!(rope.char_count(), 15); - assert_eq!(rope.grapheme_count(), 14); - assert_eq!(rope.line_ending_count(), 1); - - assert_eq!("H", rope.grapheme_at_index(0)); - assert_eq!("界", rope.grapheme_at_index(4)); - assert_eq!("\u{000D}\u{000A}", rope.grapheme_at_index(7)); - assert_eq!("w", rope.grapheme_at_index(8)); - assert_eq!("!", rope.grapheme_at_index(13)); -} - - -#[test] -fn char_iter_1() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - let mut iter = rope.char_iter(); - - assert!(Some('H') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('l') == iter.next()); - assert!(Some('世') == iter.next()); - assert!(Some('界') == iter.next()); - assert!(Some('l') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('\u{000D}') == iter.next()); - assert!(Some('\u{000A}') == 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 char_iter_2() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - let mut iter = rope.char_iter_at_index(8); - - assert!(Some('\u{000A}') == 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 char_iter_3() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - let mut iter = rope.char_iter_at_index(9); - - 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 char_iter_4() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - let mut iter = rope.char_iter_between_indices(9, 12); - - assert!(Some('w') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('r') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn grapheme_iter_1() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - let mut iter = rope.grapheme_iter(); - - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("世"), iter.next()); - assert_eq!(Some("界"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("\u{000D}\u{000A}"), iter.next()); - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn grapheme_iter_2() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - let mut iter = rope.grapheme_iter_at_index(7); - - assert_eq!(Some("\u{000D}\u{000A}"), iter.next()); - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn grapheme_iter_3() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - let mut iter = rope.grapheme_iter_at_index(8); - - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn grapheme_iter_4() { - let rope = Rope::from_str("Hel世界lo\u{000D}\u{000A}world!"); - - let mut iter = rope.grapheme_iter_between_indices(8, 11); - - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn slice_1() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); - let s = rope.slice(0, 15); - - let mut iter = s.char_iter(); - - assert_eq!(s.char_count(), 15); - assert_eq!(s.grapheme_count(), 15); - assert!(Some('H') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('l') == iter.next()); - assert!(Some('l') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('v') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('r') == iter.next()); - assert!(Some('y') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('n') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('!') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn slice_2() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); - let s = rope.slice(6, 20); - - let mut iter = s.char_iter(); - - assert_eq!(s.char_count(), 14); - assert_eq!(s.grapheme_count(), 14); - assert!(Some('e') == iter.next()); - assert!(Some('v') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('r') == iter.next()); - assert!(Some('y') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('n') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('!') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('H') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('w') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn slice_3() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); - let s = rope.slice(21, 39); - - let mut iter = s.char_iter(); - - assert_eq!(s.char_count(), 18); - assert_eq!(s.grapheme_count(), 18); - assert!(Some('a') == iter.next()); - assert!(Some('r') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('y') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('u') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('d') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('i') == iter.next()); - assert!(Some('n') == iter.next()); - assert!(Some('g') == iter.next()); - assert!(Some(',') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('h') == iter.next()); - assert!(Some('?') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn slice_4() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); - let s = rope.slice(21, 39); - - let mut iter = s.char_iter(); - - assert_eq!(s.char_count(), 18); - assert_eq!(s.grapheme_count(), 18); - assert!(Some('a') == iter.next()); - assert!(Some('r') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('y') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('u') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('d') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('i') == iter.next()); - assert!(Some('n') == iter.next()); - assert!(Some('g') == iter.next()); - assert!(Some(',') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('h') == iter.next()); - assert!(Some('?') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn slice_5() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); - let s = rope.slice(21, 39); - let s2 = s.slice(3, 10); - - let mut iter = s2.char_iter(); - - assert_eq!(s.char_count(), 18); - assert_eq!(s.grapheme_count(), 18); - assert!(Some(' ') == iter.next()); - assert!(Some('y') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('u') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('d') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn slice_6() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); - let s = rope.slice(15, 39); - - let mut iter = s.char_iter_between_indices(0, 24); - - assert!(Some(' ') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('H') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('w') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('a') == iter.next()); - assert!(Some('r') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('y') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('u') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('d') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('i') == iter.next()); - assert!(Some('n') == iter.next()); - assert!(Some('g') == iter.next()); - assert!(Some(',') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('h') == iter.next()); - assert!(Some('?') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn slice_7() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); - let s = rope.slice(15, 39); - - let mut iter = s.char_iter_between_indices(10, 20); - - assert!(Some('y') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('u') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('d') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('i') == iter.next()); - assert!(Some('n') == iter.next()); - assert!(Some('g') == iter.next()); - assert!(Some(',') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn slice_8() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); - let s = rope.slice(15, 39); - - let mut iter = s.char_iter_between_indices(0, 24); - - assert!(Some(' ') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('H') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('w') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('a') == iter.next()); - assert!(Some('r') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('y') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('u') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('d') == iter.next()); - assert!(Some('o') == iter.next()); - assert!(Some('i') == iter.next()); - assert!(Some('n') == iter.next()); - assert!(Some('g') == iter.next()); - assert!(Some(',') == iter.next()); - assert!(Some(' ') == iter.next()); - assert!(Some('e') == iter.next()); - assert!(Some('h') == iter.next()); - assert!(Some('?') == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn slice_9() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); - let s = rope.slice(0, 39); - - let mut iter = s.grapheme_iter(); - - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("v"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("y"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(Some("\u{000D}\u{000A}"), iter.next()); - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("y"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("u"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some(","), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("h"), iter.next()); - assert_eq!(Some("?"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn slice_10() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); - let s = rope.slice(0, 39); - - let mut iter = s.grapheme_iter_at_index(16); - - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("y"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("u"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some(","), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("h"), iter.next()); - assert_eq!(Some("?"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn slice_11() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); - let s = rope.slice(17, 39); - - let mut iter = s.grapheme_iter_at_index(0); - - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("y"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("u"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some(","), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("h"), iter.next()); - assert_eq!(Some("?"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn slice_12() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); - let s = rope.slice(5, 20); - - let mut iter = s.grapheme_iter_between_indices(6, 12); - - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(Some("\u{000D}\u{000A}"), iter.next()); - assert_eq!(Some("H"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn slice_13() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); - let s = rope.slice(16, 39); - - let mut iter = s.grapheme_iter_at_index(0); - - assert_eq!(Some("\u{000A}"), iter.next()); - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("w"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("y"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("u"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("d"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some(","), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("h"), iter.next()); - assert_eq!(Some("?"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn slice_14() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); - let s = rope.slice(0, 16); - - let mut iter = s.grapheme_iter_at_index(0); - - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("v"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("y"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(Some("\u{000D}"), iter.next()); - assert_eq!(None, iter.next()); -} - - -#[test] -fn slice_15() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); - let s = rope.slice(16, 39); - - assert_eq!("\u{000A}", s.grapheme_at_index(0)); -} - - -#[test] -fn slice_16() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); - let s = rope.slice(0, 16); - - assert_eq!("\u{000D}", s.grapheme_at_index(15)); -} - - -#[test] -fn char_index_to_grapheme_index_1() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); // 39 chars, 39 graphemes - - assert_eq!(rope.char_index_to_grapheme_index(0), 0); - assert_eq!(rope.char_index_to_grapheme_index(5), 5); - assert_eq!(rope.char_index_to_grapheme_index(39), 39); -} - - -#[test] -fn char_index_to_grapheme_index_2() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); // 39 chars, 38 graphemes - - assert_eq!(rope.char_index_to_grapheme_index(0), 0); - assert_eq!(rope.char_index_to_grapheme_index(15), 15); - assert_eq!(rope.char_index_to_grapheme_index(16), 15); - assert_eq!(rope.char_index_to_grapheme_index(17), 16); - assert_eq!(rope.char_index_to_grapheme_index(39), 38); -} - - -#[test] -fn grapheme_index_to_char_index_1() { - let rope = Rope::from_str("Hello everyone! How are you doing, eh?"); // 39 chars, 39 graphemes - - assert_eq!(rope.grapheme_index_to_char_index(0), 0); - assert_eq!(rope.grapheme_index_to_char_index(5), 5); - assert_eq!(rope.grapheme_index_to_char_index(39), 39); -} - - -#[test] -fn grapheme_index_to_char_index_2() { - let rope = Rope::from_str("Hello everyone!\u{000D}\u{000A}How are you doing, eh?"); // 39 chars, 38 graphemes - - assert_eq!(rope.grapheme_index_to_char_index(0), 0); - assert_eq!(rope.grapheme_index_to_char_index(15), 15); - assert_eq!(rope.grapheme_index_to_char_index(16), 17); - assert_eq!(rope.grapheme_index_to_char_index(38), 39); -} - - -#[test] -fn line_index_to_char_index_1() { - let rope = Rope::from_str("Hello\nworld!\n"); - - assert_eq!(rope.line_index_to_char_index(0), 0); - assert_eq!(rope.line_index_to_char_index(1), 6); - assert_eq!(rope.line_index_to_char_index(2), 13); -} - - -#[test] -fn line_index_to_grapheme_index_2() { - let rope = Rope::from_str("Hi\nthere\npeople\nof\nthe\nworld!"); - - assert_eq!(rope.line_index_to_char_index(0), 0); - assert_eq!(rope.line_index_to_char_index(1), 3); - assert_eq!(rope.line_index_to_char_index(2), 9); - assert_eq!(rope.line_index_to_char_index(3), 16); - assert_eq!(rope.line_index_to_char_index(4), 19); - assert_eq!(rope.line_index_to_char_index(5), 23); -} - - -#[test] -fn char_index_to_line_index_1() { - let rope = Rope::from_str("Hello\nworld!\n"); - - assert_eq!(rope.char_index_to_line_index(0), 0); - assert_eq!(rope.char_index_to_line_index(1), 0); - assert_eq!(rope.char_index_to_line_index(5), 0); - assert_eq!(rope.char_index_to_line_index(6), 1); - assert_eq!(rope.char_index_to_line_index(12), 1); - assert_eq!(rope.char_index_to_line_index(13), 2); -} - - -#[test] -fn char_index_to_line_index_2() { - let rope = Rope::from_str("Hi\nthere\npeople\nof\nthe\nworld!"); - - assert_eq!(rope.char_index_to_line_index(0), 0); - assert_eq!(rope.char_index_to_line_index(2), 0); - assert_eq!(rope.char_index_to_line_index(3), 1); - assert_eq!(rope.char_index_to_line_index(8), 1); - assert_eq!(rope.char_index_to_line_index(9), 2); - assert_eq!(rope.char_index_to_line_index(15), 2); - assert_eq!(rope.char_index_to_line_index(16), 3); - assert_eq!(rope.char_index_to_line_index(18), 3); - assert_eq!(rope.char_index_to_line_index(19), 4); - assert_eq!(rope.char_index_to_line_index(22), 4); - assert_eq!(rope.char_index_to_line_index(23), 5); - assert_eq!(rope.char_index_to_line_index(29), 5); -} - - -#[test] -fn to_string() { - let rope = Rope::from_str("Hello there good people of the world!"); - let s = rope.to_string(); - - assert_eq!("Hello there good people of the world!", &s[..]); -} - - -#[test] -fn split_at_char_index_1() { - let mut rope1 = Rope::from_str("Hello there good people of the world!"); - - //let mut f1 = BufferedWriter::new(File::create(&Path::new("yar1.gv")).unwrap()); - //f1.write_str(&(rope1.to_graphviz())[..]); - - let rope2 = rope1.split_at_char_index(18); - - //let mut f2 = BufferedWriter::new(File::create(&Path::new("yar2.gv")).unwrap()); - //f2.write_str(&(rope1.to_graphviz())[..]); - //f2.write_str(&(rope2.to_graphviz())[..]); - - assert!(rope1.is_balanced()); - assert!(rope2.is_balanced()); - assert_eq!("Hello there good p", &(rope1.to_string())[..]); - assert_eq!("eople of the world!", &(rope2.to_string())[..]); -} - - -#[test] -fn split_at_char_index_2() { - let mut rope1 = Rope::from_str("Hello there good people of the world!"); - - //let mut f1 = BufferedWriter::new(File::create(&Path::new("yar1.gv")).unwrap()); - //f1.write_str(&(rope1.to_graphviz())[..]); - - let rope2 = rope1.split_at_char_index(31); - - //let mut f2 = BufferedWriter::new(File::create(&Path::new("yar2.gv")).unwrap()); - //f2.write_str(&(rope1.to_graphviz())[..]); - //f2.write_str(&(rope2.to_graphviz())[..]); - - assert!(rope1.is_balanced()); - assert!(rope2.is_balanced()); - assert_eq!("Hello there good people of the ", &(rope1.to_string())[..]); - assert_eq!("world!", &(rope2.to_string())[..]); -} - - -#[test] -fn split_at_char_index_3() { - let mut rope1 = Rope::from_str("Hello there good people of the world!"); - - //let mut f1 = BufferedWriter::new(File::create(&Path::new("yar1.gv")).unwrap()); - //f1.write_str(&(rope1.to_graphviz())[..]); - - let rope2 = rope1.split_at_char_index(5); - - //let mut f2 = BufferedWriter::new(File::create(&Path::new("yar2.gv")).unwrap()); - //f2.write_str(&(rope1.to_graphviz())[..]); - //f2.write_str(&(rope2.to_graphviz())[..]); - - assert!(rope1.is_balanced()); - assert!(rope2.is_balanced()); - assert_eq!("Hello", &(rope1.to_string())[..]); - assert_eq!(" there good people of the world!", &(rope2.to_string())[..]); -} - - -#[test] -fn split_at_char_index_4() { - let mut rope1 = Rope::from_str("Hello there good people of the world!"); - let rope2 = rope1.split_at_char_index(37); - - assert!(rope1.is_balanced()); - assert!(rope2.is_balanced()); - assert_eq!("Hello there good people of the world!", &(rope1.to_string())[..]); - assert_eq!("", &(rope2.to_string())[..]); -} - - -#[test] -fn split_at_char_index_5() { - let mut rope1 = Rope::from_str("Hello there good people of the world!"); - let rope2 = rope1.split_at_char_index(0); - - assert!(rope1.is_balanced()); - assert!(rope2.is_balanced()); - assert_eq!("", &(rope1.to_string())[..]); - assert_eq!("Hello there good people of the world!", &(rope2.to_string())[..]); -} - - -#[test] -fn split_at_char_index_6() { - let mut rope1 = Rope::from_str("Hello there good\u{000D}\u{000A}people of the world!"); - let rope2 = rope1.split_at_char_index(17); - - assert!(rope1.is_balanced()); - assert!(rope2.is_balanced()); - assert_eq!("Hello there good\u{000D}", &(rope1.to_string())[..]); - assert_eq!("\u{000A}people of the world!", &(rope2.to_string())[..]); -} - - -#[test] -fn append_1() { - let mut rope1 = Rope::from_str("Hello there good p"); - let rope2 = Rope::from_str("eople of the world!"); - - rope1.append(rope2); - - assert!(rope1.is_balanced()); - assert_eq!("Hello there good people of the world!", &(rope1.to_string())[..]); -} - - -#[test] -fn append_2() { - let mut rope1 = Rope::from_str("Hello there good people of the world!"); - let rope2 = Rope::from_str(""); - - rope1.append(rope2); - - assert!(rope1.is_balanced()); - assert_eq!("Hello there good people of the world!", &(rope1.to_string())[..]); -} - - -#[test] -fn append_3() { - let mut rope1 = Rope::from_str(""); - let rope2 = Rope::from_str("Hello there good people of the world!"); - - rope1.append(rope2); - - assert!(rope1.is_balanced()); - assert_eq!("Hello there good people of the world!", &(rope1.to_string())[..]); -} - - -#[test] -fn append_4() { - let mut rope1 = Rope::from_str("1234567890-=qwertyuiop{}asdfghjkl;'zxcvbnm,.Hello World! Let's make this a long string for kicks and giggles. Who knows when it will end? No one! Well, except for the person writing it. And... eh... later, the person reading it. Because they'll get to the end. And then they'll know."); - let rope2 = Rope::from_str("Z"); - - rope1.append(rope2); - - assert!(rope1.is_balanced()); - assert_eq!(rope1.to_string(), "1234567890-=qwertyuiop{}asdfghjkl;'zxcvbnm,.Hello World! Let's make this a long string for kicks and giggles. Who knows when it will end? No one! Well, except for the person writing it. And... eh... later, the person reading it. Because they'll get to the end. And then they'll know.Z"); -} - - -#[test] -fn append_5() { - let mut rope1 = Rope::from_str("Z"); - let rope2 = Rope::from_str("1234567890-=qwertyuiop{}asdfghjkl;'zxcvbnm,.Hello World! Let's make this a long string for kicks and giggles. Who knows when it will end? No one! Well, except for the person writing it. And... eh... later, the person reading it. Because they'll get to the end. And then they'll know."); - - rope1.append(rope2); - - assert!(rope1.is_balanced()); - assert_eq!(rope1.to_string(), "Z1234567890-=qwertyuiop{}asdfghjkl;'zxcvbnm,.Hello World! Let's make this a long string for kicks and giggles. Who knows when it will end? No one! Well, except for the person writing it. And... eh... later, the person reading it. Because they'll get to the end. And then they'll know."); -} - - -#[test] -fn append_6() { - let mut rope1 = Rope::from_str("Hello there everyone!\u{000D}"); - let rope2 = Rope::from_str("\u{000A}How is everyone doing?"); - - assert_eq!(rope1.grapheme_count(), 22); - assert_eq!(rope2.grapheme_count(), 23); - - rope1.append(rope2); - - assert_eq!(rope1.to_string(), "Hello there everyone!\u{000D}\u{000A}How is everyone doing?"); - assert_eq!(rope1.grapheme_count(), 44); - assert_eq!(rope1.grapheme_at_index(21), "\u{000D}\u{000A}"); -} - - -#[test] -fn insert_text_at_char_index_1() { - let mut rope = Rope::from_str("Hello there!\u{000D}\u{000A}How are you?"); - - rope.insert_text_at_char_index("Z", 0); - - assert_eq!(rope.to_string(), "ZHello there!\u{000D}\u{000A}How are you?".to_string()); -} - - -#[test] -fn insert_text_at_char_index_2() { - let mut rope = Rope::from_str("Hello there!\u{000D}\u{000A}How are you?"); - - rope.insert_text_at_char_index("Z", 12); - - assert_eq!(rope.to_string(), "Hello there!Z\u{000D}\u{000A}How are you?".to_string()); -} - - -#[test] -fn insert_text_at_char_index_3() { - let mut rope = Rope::from_str("Hello there!\u{000D}\u{000A}How are you?"); - - rope.insert_text_at_char_index("Z", 13); - - assert_eq!(rope.to_string(), "Hello there!\u{000D}Z\u{000A}How are you?".to_string()); -} - - -#[test] -fn insert_text_at_char_index_4() { - let mut rope = Rope::from_str("Hello there!\u{000D}\u{000A}How are you?"); - - rope.insert_text_at_char_index("Z", 14); - - assert_eq!(rope.to_string(), "Hello there!\u{000D}\u{000A}ZHow are you?".to_string()); -} - - -#[test] -fn insert_text_at_char_index_5() { - let mut rope = Rope::from_str("Hello there!\u{000D}\u{000A}How are you?"); - - rope.insert_text_at_char_index("Z", 26); - - assert_eq!(rope.to_string(), "Hello there!\u{000D}\u{000A}How are you?Z".to_string()); -} - - -#[test] -fn insert_text_at_char_index_6() { - let mut s = String::from_utf8(vec!['c' as u8; (MAX_NODE_SIZE*47) - 1]).unwrap(); - s.push_str("\u{000D}"); - - let mut rope = Rope::from_str(&s[..]); - - s.push_str("\u{000A}"); - rope.insert_text_at_char_index("\u{000A}", MAX_NODE_SIZE*47); - - assert_eq!(rope.to_string(), s); - assert_eq!(rope.grapheme_count(), MAX_NODE_SIZE * 47); - assert_eq!(rope.grapheme_at_index((MAX_NODE_SIZE * 47) - 1), "\u{000D}\u{000A}"); -} - - -#[test] -fn remove_text_between_char_indices_1() { - let mut rope = Rope::from_str("Hello there!\u{000D}\u{000A}How are you?"); - - rope.remove_text_between_char_indices(0, 1); - - assert_eq!(rope.to_string(), "ello there!\u{000D}\u{000A}How are you?".to_string()); -} - - -#[test] -fn remove_text_between_char_indices_2() { - let mut rope = Rope::from_str("Hello there!\u{000D}\u{000A}How are you?"); - - rope.remove_text_between_char_indices(12, 13); - - assert_eq!(rope.to_string(), "Hello there!\u{000A}How are you?".to_string()); -} - - -#[test] -fn remove_text_between_char_indices_3() { - let mut rope = Rope::from_str("Hello there!\u{000D}\u{000A}How are you?"); - - rope.remove_text_between_char_indices(13, 14); - - assert_eq!(rope.to_string(), "Hello there!\u{000D}How are you?".to_string()); -} - - -#[test] -fn remove_text_between_char_indices_4() { - let mut s = String::from_utf8(vec!['c' as u8; (MAX_NODE_SIZE*27) - 1]).unwrap(); - s.push_str("\u{000D}"); - s.push_str("Hello there!\u{000A}How are you doing?"); - - let mut rope = Rope::from_str(&s[..]); - - rope.remove_text_between_char_indices((MAX_NODE_SIZE*27), (MAX_NODE_SIZE*27)+12); - - remove_text_between_char_indices(&mut s, (MAX_NODE_SIZE*27), (MAX_NODE_SIZE*27)+12); - - assert_eq!(rope.to_string(), s); - assert_eq!(rope.grapheme_count(), (MAX_NODE_SIZE * 27) + 18); - assert_eq!(rope.grapheme_at_index((MAX_NODE_SIZE * 27) - 1), "\u{000D}\u{000A}"); -} - - -#[test] -fn insert_text() { - let mut rope = Rope::new(); - - rope.insert_text_at_char_index("Hello 世界!", 0); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert!(rope.grapheme_count() == 9); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("!") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn insert_text_in_non_empty_buffer_1() { - let mut rope = Rope::from_str("Hello\n 世界\r\n!"); - - rope.insert_text_at_char_index("Again ", 0); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 17); - assert_eq!(rope.line_ending_count(), 2); - assert!(Some("A") == iter.next()); - assert!(Some("g") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("n") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("\r\n") == iter.next()); - assert!(Some("!") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn insert_text_in_non_empty_buffer_2() { - let mut rope = Rope::from_str("Hello\n 世界\r\n!"); - - rope.insert_text_at_char_index(" again", 5); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 17); - assert_eq!(rope.line_ending_count(), 2); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("g") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("n") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("\r\n") == iter.next()); - assert!(Some("!") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn insert_text_in_non_empty_buffer_3() { - let mut rope = Rope::from_str("Hello\n 世界\r\n!"); - - rope.insert_text_at_char_index("again", 6); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 16); - assert_eq!(rope.line_ending_count(), 2); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("g") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("n") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("\r\n") == iter.next()); - assert!(Some("!") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn insert_text_in_non_empty_buffer_4() { - let mut rope = Rope::from_str("Hello\n 世界\r\n!"); - - rope.insert_text_at_char_index("again", 12); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 16); - assert_eq!(rope.line_ending_count(), 2); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("\r\n") == iter.next()); - assert!(Some("!") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("g") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("n") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn insert_text_in_non_empty_buffer_5() { - let mut rope = Rope::from_str("Hello\n 世界\r\n!"); - - rope.insert_text_at_char_index("again", 2); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 16); - assert_eq!(rope.line_ending_count(), 2); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("g") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("n") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("\r\n") == iter.next()); - assert!(Some("!") == iter.next()); - - assert!(None == iter.next()); -} - - -#[test] -fn insert_text_in_non_empty_buffer_6() { - let mut rope = Rope::from_str("Hello\n 世界\r\n!"); - - rope.insert_text_at_char_index("again", 8); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 16); - assert_eq!(rope.line_ending_count(), 2); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("g") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("n") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("\r\n") == iter.next()); - assert!(Some("!") == iter.next()); - - assert!(None == iter.next()); -} - - -#[test] -fn insert_text_in_non_empty_buffer_7() { - let mut rope = Rope::from_str("Hello\n 世界\r\n!"); - - rope.insert_text_at_char_index("\nag\n\nain\n", 2); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 20); - assert_eq!(rope.line_ending_count(), 6); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("g") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("a") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("n") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("\r\n") == iter.next()); - assert!(Some("!") == iter.next()); - - assert!(None == iter.next()); -} - - -#[test] -fn remove_text_1() { - let mut rope = Rope::from_str("Hi\nthere\npeople\nof\nthe\nworld!"); - - rope.remove_text_between_char_indices(0, 3); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 26); - assert_eq!(rope.line_ending_count(), 4); - 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 rope = Rope::from_str("Hi\nthere\npeople\nof\nthe\nworld!"); - - rope.remove_text_between_char_indices(0, 12); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 17); - assert_eq!(rope.line_ending_count(), 3); - 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_3() { - let mut rope = Rope::from_str("Hi\nthere\npeople\nof\nthe\nworld!"); - - rope.remove_text_between_char_indices(5, 17); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 17); - assert_eq!(rope.line_ending_count(), 3); - 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_4() { - let mut rope = Rope::from_str("Hi\nthere\npeople\nof\nthe\nworld!"); - - rope.remove_text_between_char_indices(23, 29); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 23); - assert_eq!(rope.line_ending_count(), 5); - 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!(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!(None == iter.next()); -} - - -#[test] -fn remove_text_5() { - let mut rope = Rope::from_str("Hi\nthere\npeople\nof\nthe\nworld!"); - - rope.remove_text_between_char_indices(17, 29); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 17); - assert_eq!(rope.line_ending_count(), 3); - 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] -fn remove_text_6() { - let mut rope = Rope::from_str("Hello\nworld!"); - - rope.remove_text_between_char_indices(3, 12); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 3); - assert_eq!(rope.line_ending_count(), 0); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn remove_text_7() { - let mut rope = Rope::from_str("Hi\nthere\nworld!"); - - rope.remove_text_between_char_indices(5, 15); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 5); - assert_eq!(rope.line_ending_count(), 1); - 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!(None == iter.next()); -} - - -#[test] -fn remove_text_8() { - let mut rope = Rope::from_str("Hello\nworld!"); - - rope.remove_text_between_char_indices(3, 11); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 4); - assert_eq!(rope.line_ending_count(), 0); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("!") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn remove_text_9() { - let mut rope = Rope::from_str("Hello\nworld!"); - - rope.remove_text_between_char_indices(8, 12); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 8); - assert_eq!(rope.line_ending_count(), 1); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("w") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn remove_text_10() { - let mut rope = Rope::from_str("12\n34\n56\n78"); - - rope.remove_text_between_char_indices(4, 11); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 4); - assert_eq!(rope.line_ending_count(), 1); - assert!(Some("1") == iter.next()); - assert!(Some("2") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("3") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn remove_text_11() { - let mut rope = Rope::from_str("1234567890"); - - rope.remove_text_between_char_indices(9, 10); - - let mut iter = rope.grapheme_iter(); - - assert!(rope.is_balanced()); - assert_eq!(rope.grapheme_count(), 9); - assert_eq!(rope.line_ending_count(), 0); - assert!(Some("1") == iter.next()); - assert!(Some("2") == iter.next()); - assert!(Some("3") == iter.next()); - assert!(Some("4") == iter.next()); - assert!(Some("5") == iter.next()); - assert!(Some("6") == iter.next()); - assert!(Some("7") == iter.next()); - assert!(Some("8") == iter.next()); - assert!(Some("9") == iter.next()); - assert!(None == iter.next()); -} - - -#[test] -fn rebalance_1() { - let left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; MAX_NODE_SIZE * 64]).unwrap())[..]); - let right = Rope::from_str(&(String::from_utf8(vec!['c' as u8; MAX_NODE_SIZE * 1]).unwrap())[..]); - - let mut rope = Rope { - data: RopeData::Branch(Box::new(left), Box::new(right)), - char_count_: 0, - grapheme_count_: 0, - line_ending_count_: 0, - tree_height: 1, - }; - rope.update_stats(); - - //let mut f1 = BufferedWriter::new(File::create(&Path::new("yar1.gv")).unwrap()); - //f1.write_str(&(rope.to_graphviz())[..]); - - rope.rebalance(); - - //let mut f2 = BufferedWriter::new(File::create(&Path::new("yar2.gv")).unwrap()); - //f2.write_str(&(rope.to_graphviz())[..]); - - assert!(rope.is_balanced()); -} - - -#[test] -fn rebalance_2() { - let left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; MAX_NODE_SIZE * 1]).unwrap())[..]); - let right = Rope::from_str(&(String::from_utf8(vec!['c' as u8; MAX_NODE_SIZE * 64]).unwrap())[..]); - - let mut rope = Rope { - data: RopeData::Branch(Box::new(left), Box::new(right)), - char_count_: 0, - grapheme_count_: 0, - line_ending_count_: 0, - tree_height: 1, - }; - rope.update_stats(); - - //let mut f1 = BufferedWriter::new(File::create(&Path::new("yar1.gv")).unwrap()); - //f1.write_str(&(rope.to_graphviz())[..]); - - rope.rebalance(); - - //let mut f2 = BufferedWriter::new(File::create(&Path::new("yar2.gv")).unwrap()); - //f2.write_str(&(rope.to_graphviz())[..]); - - assert!(rope.is_balanced()); -} - - -#[test] -fn rebalance_3() { - let left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; MAX_NODE_SIZE * 53]).unwrap())[..]); - let right = Rope::from_str(&(String::from_utf8(vec!['c' as u8; MAX_NODE_SIZE * 1]).unwrap())[..]); - - let mut rope = Rope { - data: RopeData::Branch(Box::new(left), Box::new(right)), - char_count_: 0, - grapheme_count_: 0, - line_ending_count_: 0, - tree_height: 1, - }; - rope.update_stats(); - - //let mut f1 = BufferedWriter::new(File::create(&Path::new("yar1.gv")).unwrap()); - //f1.write_str(&(rope.to_graphviz())[..]); - - rope.rebalance(); - - //let mut f2 = BufferedWriter::new(File::create(&Path::new("yar2.gv")).unwrap()); - //f2.write_str(&(rope.to_graphviz())[..]); - - assert!(rope.is_balanced()); -} - - -#[test] -fn rebalance_4() { - let left = Rope::from_str(&(String::from_utf8(vec!['c' as u8; MAX_NODE_SIZE * 1]).unwrap())[..]); - let right = Rope::from_str(&(String::from_utf8(vec!['c' as u8; MAX_NODE_SIZE * 53]).unwrap())[..]); - - let mut rope = Rope { - data: RopeData::Branch(Box::new(left), Box::new(right)), - char_count_: 0, - grapheme_count_: 0, - line_ending_count_: 0, - tree_height: 1, - }; - rope.update_stats(); - - //let mut f1 = BufferedWriter::new(File::create(&Path::new("yar1.gv")).unwrap()); - //f1.write_str(&(rope.to_graphviz())[..]); - - rope.rebalance(); - - //let mut f2 = BufferedWriter::new(File::create(&Path::new("yar2.gv")).unwrap()); - //f2.write_str(&(rope.to_graphviz())[..]); - - assert!(rope.is_balanced()); -}