diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 2852d4a..4e64e2a 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -205,33 +205,55 @@ impl Buffer { // Position conversions //------------------------------------------------------------------------ - pub fn pos_2d_to_closest_1d(&self, pos: (usize, usize)) -> usize { - return self.text.pos_2d_to_closest_1d_recursive(pos); - } - - - pub fn pos_vis_2d_to_closest_1d(&self, pos: (usize, usize), tab_width: usize) -> usize { - if pos.0 >= self.line_count() { - return self.grapheme_count(); - } - else { - let gs = self.pos_2d_to_closest_1d((pos.0, 0)); - let h = self.get_line(pos.0).vis_pos_to_closest_grapheme_index(pos.1, tab_width); - return gs + h; - } - } - - - pub fn pos_1d_to_closest_2d(&self, pos: usize) -> (usize, usize) { + /// 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 index_to_line_col(&self, pos: usize) -> (usize, usize) { return self.text.pos_1d_to_closest_2d_recursive(pos); } - pub fn pos_1d_to_closest_vis_2d(&self, pos: usize, tab_width: usize) -> (usize, usize) { + /// 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_index(&self, pos: (usize, usize)) -> usize { + return self.text.pos_2d_to_closest_1d_recursive(pos); + } + + + /// Converts a grapheme index into a visual line and column number. + /// + /// If the index is off the end of the text, returns the visual line and + /// column number of the last valid text position. + pub fn index_to_v2d(&self, pos: usize, tab_width: usize) -> (usize, usize) { let (v, h) = self.text.pos_1d_to_closest_2d_recursive(pos); let vis_h = self.get_line(v).grapheme_index_to_closest_vis_pos(h, tab_width); return (v, vis_h); } + + + /// Converts a visual line and column number into a grapheme index. + /// + /// If the visual column number given is outside of the text, returns the + /// index of the horizontally-closest valid position. If the visual line + /// number given is beyond the end of the buffer, returns the index of + /// the buffer's last valid position. + pub fn v2d_to_index(&self, pos: (usize, usize), tab_width: usize) -> usize { + if pos.0 >= self.line_count() { + return self.grapheme_count(); + } + else { + let gs = self.line_col_to_index((pos.0, 0)); + let h = self.get_line(pos.0).vis_pos_to_closest_grapheme_index(pos.1, tab_width); + return gs + h; + } + } @@ -1122,54 +1144,54 @@ mod tests { #[test] - fn pos_2d_to_closest_1d_1() { + fn line_col_to_index_1() { let mut buf = Buffer::new(); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); - let pos = buf.pos_2d_to_closest_1d((2, 3)); + let pos = buf.line_col_to_index((2, 3)); assert!(pos == 12); } #[test] - fn pos_2d_to_closest_1d_2() { + fn line_col_to_index_2() { let mut buf = Buffer::new(); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); - let pos = buf.pos_2d_to_closest_1d((2, 10)); + let pos = buf.line_col_to_index((2, 10)); assert!(pos == 15); } #[test] - fn pos_2d_to_closest_1d_3() { + fn line_col_to_index_3() { let mut buf = Buffer::new(); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); - let pos = buf.pos_2d_to_closest_1d((10, 2)); + let pos = buf.line_col_to_index((10, 2)); assert!(pos == 29); } #[test] - fn pos_1d_to_closest_2d_1() { + fn index_to_line_col_1() { let mut buf = Buffer::new(); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); - let pos = buf.pos_1d_to_closest_2d(5); + let pos = buf.index_to_line_col(5); assert!(pos == (1, 2)); } #[test] - fn pos_1d_to_closest_2d_2() { + fn index_to_line_col_2() { let mut buf = Buffer::new(); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); - let pos = buf.pos_1d_to_closest_2d(50); + let pos = buf.index_to_line_col(50); assert!(pos == (5, 6)); } diff --git a/src/editor.rs b/src/editor.rs index 651d5d4..05b2451 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -27,7 +27,7 @@ impl Cursor { } pub fn update_vis_start(&mut self, buf: &Buffer, tab_width: usize) { - let (v, h) = buf.pos_1d_to_closest_2d(self.range.0); + let (v, h) = buf.index_to_line_col(self.range.0); self.vis_start = buf.get_line(v).grapheme_index_to_closest_vis_pos(h, tab_width); } } @@ -213,7 +213,7 @@ impl Editor { /// Moves the editor's view the minimum amount to show the cursor pub fn move_view_to_cursor(&mut self) { - let (v, h) = self.buffer.pos_1d_to_closest_vis_2d(self.cursor.range.0, self.tab_width); + let (v, h) = self.buffer.index_to_v2d(self.cursor.range.0, self.tab_width); // Horizontal if h < self.view_pos.1 { @@ -251,7 +251,7 @@ impl Editor { pub fn insert_tab_at_cursor(&mut self) { if self.soft_tabs { // Figure out how many spaces to insert - let (v, h) = self.buffer.pos_1d_to_closest_2d(self.cursor.range.0); + let (v, h) = self.buffer.index_to_line_col(self.cursor.range.0); let vis_pos = self.buffer.get_line(v).grapheme_index_to_closest_vis_pos(h, self.tab_width); let next_tab_stop = ((vis_pos / self.tab_width) + 1) * self.tab_width; let space_count = min(next_tab_stop - vis_pos, 8); @@ -381,10 +381,10 @@ impl Editor { } pub fn cursor_up(&mut self, n: usize) { - let (v, _) = self.buffer.pos_1d_to_closest_vis_2d(self.cursor.range.0, self.tab_width); + let (v, _) = self.buffer.index_to_v2d(self.cursor.range.0, self.tab_width); if v >= n { - self.cursor.range.0 = self.buffer.pos_vis_2d_to_closest_1d((v - n, self.cursor.vis_start), self.tab_width); + self.cursor.range.0 = self.buffer.v2d_to_index((v - n, self.cursor.vis_start), self.tab_width); self.cursor.range.1 = self.cursor.range.0; } else { @@ -396,10 +396,10 @@ impl Editor { } pub fn cursor_down(&mut self, n: usize) { - let (v, _) = self.buffer.pos_1d_to_closest_vis_2d(self.cursor.range.0, self.tab_width); + let (v, _) = self.buffer.index_to_v2d(self.cursor.range.0, self.tab_width); if v < (self.buffer.line_count() - n) { - self.cursor.range.0 = self.buffer.pos_vis_2d_to_closest_1d((v + n, self.cursor.vis_start), self.tab_width); + self.cursor.range.0 = self.buffer.v2d_to_index((v + n, self.cursor.vis_start), self.tab_width); self.cursor.range.1 = self.cursor.range.0; } else { @@ -456,9 +456,9 @@ impl Editor { } pub fn jump_to_line(&mut self, n: usize) { - let pos = self.buffer.pos_2d_to_closest_1d((n, 0)); - let (v, _) = self.buffer.pos_1d_to_closest_vis_2d(pos, self.tab_width); - self.cursor.range.0 = self.buffer.pos_vis_2d_to_closest_1d((v, self.cursor.vis_start), self.tab_width); + let pos = self.buffer.line_col_to_index((n, 0)); + let (v, _) = self.buffer.index_to_v2d(pos, self.tab_width); + self.cursor.range.0 = self.buffer.v2d_to_index((v, self.cursor.vis_start), self.tab_width); self.cursor.range.1 = self.cursor.range.0; // Adjust view diff --git a/src/term_ui.rs b/src/term_ui.rs index f1b49f3..7772954 100644 --- a/src/term_ui.rs +++ b/src/term_ui.rs @@ -353,7 +353,7 @@ impl TermUI { vis_col_num += excess; print_col_num += excess; - grapheme_index = editor.buffer.pos_vis_2d_to_closest_1d((vis_line_num, vis_col_num), editor.tab_width); + grapheme_index = editor.buffer.v2d_to_index((vis_line_num, vis_col_num), editor.tab_width); for (g, pos, width) in g_iter { print_col_num = pos - editor.view_pos.1; @@ -406,7 +406,7 @@ impl TermUI { // Print cursor if it's at the end of the text, and thus wasn't printed // already. if editor.cursor.range.0 >= editor.buffer.grapheme_count() { - let vis_cursor_pos = editor.buffer.pos_1d_to_closest_vis_2d(editor.cursor.range.0, editor.tab_width); + let vis_cursor_pos = editor.buffer.index_to_v2d(editor.cursor.range.0, editor.tab_width); if (vis_cursor_pos.0 >= editor.view_pos.0) && (vis_cursor_pos.1 >= editor.view_pos.1) { let print_cursor_pos = (vis_cursor_pos.0 - editor.view_pos.0 + c1.0, vis_cursor_pos.1 - editor.view_pos.1 + c1.1);