From 0ee364f6ce14fd40c67b14748c85b1b5299053c4 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sun, 22 Feb 2015 00:15:18 -0800 Subject: [PATCH] Added Rope methods for converting between grapheme indices and line/column indices. --- src/buffer/rope.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/buffer/rope.rs b/src/buffer/rope.rs index 7bfdfa3..cf70698 100644 --- a/src/buffer/rope.rs +++ b/src/buffer/rope.rs @@ -11,7 +11,7 @@ use string_utils::{ is_line_ending }; -pub const MIN_NODE_SIZE: usize = 1; +pub const MIN_NODE_SIZE: usize = 64; pub const MAX_NODE_SIZE: usize = MIN_NODE_SIZE * 2; @@ -220,6 +220,45 @@ impl Rope { } + /// Converts a grapheme index into a line number and grapheme-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 grapheme_index_to_line_col(&self, pos: usize) -> (usize, usize) { + let p = min(pos, self.grapheme_count_); + let line = self.grapheme_index_to_line_index(p); + let line_pos = self.line_index_to_grapheme_index(line); + return (line, p - line_pos); + } + + + /// Converts a line number and grapheme-column number into a grapheme + /// index. + /// + /// If the column number given is beyond the end of the line, returns the + /// index of the line's last valid position. If the line number given is + /// beyond the end of the buffer, returns the index of the buffer's last + /// valid position. + pub fn line_col_to_grapheme_index(&self, pos: (usize, usize)) -> usize { + if pos.0 <= self.line_ending_count { + let l_begin_pos = self.line_index_to_grapheme_index(pos.0); + + let l_end_pos = if pos.0 < self.line_ending_count { + self.line_index_to_grapheme_index(pos.0 + 1) - 1 + } + else { + self.grapheme_count_ + }; + + return min(l_begin_pos + pos.1, l_end_pos); + } + else { + return self.grapheme_count_; + } + } + + /// Inserts the given text at the given grapheme index. /// For small lengths of 'text' runs in O(log N) time. /// For large lengths of 'text', dunno. But it seems to perform @@ -1070,6 +1109,36 @@ mod tests { } + #[test] + fn grapheme_index_to_line_col_1() { + let rope = Rope::new_from_str("Hello\nworld!\n"); + + assert_eq!(rope.grapheme_index_to_line_col(0), (0,0)); + assert_eq!(rope.grapheme_index_to_line_col(5), (0,5)); + assert_eq!(rope.grapheme_index_to_line_col(6), (1,0)); + assert_eq!(rope.grapheme_index_to_line_col(12), (1,6)); + assert_eq!(rope.grapheme_index_to_line_col(13), (2,0)); + assert_eq!(rope.grapheme_index_to_line_col(14), (2,0)); + } + + + #[test] + fn line_col_to_grapheme_index_1() { + let rope = Rope::new_from_str("Hello\nworld!\n"); + + assert_eq!(rope.line_col_to_grapheme_index((0,0)), 0); + assert_eq!(rope.line_col_to_grapheme_index((0,5)), 5); + assert_eq!(rope.line_col_to_grapheme_index((0,6)), 5); + + assert_eq!(rope.line_col_to_grapheme_index((1,0)), 6); + assert_eq!(rope.line_col_to_grapheme_index((1,6)), 12); + assert_eq!(rope.line_col_to_grapheme_index((1,7)), 12); + + assert_eq!(rope.line_col_to_grapheme_index((2,0)), 13); + assert_eq!(rope.line_col_to_grapheme_index((2,1)), 13); + } + + #[test] fn to_string() { let rope = Rope::new_from_str("Hello there good people of the world!");