diff --git a/Cargo.lock b/Cargo.lock index 7f867f1..b0bfc63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ name = "Led" version = "0.0.2" dependencies = [ "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ropey 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ropey 0.6.3 (git+https://github.com/cessen/ropey)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -87,11 +87,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ropey" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.6.3" +source = "git+https://github.com/cessen/ropey#e9b361b5b45d3e389bbfcb4f75127bec69e0b199" 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)", ] [[package]] @@ -209,7 +208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa" "checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" -"checksum ropey 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "661796e716a1916c9fd80457ee204b05b47afa4c1f1ceec3c7fd40e59f0e31f1" +"checksum ropey 0.6.3 (git+https://github.com/cessen/ropey)" = "" "checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" "checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" "checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" diff --git a/Cargo.toml b/Cargo.toml index fd056ba..0f8fb93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ name = "led" path = "src/main.rs" [dependencies] -ropey = "0.6" +ropey = { git = "https://github.com/cessen/ropey", branch = "master" } unicode-segmentation = "1.2" unicode-width = "0.1" serde = "1.*" diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 0a5d641..41ae239 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -10,6 +10,7 @@ use ropey::{Rope, RopeSlice}; use self::undo_stack::UndoStack; use self::undo_stack::Operation::*; use string_utils::char_count; +use utils::{prev_grapheme_boundary, next_grapheme_boundary, is_grapheme_boundary, RopeGraphemes}; mod undo_stack; @@ -70,21 +71,23 @@ impl Buffer { } pub fn is_grapheme(&self, char_idx: usize) -> bool { - self.text.is_grapheme_boundary(char_idx) + is_grapheme_boundary(&self.text.slice(..), char_idx) } + /// Finds the nth next grapheme boundary after the given char position. pub fn nth_next_grapheme(&self, char_idx: usize, n: usize) -> usize { let mut char_idx = char_idx; for _ in 0..n { - char_idx = self.text.next_grapheme_boundary(char_idx); + char_idx = next_grapheme_boundary(&self.text.slice(..), char_idx); } char_idx } + /// Finds the nth previous grapheme boundary before the given char position. pub fn nth_prev_grapheme(&self, char_idx: usize, n: usize) -> usize { let mut char_idx = char_idx; for _ in 0..n { - char_idx = self.text.prev_grapheme_boundary(char_idx); + char_idx = prev_grapheme_boundary(&self.text.slice(..), char_idx); } char_idx } @@ -331,10 +334,8 @@ impl Buffer { // Text reading functions // ------------------------------------------------------------------------ - pub fn get_grapheme<'a>(&'a self, index: usize) -> &'a str { - self.text - .slice(index..(index + 1)) - .graphemes() + pub fn get_grapheme<'a>(&'a self, index: usize) -> RopeSlice<'a> { + RopeGraphemes::new(&self.text.slice(index..)) .nth(0) .unwrap() } @@ -353,16 +354,16 @@ impl Buffer { // ------------------------------------------------------------------------ /// Creates an iterator at the first character - pub fn grapheme_iter<'a>(&'a self) -> ropey::iter::Graphemes<'a> { - self.text.graphemes() + pub fn grapheme_iter<'a>(&'a self) -> RopeGraphemes<'a> { + RopeGraphemes::new(&self.text.slice(..)) } /// 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) -> ropey::iter::Graphemes<'a> { + pub fn grapheme_iter_at_index<'a>(&'a self, index: usize) -> RopeGraphemes<'a> { let len = self.text.len_chars(); - self.text.slice(index..len).graphemes() + RopeGraphemes::new(&self.text.slice(index..len)) } pub fn line_iter<'a>(&'a self) -> ropey::iter::Lines<'a> { @@ -394,15 +395,15 @@ mod tests { assert!(buf.char_count() == 9); assert!(buf.line_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(" ") == iter.next()); - assert!(Some("世") == iter.next()); - assert!(Some("界") == iter.next()); - assert!(Some("!") == iter.next()); + assert!("H" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!(" " == iter.next().unwrap()); + assert!("世" == iter.next().unwrap()); + assert!("界" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -416,17 +417,17 @@ mod tests { assert!(buf.char_count() == 12); assert!(buf.line_count() == 3); - 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!("H" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!(" " == iter.next().unwrap()); + assert!("世" == iter.next().unwrap()); + assert!("界" == iter.next().unwrap()); + assert!("\r\n" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -441,23 +442,23 @@ mod tests { assert_eq!(buf.char_count(), 18); assert_eq!(buf.line_count(), 3); - assert_eq!(Some("A"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some(" "), iter.next()); - 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("\n"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("世"), iter.next()); - assert_eq!(Some("界"), iter.next()); - assert_eq!(Some("\r\n"), iter.next()); - assert_eq!(Some("!"), iter.next()); + assert_eq!("A", iter.next().unwrap()); + assert_eq!("g", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("n", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("世", iter.next().unwrap()); + assert_eq!("界", iter.next().unwrap()); + assert_eq!("\r\n", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -472,23 +473,23 @@ mod tests { assert_eq!(buf.char_count(), 18); assert_eq!(buf.line_count(), 3); - 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("a"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("世"), iter.next()); - assert_eq!(Some("界"), iter.next()); - assert_eq!(Some("\r\n"), iter.next()); - assert_eq!(Some("!"), iter.next()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("g", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("n", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("世", iter.next().unwrap()); + assert_eq!("界", iter.next().unwrap()); + assert_eq!("\r\n", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -503,22 +504,22 @@ mod tests { assert_eq!(buf.char_count(), 17); assert_eq!(buf.line_count(), 3); - 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("\n"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("世"), iter.next()); - assert_eq!(Some("界"), iter.next()); - assert_eq!(Some("\r\n"), iter.next()); - assert_eq!(Some("!"), iter.next()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("g", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("n", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("世", iter.next().unwrap()); + assert_eq!("界", iter.next().unwrap()); + assert_eq!("\r\n", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -533,22 +534,22 @@ mod tests { assert_eq!(buf.char_count(), 17); assert_eq!(buf.line_count(), 3); - 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("\n"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("世"), iter.next()); - assert_eq!(Some("界"), iter.next()); - assert_eq!(Some("\r\n"), iter.next()); - assert_eq!(Some("!"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("世", iter.next().unwrap()); + assert_eq!("界", iter.next().unwrap()); + assert_eq!("\r\n", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("g", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("n", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -563,22 +564,22 @@ mod tests { assert_eq!(buf.char_count(), 17); assert_eq!(buf.line_count(), 3); - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("世"), iter.next()); - assert_eq!(Some("界"), iter.next()); - assert_eq!(Some("\r\n"), iter.next()); - assert_eq!(Some("!"), iter.next()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("g", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("n", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("世", iter.next().unwrap()); + assert_eq!("界", iter.next().unwrap()); + assert_eq!("\r\n", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -594,22 +595,22 @@ mod tests { assert_eq!(buf.char_count(), 17); assert_eq!(buf.line_count(), 3); - 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("\n"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("世"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("界"), iter.next()); - assert_eq!(Some("\r\n"), iter.next()); - assert_eq!(Some("!"), iter.next()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("世", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("g", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("n", iter.next().unwrap()); + assert_eq!("界", iter.next().unwrap()); + assert_eq!("\r\n", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -625,26 +626,26 @@ mod tests { assert_eq!(buf.char_count(), 21); assert_eq!(buf.line_count(), 7); - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("g"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("a"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("n"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some(" "), iter.next()); - assert_eq!(Some("世"), iter.next()); - assert_eq!(Some("界"), iter.next()); - assert_eq!(Some("\r\n"), iter.next()); - assert_eq!(Some("!"), iter.next()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("g", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("a", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("n", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!(" ", iter.next().unwrap()); + assert_eq!("世", iter.next().unwrap()); + assert_eq!("界", iter.next().unwrap()); + assert_eq!("\r\n", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -663,32 +664,32 @@ mod tests { assert!(buf.char_count() == 26); assert!(buf.line_count() == 5); - assert!(Some("t") == iter.next()); - assert!(Some("h") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("r") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("p") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("p") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("f") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("t") == iter.next()); - assert!(Some("h") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("w") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("r") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("d") == iter.next()); - assert!(Some("!") == iter.next()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -706,23 +707,23 @@ mod tests { assert!(buf.char_count() == 17); assert!(buf.line_count() == 4); - 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!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -740,23 +741,23 @@ mod tests { assert!(buf.char_count() == 17); assert!(buf.line_count() == 4); - assert!(Some("H") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("t") == iter.next()); - assert!(Some("h") == iter.next()); - assert!(Some("f") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("t") == iter.next()); - assert!(Some("h") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("w") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("r") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("d") == iter.next()); - assert!(Some("!") == iter.next()); + assert!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -774,29 +775,29 @@ mod tests { assert!(buf.char_count() == 23); assert!(buf.line_count() == 6); - 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!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -814,23 +815,23 @@ mod tests { assert!(buf.char_count() == 17); assert!(buf.line_count() == 4); - assert!(Some("H") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("t") == iter.next()); - assert!(Some("h") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("r") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("p") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("o") == iter.next()); - assert!(Some("p") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("o") == iter.next()); + assert!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -848,9 +849,9 @@ mod tests { assert!(buf.char_count() == 3); assert!(buf.line_count() == 1); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); + assert!("H" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -868,11 +869,11 @@ mod tests { assert!(buf.char_count() == 5); assert!(buf.line_count() == 2); - 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!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -890,10 +891,10 @@ mod tests { assert!(buf.char_count() == 4); assert!(buf.line_count() == 1); - assert!(Some("H") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("l") == iter.next()); - assert!(Some("!") == iter.next()); + assert!("H" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -911,14 +912,14 @@ mod tests { assert!(buf.char_count() == 8); assert!(buf.line_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("w") == iter.next()); - assert!(Some("o") == iter.next()); + assert!("H" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -936,10 +937,10 @@ mod tests { assert!(buf.char_count() == 4); assert!(buf.line_count() == 2); - assert!(Some("1") == iter.next()); - assert!(Some("2") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("3") == iter.next()); + assert!("1" == iter.next().unwrap()); + assert!("2" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("3" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -957,15 +958,15 @@ mod tests { assert!(buf.char_count() == 9); assert!(buf.line_count() == 1); - 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!("1" == iter.next().unwrap()); + assert!("2" == iter.next().unwrap()); + assert!("3" == iter.next().unwrap()); + assert!("4" == iter.next().unwrap()); + assert!("5" == iter.next().unwrap()); + assert!("6" == iter.next().unwrap()); + assert!("7" == iter.next().unwrap()); + assert!("8" == iter.next().unwrap()); + assert!("9" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -981,35 +982,35 @@ mod tests { assert!(buf.char_count() == 29); assert!(buf.line_count() == 6); - assert!(Some("t") == iter.next()); - assert!(Some("h") == iter.next()); - assert!(Some("H") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("\n") == 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!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -1025,35 +1026,35 @@ mod tests { assert!(buf.char_count() == 29); assert!(buf.line_count() == 6); - assert!(Some("H") == iter.next()); - assert!(Some("i") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("\n") == iter.next()); - assert!(Some("p") == iter.next()); - assert!(Some("e") == 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("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!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -1069,35 +1070,35 @@ mod tests { assert!(buf.char_count() == 29); assert!(buf.line_count() == 6); - 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("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("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("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!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -1113,35 +1114,35 @@ mod tests { assert!(buf.char_count() == 29); assert!(buf.line_count() == 6); - 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("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!(Some("h") == iter.next()); - assert!(Some("e") == iter.next()); - assert!(Some("\n") == iter.next()); + assert!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -1157,35 +1158,35 @@ mod tests { assert!(buf.char_count() == 29); assert!(buf.line_count() == 6); - 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!(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!("H" == iter.next().unwrap()); + assert!("i" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("p" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } @@ -1203,19 +1204,19 @@ mod tests { assert_eq!(buf.char_count(), 13); assert_eq!(buf.line_count(), 3); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("f"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("t"), iter.next()); - assert_eq!(Some("h"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("\n"), 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!("o", iter.next().unwrap()); + assert_eq!("f", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("t", iter.next().unwrap()); + assert_eq!("h", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("w", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("r", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("d", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -1233,19 +1234,19 @@ mod tests { assert_eq!(buf.char_count(), 13); assert_eq!(buf.line_count(), 3); - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("t"), iter.next()); - assert_eq!(Some("h"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("\n"), 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!("H", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("t", iter.next().unwrap()); + assert_eq!("h", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("w", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("r", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("d", iter.next().unwrap()); + assert_eq!("!", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -1265,21 +1266,21 @@ mod tests { assert_eq!(buf.char_count(), 15); assert_eq!(buf.line_count(), 3); - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("t"), iter.next()); - assert_eq!(Some("h"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("p"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("p"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("e"), iter.next()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("t", iter.next().unwrap()); + assert_eq!("h", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("r", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("p", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("p", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -1297,21 +1298,21 @@ mod tests { assert_eq!(buf.char_count(), 15); assert_eq!(buf.line_count(), 3); - assert_eq!(Some("H"), iter.next()); - assert_eq!(Some("i"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("t"), iter.next()); - assert_eq!(Some("h"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("r"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("\n"), iter.next()); - assert_eq!(Some("p"), iter.next()); - assert_eq!(Some("e"), iter.next()); - assert_eq!(Some("o"), iter.next()); - assert_eq!(Some("p"), iter.next()); - assert_eq!(Some("l"), iter.next()); - assert_eq!(Some("e"), iter.next()); + assert_eq!("H", iter.next().unwrap()); + assert_eq!("i", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("t", iter.next().unwrap()); + assert_eq!("h", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("r", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("\n", iter.next().unwrap()); + assert_eq!("p", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); + assert_eq!("o", iter.next().unwrap()); + assert_eq!("p", iter.next().unwrap()); + assert_eq!("l", iter.next().unwrap()); + assert_eq!("e", iter.next().unwrap()); assert_eq!(None, iter.next()); } @@ -1456,19 +1457,19 @@ mod tests { let mut iter = buf.grapheme_iter_at_index(16); - 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!("o" == iter.next().unwrap()); + assert!("f" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("t" == iter.next().unwrap()); + assert!("h" == iter.next().unwrap()); + assert!("e" == iter.next().unwrap()); + assert!("\n" == iter.next().unwrap()); + assert!("w" == iter.next().unwrap()); + assert!("o" == iter.next().unwrap()); + assert!("r" == iter.next().unwrap()); + assert!("l" == iter.next().unwrap()); + assert!("d" == iter.next().unwrap()); + assert!("!" == iter.next().unwrap()); assert!(None == iter.next()); } diff --git a/src/editor/mod.rs b/src/editor/mod.rs index cd46321..7c4016b 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -7,8 +7,8 @@ use formatter::LineFormatter; use formatter::RoundingBehavior::*; use std::path::{Path, PathBuf}; use std::cmp::{max, min}; -use string_utils::{char_count, str_to_line_ending, LineEnding}; -use utils::digit_count; +use string_utils::{char_count, rope_slice_to_line_ending, LineEnding}; +use utils::{digit_count, RopeGraphemes}; use self::cursor::CursorSet; mod cursor; @@ -100,17 +100,15 @@ impl Editor { for line in self.buffer.line_iter().take(100) { // Get the line ending let ending = if line.len_chars() == 1 { - let g = line.slice((line.len_chars() - 1)..) - .graphemes() + let g = RopeGraphemes::new(&line.slice((line.len_chars() - 1)..)) .last() .unwrap(); - str_to_line_ending(g) + rope_slice_to_line_ending(&g) } else if line.len_chars() > 1 { - let g = line.slice((line.len_chars() - 2)..) - .graphemes() + let g = RopeGraphemes::new(&line.slice((line.len_chars() - 2)..)) .last() .unwrap(); - str_to_line_ending(g) + rope_slice_to_line_ending(&g) } else { LineEnding::None }; @@ -626,7 +624,7 @@ impl Editor { 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_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, @@ -642,7 +640,7 @@ impl Editor { 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_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, diff --git a/src/formatter.rs b/src/formatter.rs index 206719f..3de0e2d 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -1,7 +1,9 @@ #![allow(dead_code)] use std::cmp::min; +use ropey::RopeSlice; use buffer::Buffer; +use utils::RopeGraphemes; // Maximum graphemes in a line before a soft line break is forced. // This is necessary to prevent pathological formatting cases which @@ -24,13 +26,13 @@ pub trait LineFormatter { /// 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; + T: Iterator>; /// Converts a char 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, char_idx: usize) -> (usize, usize) where - T: Iterator; + T: Iterator>; /// Converts a visual 2d position into a char index within a text. /// The text to be formatted is passed as a grapheme iterator. @@ -41,7 +43,7 @@ pub trait LineFormatter { rounding: (RoundingBehavior, RoundingBehavior), ) -> usize where - T: Iterator; + T: Iterator>; fn index_to_horizontal_v2d(&self, buf: &Buffer, char_idx: usize) -> usize { let (line_i, col_i) = buf.index_to_line_col(char_idx); @@ -53,7 +55,7 @@ pub trait LineFormatter { // Get an iter into the right block let a = line_block * LINE_BLOCK_LENGTH; let b = min(line.len_chars(), (line_block + 1) * LINE_BLOCK_LENGTH); - let g_iter = line.slice(a..b).graphemes(); + let g_iter = RopeGraphemes::new(&line.slice(a..b)); return self.index_to_v2d(g_iter, col_i_adjusted).1; } @@ -77,10 +79,10 @@ pub trait LineFormatter { let mut line = buf.get_line(line_i); let (mut y, x) = self.index_to_v2d( - line.slice( + RopeGraphemes::new(&line.slice( (line_block * LINE_BLOCK_LENGTH) ..min(line.len_chars(), (line_block + 1) * LINE_BLOCK_LENGTH), - ).graphemes(), + )), col_i_adjusted, ); @@ -90,10 +92,10 @@ pub trait LineFormatter { let mut block_index: usize = line_block; loop { line = buf.get_line(line_i); - let (h, _) = self.dimensions(line.slice( + let (h, _) = self.dimensions(RopeGraphemes::new(&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; @@ -127,10 +129,10 @@ pub trait LineFormatter { } else { block_index -= 1; } - let (h, _) = self.dimensions(line.slice( + let (h, _) = self.dimensions(RopeGraphemes::new(&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!(); @@ -145,7 +147,7 @@ pub trait LineFormatter { ..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), + self.v2d_to_index(RopeGraphemes::new(&block_slice), (y, x), rounding), LINE_BLOCK_LENGTH - 1, ); col_i = (block_index * LINE_BLOCK_LENGTH) + block_col_i; @@ -173,11 +175,11 @@ pub trait LineFormatter { // Calculate the horizontal position let (v, _) = self.index_to_v2d( - line.slice(start_index..end_index).graphemes(), + RopeGraphemes::new(&line.slice(start_index..end_index)), col_i_adjusted, ); let block_col_i = self.v2d_to_index( - line.slice(start_index..end_index).graphemes(), + RopeGraphemes::new(&line.slice(start_index..end_index)), (v, horizontal), (RoundingBehavior::Floor, rounding), ); diff --git a/src/string_utils.rs b/src/string_utils.rs index dfe7e4c..cfd6517 100644 --- a/src/string_utils.rs +++ b/src/string_utils.rs @@ -2,6 +2,7 @@ //! Misc helpful utility functions for TextBuffer related stuff. use std::iter::repeat; +use ropey::RopeSlice; use unicode_segmentation::UnicodeSegmentation; pub fn is_line_ending(text: &str) -> bool { @@ -13,6 +14,10 @@ pub fn is_line_ending(text: &str) -> bool { } } +pub fn rope_slice_is_line_ending(text: &RopeSlice) -> bool { + rope_slice_to_line_ending(text) != LineEnding::None +} + pub fn is_whitespace(text: &str) -> bool { // TODO: this is a naive categorization of whitespace characters. // For better categorization these should be split up into groups @@ -44,6 +49,34 @@ pub fn is_whitespace(text: &str) -> bool { } } +pub fn rope_slice_is_whitespace(text: &RopeSlice) -> bool { + // TODO: this is a naive categorization of whitespace characters. + // For better categorization these should be split up into groups + // based on e.g. breaking vs non-breaking spaces, among other things. + + text == "\u{0020}" // SPACE + || text == "\u{0009}" // CHARACTER TABULATION + || text == "\u{00A0}" // NO-BREAK SPACE + //|| "\u{1680}" // OGHAM SPACE MARK (here for completeness, but usually displayed as a dash, not as whitespace) + || text == "\u{180E}" // MONGOLIAN VOWEL SEPARATOR + || text == "\u{2000}" // EN QUAD + || text == "\u{2001}" // EM QUAD + || text == "\u{2002}" // EN SPACE + || text == "\u{2003}" // EM SPACE + || text == "\u{2004}" // THREE-PER-EM SPACE + || text == "\u{2005}" // FOUR-PER-EM SPACE + || text == "\u{2006}" // SIX-PER-EM SPACE + || text == "\u{2007}" // FIGURE SPACE + || text == "\u{2008}" // PUNCTUATION SPACE + || text == "\u{2009}" // THIN SPACE + || text == "\u{200A}" // HAIR SPACE + || text == "\u{200B}" // ZERO WIDTH SPACE + || text == "\u{202F}" // NARROW NO-BREAK SPACE + || text == "\u{205F}" // MEDIUM MATHEMATICAL SPACE + || text == "\u{3000}" // IDEOGRAPHIC SPACE + || text == "\u{FEFF}" // ZERO WIDTH NO-BREAK SPACE +} + pub fn line_ending_count(text: &str) -> usize { let mut count = 0; for g in UnicodeSegmentation::graphemes(text, true) { @@ -286,6 +319,29 @@ pub fn str_to_line_ending(g: &str) -> LineEnding { } } +pub fn rope_slice_to_line_ending(g: &RopeSlice) -> LineEnding { + if g == "\u{000D}\u{000A}" { + LineEnding::CRLF + } else if g == "\u{000A}" { + LineEnding::LF + } else if g == "\u{000B}" { + LineEnding::VT + } else if g == "\u{000C}" { + LineEnding::FF + } else if g == "\u{000D}" { + LineEnding::CR + } else if g == "\u{0085}" { + LineEnding::NEL + } else if g == "\u{2028}" { + LineEnding::LS + } else if g == "\u{2029}" { + LineEnding::PS + } else { + // Not a line ending + LineEnding::None + } +} + pub fn line_ending_to_str(ending: LineEnding) -> &'static str { LINE_ENDINGS[ending as usize] } diff --git a/src/term_ui/formatter.rs b/src/term_ui/formatter.rs index 2075a9e..fc04ef8 100644 --- a/src/term_ui/formatter.rs +++ b/src/term_ui/formatter.rs @@ -1,7 +1,8 @@ use std::cmp::max; -use unicode_width::UnicodeWidthStr; -use string_utils::{is_line_ending, is_whitespace}; +use ropey::RopeSlice; +use utils::grapheme_width; +use string_utils::{rope_slice_is_line_ending, rope_slice_is_whitespace}; use formatter::{LineFormatter, RoundingBehavior}; pub enum WrapType { @@ -47,7 +48,7 @@ impl ConsoleLineFormatter { pub fn iter<'a, T>(&'a self, g_iter: T) -> ConsoleLineFormatterVisIter<'a, T> where - T: Iterator, + T: Iterator>, { ConsoleLineFormatterVisIter::<'a, T> { grapheme_iter: g_iter, @@ -68,7 +69,7 @@ impl LineFormatter for ConsoleLineFormatter { fn dimensions<'a, T>(&'a self, g_iter: T) -> (usize, usize) where - T: Iterator, + T: Iterator>, { let mut dim: (usize, usize) = (0, 0); @@ -83,7 +84,7 @@ impl LineFormatter for ConsoleLineFormatter { fn index_to_v2d<'a, T>(&'a self, g_iter: T, char_idx: usize) -> (usize, usize) where - T: Iterator, + T: Iterator>, { let mut pos = (0, 0); let mut i = 0; @@ -109,7 +110,7 @@ impl LineFormatter for ConsoleLineFormatter { _: (RoundingBehavior, RoundingBehavior), ) -> usize where - T: Iterator, + T: Iterator>, { // TODO: handle rounding modes let mut prev_i = 0; @@ -137,7 +138,7 @@ impl LineFormatter for ConsoleLineFormatter { // =================================================================== pub struct ConsoleLineFormatterVisIter<'a, T> where - T: Iterator, + T: Iterator>, { grapheme_iter: T, f: &'a ConsoleLineFormatter, @@ -146,15 +147,15 @@ where indent: usize, indent_found: bool, - word_buf: Vec<&'a str>, + word_buf: Vec>, word_i: usize, } impl<'a, T> ConsoleLineFormatterVisIter<'a, T> where - T: Iterator, + T: Iterator>, { - fn next_nowrap(&mut self, g: &'a str) -> Option<(&'a str, (usize, usize), usize)> { + fn next_nowrap(&mut self, g: RopeSlice<'a>) -> Option<(RopeSlice<'a>, (usize, usize), usize)> { let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize); let pos = self.pos; @@ -164,9 +165,9 @@ where fn next_charwrap( &mut self, - g: &'a str, + g: RopeSlice<'a>, wrap_width: usize, - ) -> Option<(&'a str, (usize, usize), usize)> { + ) -> Option<(RopeSlice<'a>, (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 { @@ -198,7 +199,7 @@ where } } else { if !self.indent_found { - if is_whitespace(g) { + if rope_slice_is_whitespace(&g) { self.indent += width; } else { self.indent_found = true; @@ -214,11 +215,11 @@ where impl<'a, T> Iterator for ConsoleLineFormatterVisIter<'a, T> where - T: Iterator, + T: Iterator>, { - type Item = (&'a str, (usize, usize), usize); + type Item = (RopeSlice<'a>, (usize, usize), usize); - fn next(&mut self) -> Option<(&'a str, (usize, usize), usize)> { + fn next(&mut self) -> Option<(RopeSlice<'a>, (usize, usize), usize)> { match self.f.wrap_type { WrapType::NoWrap => { if let Some(g) = self.grapheme_iter.next() { @@ -249,14 +250,14 @@ where self.f.tab_width as usize, ); word_width += width; - if is_whitespace(g) { + if rope_slice_is_whitespace(&g) { break; } } if self.word_buf.len() == 0 { return None; - } else if !self.indent_found && !is_whitespace(self.word_buf[0]) { + } else if !self.indent_found && !rope_slice_is_whitespace(&self.word_buf[0]) { self.indent_found = true; } @@ -300,27 +301,22 @@ where /// Returns the visual width of a grapheme given a starting /// position on a line. -fn grapheme_vis_width_at_vis_pos(g: &str, pos: usize, tab_width: usize) -> usize { - match g { - "\t" => { - let ending_pos = ((pos / tab_width) + 1) * tab_width; - return ending_pos - pos; - } - - _ => { - if is_line_ending(g) { - return 1; - } else { - return UnicodeWidthStr::width(g); - } - } +fn grapheme_vis_width_at_vis_pos(g: RopeSlice, pos: usize, tab_width: usize) -> usize { + if g == "\t" { + let ending_pos = ((pos / tab_width) + 1) * tab_width; + return ending_pos - pos; + } else if rope_slice_is_line_ending(&g) { + return 1; + } else { + return grapheme_width(&g); } } #[cfg(test)] mod tests { #![allow(unused_imports)] - use unicode_segmentation::UnicodeSegmentation; + use ropey::Rope; + use utils::RopeGraphemes; use super::*; use formatter::{LineFormatter, LINE_BLOCK_LENGTH}; use formatter::RoundingBehavior::{Ceiling, Floor, Round}; @@ -328,7 +324,7 @@ mod tests { #[test] fn dimensions_1() { - let text = "Hello there, stranger!"; // 22 graphemes long + let text = Rope::from_str("Hello there, stranger!"); // 22 graphemes long let mut f = ConsoleLineFormatter::new(4); f.wrap_type = WrapType::CharWrap(0); @@ -337,14 +333,14 @@ mod tests { f.set_wrap_width(80); assert_eq!( - f.dimensions(UnicodeSegmentation::graphemes(text, true)), + f.dimensions(RopeGraphemes::new(&text.slice(..))), (1, 22) ); } #[test] fn dimensions_2() { - let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long + let text = Rope::from_str("Hello there, stranger! How are you doing this fine day?"); // 56 graphemes long let mut f = ConsoleLineFormatter::new(4); f.wrap_type = WrapType::CharWrap(0); @@ -353,7 +349,7 @@ mod tests { f.set_wrap_width(12); assert_eq!( - f.dimensions(UnicodeSegmentation::graphemes(text, true)), + f.dimensions(RopeGraphemes::new(&text.slice(..))), (5, 12) ); } @@ -361,7 +357,7 @@ mod tests { #[test] fn dimensions_3() { // 55 graphemes long - let text = "税マイミ文末\ + let text = Rope::from_str("税マイミ文末\ レ日題イぽじ\ や男目統ス公\ 身みトしつ結\ @@ -370,7 +366,7 @@ mod tests { 凱字テ式重反\ てす献罪がご\ く官俵呉嫁ー\ - 。"; + 。"); let mut f = ConsoleLineFormatter::new(4); f.wrap_type = WrapType::CharWrap(0); @@ -379,7 +375,7 @@ mod tests { f.set_wrap_width(12); assert_eq!( - f.dimensions(UnicodeSegmentation::graphemes(text, true)), + f.dimensions(RopeGraphemes::new(&text.slice(..))), (10, 12) ); } @@ -387,7 +383,7 @@ mod tests { #[test] fn dimensions_4() { // 55 graphemes long - let text = "税マイミ文末\ + let text = Rope::from_str("税マイミ文末\ レ日題イぽじ\ や男目統ス公\ 身みトしつ結\ @@ -396,7 +392,7 @@ mod tests { 凱字テ式重反\ てす献罪がご\ く官俵呉嫁ー\ - 。"; + 。"); let mut f = ConsoleLineFormatter::new(4); f.wrap_type = WrapType::WordWrap(0); @@ -405,14 +401,14 @@ mod tests { f.set_wrap_width(12); assert_eq!( - f.dimensions(UnicodeSegmentation::graphemes(text, true)), + f.dimensions(RopeGraphemes::new(&text.slice(..))), (10, 12) ); } #[test] fn index_to_v2d_1() { - let text = "Hello there, stranger!"; // 22 graphemes long + let text = Rope::from_str("Hello there, stranger!"); // 22 graphemes long let mut f = ConsoleLineFormatter::new(4); f.wrap_type = WrapType::CharWrap(0); @@ -421,26 +417,26 @@ mod tests { f.set_wrap_width(80); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 0), (0, 0) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 5), (0, 5) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 22), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 22), (0, 22) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 23), (0, 22) ); } #[test] fn index_to_v2d_2() { - let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long + let text = Rope::from_str("Hello there, stranger! How are you doing this fine day?"); // 56 graphemes long let mut f = ConsoleLineFormatter::new(4); f.wrap_type = WrapType::CharWrap(0); @@ -449,79 +445,79 @@ mod tests { f.set_wrap_width(12); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 0), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 0), (0, 0) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 5), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 5), (0, 5) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 11), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 11), (0, 11) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 12), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 12), (1, 0) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 15), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 15), (1, 3) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 23), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 23), (1, 11) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 24), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 24), (2, 0) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 28), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 28), (2, 4) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 35), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 35), (2, 11) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 36), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 36), (3, 0) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 43), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 43), (3, 7) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 47), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 47), (3, 11) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 48), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 48), (4, 0) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 50), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 50), (4, 2) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 56), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 56), (4, 8) ); assert_eq!( - f.index_to_v2d(UnicodeSegmentation::graphemes(text, true), 57), + f.index_to_v2d(RopeGraphemes::new(&text.slice(..)), 57), (4, 8) ); } #[test] fn v2d_to_index_1() { - let text = "Hello there, stranger!"; // 22 graphemes long + let text = Rope::from_str("Hello there, stranger!"); // 22 graphemes long let mut f = ConsoleLineFormatter::new(4); f.wrap_type = WrapType::CharWrap(0); @@ -531,7 +527,7 @@ mod tests { assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (0, 0), (Floor, Floor) ), @@ -539,7 +535,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (0, 5), (Floor, Floor) ), @@ -547,7 +543,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (0, 22), (Floor, Floor) ), @@ -555,7 +551,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (0, 23), (Floor, Floor) ), @@ -563,7 +559,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (1, 0), (Floor, Floor) ), @@ -571,7 +567,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (1, 1), (Floor, Floor) ), @@ -581,7 +577,7 @@ mod tests { #[test] fn v2d_to_index_2() { - let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long + let text = Rope::from_str("Hello there, stranger! How are you doing this fine day?"); // 56 graphemes long let mut f = ConsoleLineFormatter::new(4); f.wrap_type = WrapType::CharWrap(0); @@ -591,7 +587,7 @@ mod tests { assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (0, 0), (Floor, Floor) ), @@ -599,7 +595,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (0, 11), (Floor, Floor) ), @@ -607,7 +603,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (0, 12), (Floor, Floor) ), @@ -616,7 +612,7 @@ mod tests { assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (1, 0), (Floor, Floor) ), @@ -624,7 +620,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (1, 11), (Floor, Floor) ), @@ -632,7 +628,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (1, 12), (Floor, Floor) ), @@ -641,7 +637,7 @@ mod tests { assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (2, 0), (Floor, Floor) ), @@ -649,7 +645,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (2, 11), (Floor, Floor) ), @@ -657,7 +653,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (2, 12), (Floor, Floor) ), @@ -666,7 +662,7 @@ mod tests { assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (3, 0), (Floor, Floor) ), @@ -674,7 +670,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (3, 11), (Floor, Floor) ), @@ -682,7 +678,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (3, 12), (Floor, Floor) ), @@ -691,7 +687,7 @@ mod tests { assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (4, 0), (Floor, Floor) ), @@ -699,7 +695,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (4, 7), (Floor, Floor) ), @@ -707,7 +703,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (4, 8), (Floor, Floor) ), @@ -715,7 +711,7 @@ mod tests { ); assert_eq!( f.v2d_to_index( - UnicodeSegmentation::graphemes(text, true), + RopeGraphemes::new(&text.slice(..)), (4, 9), (Floor, Floor) ), diff --git a/src/term_ui/mod.rs b/src/term_ui/mod.rs index 50f16dd..b885733 100644 --- a/src/term_ui/mod.rs +++ b/src/term_ui/mod.rs @@ -10,8 +10,8 @@ use termion::input::TermRead; use editor::Editor; use formatter::{block_index_and_offset, LineFormatter, LINE_BLOCK_LENGTH}; use self::formatter::ConsoleLineFormatter; -use string_utils::{is_line_ending, line_ending_to_str, LineEnding}; -use utils::digit_count; +use string_utils::{rope_slice_is_line_ending, line_ending_to_str, LineEnding}; +use utils::{digit_count, RopeGraphemes}; pub mod formatter; mod screen; @@ -367,15 +367,14 @@ impl TermUI { .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 + RopeGraphemes::new(&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 - char_index, ); @@ -412,9 +411,9 @@ impl TermUI { let mut last_pos_y = 0; let mut lines_traversed: usize = 0; let line_len = line.len_chars(); - let mut g_iter = editor.formatter.iter(line.slice( + let mut g_iter = editor.formatter.iter(RopeGraphemes::new(&line.slice( (line_block_index * LINE_BLOCK_LENGTH)..line_len, - ).graphemes()); + ))); loop { if let Some((g, (pos_y, pos_x), width)) = g_iter.next() { @@ -444,7 +443,7 @@ impl TermUI { } // Actually print the character - if is_line_ending(g) { + if rope_slice_is_line_ending(&g) { if at_cursor { self.screen.draw( px as usize, @@ -476,17 +475,17 @@ impl TermUI { } } else { if at_cursor { - self.screen.draw( + self.screen.draw_rope_slice( px as usize, py as usize, - g, + &g, Style(Color::Black, Color::White), ); } else { - self.screen.draw( + self.screen.draw_rope_slice( px as usize, py as usize, - g, + &g, Style(Color::White, Color::Black), ); } @@ -503,9 +502,9 @@ impl TermUI { line_block_index += 1; line_g_index = 0; let line_len = line.len_chars(); - g_iter = editor.formatter.iter(line.slice( + g_iter = editor.formatter.iter(RopeGraphemes::new(&line.slice( (line_block_index * LINE_BLOCK_LENGTH)..line_len, - ).graphemes()); + ))); lines_traversed += 1; } } diff --git a/src/term_ui/screen.rs b/src/term_ui/screen.rs index d647f8e..02d1e3f 100644 --- a/src/term_ui/screen.rs +++ b/src/term_ui/screen.rs @@ -3,6 +3,8 @@ use std::cell::RefCell; use std::io; use std::io::{BufWriter, Write}; +use ropey::RopeSlice; +use utils::{RopeGraphemes, grapheme_width}; use super::smallstring::SmallString; use unicode_width::UnicodeWidthStr; use unicode_segmentation::UnicodeSegmentation; @@ -110,6 +112,28 @@ impl Screen { } } + pub(crate) fn draw_rope_slice(&self, x: usize, y: usize, text: &RopeSlice, style: Style) { + if y < self.h { + let mut buf = self.buf.borrow_mut(); + let mut x = x; + for g in RopeGraphemes::new(&text) { + let width = grapheme_width(&g); + if width > 0 { + if x < self.w { + buf[y * self.w + x] = Some((style, SmallString::from_rope_slice(&g))); + } + x += 1; + for _ in 0..(width - 1) { + if x < self.w { + buf[y * self.w + x] = None; + } + x += 1; + } + } + } + } + } + pub(crate) fn hide_cursor(&self) { write!(self.out.borrow_mut(), "{}", termion::cursor::Hide).unwrap(); } diff --git a/src/term_ui/smallstring.rs b/src/term_ui/smallstring.rs index 8665419..8bc9578 100644 --- a/src/term_ui/smallstring.rs +++ b/src/term_ui/smallstring.rs @@ -5,6 +5,8 @@ use std::ops::Deref; use std::ptr; use std::str; +use ropey::RopeSlice; + use smallvec::SmallVec; #[derive(Clone, Default)] @@ -37,6 +39,17 @@ impl SmallString { string } + /// Creates a new `SmallString` with the same contents as the given `&str`. + pub fn from_rope_slice(text: &RopeSlice) -> Self { + let mut string = SmallString::with_capacity(text.len_bytes()); + let mut idx = 0; + for chunk in text.chunks() { + unsafe { string.insert_bytes(idx, chunk.as_bytes()) }; + idx += chunk.len(); + } + string + } + /// Appends a `&str` to end the of the `SmallString`. pub fn push_str(&mut self, string: &str) { let len = self.len(); diff --git a/src/utils.rs b/src/utils.rs index 7f9d9bc..a09bf45 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,7 @@ +use ropey::{RopeSlice, iter::Chunks}; +use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete}; +use unicode_width::UnicodeWidthStr; + pub fn digit_count(mut n: u32, b: u32) -> u32 { let mut d = 0; loop { @@ -9,6 +13,169 @@ pub fn digit_count(mut n: u32, b: u32) -> u32 { } } +//============================================================= + +pub fn grapheme_width(slice: &RopeSlice) -> usize { + // TODO: use a small stack-allocated buffer to handle the common case + // without allocation. + let s = slice.to_string(); + return UnicodeWidthStr::width(&s[..]); +} + +/// Finds the previous grapheme boundary before the given char position. +pub fn prev_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize { + // Bounds check + debug_assert!(char_idx <= slice.len_chars()); + + // We work with bytes for this, so convert. + let byte_idx = slice.char_to_byte(char_idx); + + // Get the chunk with our byte index in it, and calculate its starting + // byte within the total rope slice. + let (mut chunk, byte_offset) = slice.chunk_at_byte(byte_idx); + let mut chunk_start_idx = byte_idx - byte_offset; + + // Set up the grapheme cursor. + let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true); + + // Find the previous grapheme cluster boundary. + loop { + match gc.prev_boundary(chunk, chunk_start_idx) { + Ok(None) => return 0, + Ok(Some(n)) => return slice.byte_to_char(n), + Err(GraphemeIncomplete::PrevChunk) => { + chunk = slice.chunk_at_byte(chunk_start_idx - 1).0; + chunk_start_idx -= chunk.len(); + } + Err(GraphemeIncomplete::PreContext(n)) => { + let (ctx_chunk, _) = slice.chunk_at_byte(n - 1); + gc.provide_context(ctx_chunk, n - ctx_chunk.len()); + } + _ => unreachable!(), + } + } +} + +/// Finds the next grapheme boundary after the given char position. +pub fn next_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize { + // Bounds check + debug_assert!(char_idx <= slice.len_chars()); + + // We work with bytes for this, so convert. + let byte_idx = slice.char_to_byte(char_idx); + + // Get the chunk with our byte index in it, and calculate its starting + // byte within the total rope slice. + let (mut chunk, byte_offset) = slice.chunk_at_byte(byte_idx); + let mut chunk_start_idx = byte_idx - byte_offset; + + // Set up the grapheme cursor. + let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true); + + // Find the next grapheme cluster boundary. + loop { + match gc.next_boundary(chunk, chunk_start_idx) { + Ok(None) => return slice.len_chars(), + Ok(Some(n)) => return slice.byte_to_char(n), + Err(GraphemeIncomplete::NextChunk) => { + chunk_start_idx += chunk.len(); + chunk = slice.chunk_at_byte(chunk_start_idx).0; + } + Err(GraphemeIncomplete::PreContext(n)) => { + let (ctx_chunk, _) = slice.chunk_at_byte(n - 1); + gc.provide_context(ctx_chunk, n - ctx_chunk.len()); + } + _ => unreachable!(), + } + } +} + +/// Returns whether the given char position is a grapheme boundary. +pub fn is_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> bool { + // Bounds check + debug_assert!(char_idx <= slice.len_chars()); + + // We work with bytes for this, so convert. + let byte_idx = slice.char_to_byte(char_idx); + + // Get the chunk with our byte index in it, and calculate its starting + // byte within the total rope slice. + let (chunk, byte_offset) = slice.chunk_at_byte(byte_idx); + let chunk_start_idx = byte_idx - byte_offset; + + // Set up the grapheme cursor. + let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true); + + // Determine if the given position is a grapheme cluster boundary. + loop { + match gc.is_boundary(chunk, chunk_start_idx) { + Ok(n) => return n, + Err(GraphemeIncomplete::PreContext(n)) => { + let (ctx_chunk, _) = slice.chunk_at_byte(n - 1); + gc.provide_context(ctx_chunk, n - ctx_chunk.len()); + } + _ => unreachable!(), + } + } +} + +/// An iterator over the graphemes of a RopeSlice. +pub struct RopeGraphemes<'a> { + text: RopeSlice<'a>, + chunks: Chunks<'a>, + cur_chunk: &'a str, + cur_chunk_start: usize, + cursor: GraphemeCursor, +} + +impl<'a> RopeGraphemes<'a> { + pub fn new<'b>(slice: &RopeSlice<'b>) -> RopeGraphemes<'b> { + let mut chunks = slice.chunks(); + let first_chunk = chunks.next().unwrap_or(""); + RopeGraphemes { + text: *slice, + chunks: chunks, + cur_chunk: first_chunk, + cur_chunk_start: 0, + cursor: GraphemeCursor::new(0, slice.len_bytes(), true), + } + } +} + +impl<'a> Iterator for RopeGraphemes<'a> { + type Item = RopeSlice<'a>; + + fn next(&mut self) -> Option> { + let a = self.cursor.cur_cursor(); + let b; + loop { + match self.cursor + .next_boundary(self.cur_chunk, self.cur_chunk_start) + { + Ok(None) => { + return None; + } + Ok(Some(n)) => { + b = n; + break; + } + Err(GraphemeIncomplete::NextChunk) => { + self.cur_chunk_start += self.cur_chunk.len(); + self.cur_chunk = self.chunks.next().unwrap_or(""); + } + _ => unreachable!(), + } + } + + let a_char = self.text.byte_to_char(a); + let b_char = self.text.byte_to_char(b); + + Some(self.text.slice(a_char..b_char)) + } +} + +//============================================================= + #[cfg(test)] mod tests { use super::*;