Added Rope methods for converting between grapheme indices and line/column indices.

This commit is contained in:
Nathan Vegdahl 2015-02-22 00:15:18 -08:00
parent 759b23db4d
commit 0ee364f6ce

View File

@ -11,7 +11,7 @@ use string_utils::{
is_line_ending 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; 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. /// Inserts the given text at the given grapheme index.
/// For small lengths of 'text' runs in O(log N) time. /// For small lengths of 'text' runs in O(log N) time.
/// For large lengths of 'text', dunno. But it seems to perform /// 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] #[test]
fn to_string() { fn to_string() {
let rope = Rope::new_from_str("Hello there good people of the world!"); let rope = Rope::new_from_str("Hello there good people of the world!");