From 1b3acfd29f7f2f5a5366bc85dbe1ed0d45d5b58b Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sun, 22 Feb 2015 19:30:13 -0800 Subject: [PATCH] Fixed bug in LineFormatter::index_offset_vertical_v2d() The bug was causing very incorrect behavior when a single line was long enough to be split into multiple blocks. --- src/buffer/rope.rs | 35 +++++++++++++++++++++++++++++++++++ src/formatter.rs | 30 ++++++++++++++++++++++-------- src/term_ui/formatter.rs | 2 +- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/buffer/rope.rs b/src/buffer/rope.rs index 5f53c33..0e11f1a 100644 --- a/src/buffer/rope.rs +++ b/src/buffer/rope.rs @@ -1399,6 +1399,41 @@ mod tests { assert!(None == iter.next()); } + + #[test] + fn slice_8() { + let rope = Rope::new_from_str("Hello everyone! How are you doing, eh?"); + let s = rope.slice(15, 39); + + let mut iter = s.grapheme_iter_between_indices(0, 25); + + assert!(Some(" ") == iter.next()); + assert!(Some(" ") == iter.next()); + assert!(Some("H") == iter.next()); + assert!(Some("o") == iter.next()); + assert!(Some("w") == iter.next()); + assert!(Some(" ") == iter.next()); + assert!(Some("a") == iter.next()); + assert!(Some("r") == iter.next()); + assert!(Some("e") == iter.next()); + assert!(Some(" ") == iter.next()); + assert!(Some("y") == iter.next()); + assert!(Some("o") == iter.next()); + assert!(Some("u") == iter.next()); + assert!(Some(" ") == iter.next()); + assert!(Some("d") == iter.next()); + assert!(Some("o") == iter.next()); + assert!(Some("i") == iter.next()); + assert!(Some("n") == iter.next()); + assert!(Some("g") == iter.next()); + assert!(Some(",") == iter.next()); + assert!(Some(" ") == iter.next()); + assert!(Some("e") == iter.next()); + assert!(Some("h") == iter.next()); + assert!(Some("?") == iter.next()); + assert!(None == iter.next()); + } + #[test] fn line_index_to_grapheme_index_1() { diff --git a/src/formatter.rs b/src/formatter.rs index b588803..fd37549 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -65,14 +65,14 @@ pub trait LineFormatter { let (line_block, col_i_adjusted) = block_index_and_offset(col_i); let (mut y, x) = self.index_to_v2d(buf.get_line(line_i).grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, (line_block+1) * LINE_BLOCK_LENGTH), col_i_adjusted); - let mut new_y = y as isize + offset; // First, find the right line while keeping track of the vertical offset + let mut new_y = y as isize + offset; let mut line; let mut block_index: usize = line_block; loop { line = buf.get_line(line_i); - let (h, _) = self.dimensions(line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, (line_block+1) * LINE_BLOCK_LENGTH)); + let (h, _) = self.dimensions(line.grapheme_iter_between_indices(block_index * LINE_BLOCK_LENGTH, (block_index+1) * LINE_BLOCK_LENGTH)); if new_y >= 0 && new_y < h as isize { y = new_y as usize; @@ -80,14 +80,14 @@ pub trait LineFormatter { } else { if new_y > 0 { - let last_block = block_index >= (line.grapheme_count() / LINE_BLOCK_LENGTH); + let is_last_block = block_index >= last_block_index(line.grapheme_count()); // Check for off-the-end - if last_block && (line_i + 1) >= buf.line_count() { + if is_last_block && (line_i + 1) >= buf.line_count() { return buf.grapheme_count(); } - if last_block { + if is_last_block { line_i += 1; block_index = 0; } @@ -105,12 +105,12 @@ pub trait LineFormatter { if block_index == 0 { line_i -= 1; line = buf.get_line(line_i); - block_index = line.grapheme_count() / LINE_BLOCK_LENGTH; + block_index = last_block_index(line.grapheme_count()); } else { block_index -= 1; } - let (h, _) = self.dimensions(line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, (line_block+1) * LINE_BLOCK_LENGTH)); + let (h, _) = self.dimensions(line.grapheme_iter_between_indices(block_index * LINE_BLOCK_LENGTH, (block_index+1) * LINE_BLOCK_LENGTH)); new_y += h as isize; } else { @@ -121,7 +121,7 @@ pub trait LineFormatter { // Next, convert the resulting coordinates back into buffer-wide // coordinates. - col_i = (block_index * LINE_BLOCK_LENGTH) + self.v2d_to_index(line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, (line_block+1) * LINE_BLOCK_LENGTH), (y, x), rounding); + col_i = (block_index * LINE_BLOCK_LENGTH) + self.v2d_to_index(line.grapheme_iter_between_indices(block_index * LINE_BLOCK_LENGTH, (block_index+1) * LINE_BLOCK_LENGTH), (y, x), rounding); return buf.line_col_to_index((line_i, col_i)); } @@ -158,3 +158,17 @@ pub trait LineFormatter { pub fn block_index_and_offset(index: usize) -> (usize, usize) { (index / LINE_BLOCK_LENGTH, index % LINE_BLOCK_LENGTH) } + +pub fn last_block_index(gc: usize) -> usize { + let mut block_count = gc / LINE_BLOCK_LENGTH; + if (gc % LINE_BLOCK_LENGTH) > 0 { + block_count += 1; + } + + if block_count > 0 { + return block_count - 1; + } + else { + return 0; + } +} diff --git a/src/term_ui/formatter.rs b/src/term_ui/formatter.rs index d7e84d5..c14a7e1 100644 --- a/src/term_ui/formatter.rs +++ b/src/term_ui/formatter.rs @@ -170,7 +170,7 @@ fn grapheme_vis_width_at_vis_pos(g: &str, pos: usize, tab_width: usize) -> usize mod tests { #![allow(unused_imports)] use super::*; - use formatter::LineFormatter; + use formatter::{LineFormatter, LINE_BLOCK_LENGTH}; use formatter::RoundingBehavior::{Round, Floor, Ceiling}; use buffer::Buffer;