diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 6d73726..72d2979 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -69,9 +69,20 @@ impl Buffer { self.text.len_chars() } - pub fn grapheme_count(&self) -> usize { - // TODO: be correct - self.text.len_chars() + 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 + } + + 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 } pub fn line_count(&self) -> usize { diff --git a/src/editor/mod.rs b/src/editor/mod.rs index a84b36d..12f8dfd 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -5,7 +5,7 @@ use formatter::LineFormatter; use formatter::RoundingBehavior::*; use std::path::{Path, PathBuf}; use std::cmp::{max, min}; -use string_utils::{grapheme_count, str_to_line_ending, LineEnding}; +use string_utils::{char_count, str_to_line_ending, LineEnding}; use utils::digit_count; use self::cursor::CursorSet; @@ -26,7 +26,7 @@ pub struct Editor { // The dimensions and position of just the text view portion of the editor pub view_dim: (usize, usize), // (height, width) - pub view_pos: (usize, usize), // (grapheme index, visual horizontal offset) + pub view_pos: (usize, usize), // (char index, visual horizontal offset) // The editing cursor position pub cursors: CursorSet, @@ -324,24 +324,24 @@ impl Editor { // there are no cursors currently in view, and should jump to // the closest cursor. - // Find the first and last grapheme index visible within the editor. - let g_first = + // Find the first and last char index visible within the editor. + let c_first = self.formatter .index_set_horizontal_v2d(&self.buffer, self.view_pos.0, 0, Floor); - let mut g_last = self.formatter.index_offset_vertical_v2d( + let mut c_last = self.formatter.index_offset_vertical_v2d( &self.buffer, - g_first, + c_first, self.view_dim.0 as isize, (Floor, Floor), ); - g_last = + c_last = self.formatter - .index_set_horizontal_v2d(&self.buffer, g_last, self.view_dim.1, Floor); + .index_set_horizontal_v2d(&self.buffer, c_last, self.view_dim.1, Floor); // Adjust the view depending on where the cursor is - if self.cursors[0].range.0 < g_first { + if self.cursors[0].range.0 < c_first { self.view_pos.0 = self.cursors[0].range.0; - } else if self.cursors[0].range.0 > g_last { + } else if self.cursors[0].range.0 >= c_last { self.view_pos.0 = self.formatter.index_offset_vertical_v2d( &self.buffer, self.cursors[0].range.0, @@ -354,7 +354,7 @@ impl Editor { pub fn insert_text_at_cursor(&mut self, text: &str) { self.cursors.make_consistent(); - let str_len = grapheme_count(text); + let str_len = char_count(text); let mut offset = 0; for c in self.cursors.iter_mut() { @@ -421,13 +421,6 @@ impl Editor { self.remove_text_behind_cursor(1); } - pub fn insert_text_at_grapheme(&mut self, text: &str, pos: usize) { - self.dirty = true; - let buf_len = self.buffer.grapheme_count(); - self.buffer - .insert_text(text, if pos < buf_len { pos } else { buf_len }); - } - pub fn remove_text_behind_cursor(&mut self, grapheme_count: usize) { self.cursors.make_consistent(); @@ -443,7 +436,7 @@ impl Editor { continue; } - let len = min(c.range.0, grapheme_count); + let len = c.range.0 - self.buffer.nth_prev_grapheme(c.range.0, grapheme_count); // Remove text self.buffer.remove_text_before(c.range.0, len); @@ -475,16 +468,11 @@ impl Editor { c.range.1 -= min(c.range.1, offset); // Do nothing if there's nothing to delete. - if c.range.1 == self.buffer.grapheme_count() { + if c.range.1 == self.buffer.char_count() { return; } - let max_len = if self.buffer.grapheme_count() > c.range.1 { - self.buffer.grapheme_count() - c.range.1 - } else { - 0 - }; - let len = min(max_len, grapheme_count); + let len = self.buffer.nth_next_grapheme(c.range.1, grapheme_count) - c.range.1; // Remove text self.buffer.remove_text_after(c.range.1, len); @@ -548,7 +536,7 @@ impl Editor { } pub fn cursor_to_end_of_buffer(&mut self) { - let end = self.buffer.grapheme_count(); + let end = self.buffer.char_count(); self.cursors = CursorSet::new(); self.cursors[0].range = (end, end); @@ -560,12 +548,7 @@ impl Editor { pub fn cursor_left(&mut self, n: usize) { for c in self.cursors.iter_mut() { - if c.range.0 >= n { - c.range.0 -= n; - } else { - c.range.0 = 0; - } - + c.range.0 = self.buffer.nth_prev_grapheme(c.range.0, n); c.range.1 = c.range.0; c.update_vis_start(&(self.buffer), &(self.formatter)); } @@ -576,12 +559,7 @@ impl Editor { pub fn cursor_right(&mut self, n: usize) { for c in self.cursors.iter_mut() { - c.range.1 += n; - - if c.range.1 > self.buffer.grapheme_count() { - c.range.1 = self.buffer.grapheme_count(); - } - + c.range.1 = self.buffer.nth_next_grapheme(c.range.1, n); c.range.0 = c.range.1; c.update_vis_start(&(self.buffer), &(self.formatter)); } @@ -629,7 +607,7 @@ impl Editor { (Round, Round), ); - if temp_index == self.buffer.grapheme_count() { + if temp_index == self.buffer.char_count() { c.update_vis_start(&(self.buffer), &(self.formatter)); } else { temp_index = self.formatter.index_set_horizontal_v2d( diff --git a/src/formatter.rs b/src/formatter.rs index 6f7eb9e..636aed3 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -26,13 +26,13 @@ pub trait LineFormatter { where T: Iterator; - /// Converts a grapheme index within a text into a visual 2d position. + /// 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, index: usize) -> (usize, usize) + fn index_to_v2d<'a, T>(&'a self, g_iter: T, char_idx: usize) -> (usize, usize) where T: Iterator; - /// Converts a visual 2d position into a grapheme index within a text. + /// Converts a visual 2d position into a char index within a text. /// The text to be formatted is passed as a grapheme iterator. fn v2d_to_index<'a, T>( &'a self, @@ -43,8 +43,8 @@ pub trait LineFormatter { where T: Iterator; - fn index_to_horizontal_v2d(&self, buf: &Buffer, index: usize) -> usize { - let (line_i, col_i) = buf.index_to_line_col(index); + fn index_to_horizontal_v2d(&self, buf: &Buffer, char_idx: usize) -> usize { + let (line_i, col_i) = buf.index_to_line_col(char_idx); let line = buf.get_line(line_i); // Find the right block in the line, and the index within that block @@ -57,12 +57,12 @@ pub trait LineFormatter { return self.index_to_v2d(g_iter, col_i_adjusted).1; } - /// Takes a grapheme index and a visual vertical offset, and returns the grapheme + /// Takes a char index and a visual vertical offset, and returns the char /// index after that visual offset is applied. fn index_offset_vertical_v2d( &self, buf: &Buffer, - index: usize, + char_idx: usize, offset: isize, rounding: (RoundingBehavior, RoundingBehavior), ) -> usize { @@ -70,7 +70,7 @@ pub trait LineFormatter { // TODO: do this with bidirectional line iterator // Get the line and block index of the given index - let (mut line_i, mut col_i) = buf.index_to_line_col(index); + let (mut line_i, mut col_i) = buf.index_to_line_col(char_idx); // Find the right block in the line, and the index within that block let (line_block, col_i_adjusted) = block_index_and_offset(col_i); @@ -104,7 +104,7 @@ pub trait LineFormatter { // Check for off-the-end if is_last_block && (line_i + 1) >= buf.line_count() { - return buf.grapheme_count(); + return buf.char_count(); } if is_last_block { @@ -153,17 +153,17 @@ pub trait LineFormatter { return buf.line_col_to_index((line_i, col_i)); } - /// Takes a grapheme index and a desired visual horizontal position, and - /// returns a grapheme index on the same visual line as the given index, + /// Takes a char index and a desired visual horizontal position, and + /// returns a char index on the same visual line as the given index, /// but offset to have the desired horizontal position. fn index_set_horizontal_v2d( &self, buf: &Buffer, - index: usize, + char_idx: usize, horizontal: usize, rounding: RoundingBehavior, ) -> usize { - let (line_i, col_i) = buf.index_to_line_col(index); + let (line_i, col_i) = buf.index_to_line_col(char_idx); let line = buf.get_line(line_i); // Find the right block in the line, and the index within that block @@ -189,7 +189,7 @@ pub trait LineFormatter { new_col_i = line.len_chars() - 1; } - return (index + new_col_i) - col_i; + return (char_idx + new_col_i) - col_i; } } diff --git a/src/term_ui/mod.rs b/src/term_ui/mod.rs index b25c3b3..74fc70d 100644 --- a/src/term_ui/mod.rs +++ b/src/term_ui/mod.rs @@ -345,8 +345,8 @@ impl TermUI { // Percentage position in document // TODO: use view instead of cursor for calculation if there is more // than one cursor. - let percentage: usize = if editor.buffer.grapheme_count() > 0 { - (((editor.cursors[0].range.0 as f32) / (editor.buffer.grapheme_count() as f32)) * 100.0) + let percentage: usize = if editor.buffer.char_count() > 0 { + (((editor.cursors[0].range.0 as f32) / (editor.buffer.char_count() as f32)) * 100.0) as usize } else { 100 @@ -584,7 +584,7 @@ impl TermUI { // Calculate the cell coordinates at which to draw the cursor let pos_x = editor .formatter - .index_to_horizontal_v2d(&self.editor.buffer, self.editor.buffer.grapheme_count()); + .index_to_horizontal_v2d(&self.editor.buffer, self.editor.buffer.char_count()); let px = pos_x as isize + screen_col - editor.view_pos.1 as isize; let py = screen_line - 1;