From 9c462ee332e7645c086a20afe2118d5a04dc08a1 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sun, 22 Feb 2015 18:13:10 -0800 Subject: [PATCH] Finished converting codebase back to plain Rope's. Everything appears to be working identically to how it was before converting back. --- src/buffer/mod.rs | 9 ++ src/formatter.rs | 13 +- src/term_ui/formatter.rs | 279 ++++++++++++++++++++++++++++++++++++++- todo.md | 5 - 4 files changed, 295 insertions(+), 11 deletions(-) diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 21a19a7..3f89050 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -37,6 +37,15 @@ impl Buffer { } + pub fn new_from_str(s: &str) -> Buffer { + Buffer { + text: Rope::new_from_str(s), + file_path: None, + undo_stack: UndoStack::new(), + } + } + + pub fn new_from_file(path: &Path) -> IoResult { let mut f = BufferedReader::new(try!(File::open(path))); let string = f.read_to_string().unwrap(); diff --git a/src/formatter.rs b/src/formatter.rs index 3766d26..b588803 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -47,7 +47,7 @@ pub trait LineFormatter { let (line_block, col_i_adjusted) = block_index_and_offset(col_i); // Get an iter into the right block - let g_iter = line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, line_block * (LINE_BLOCK_LENGTH+1)); + let g_iter = line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, (line_block+1) * LINE_BLOCK_LENGTH); return self.index_to_v2d(g_iter, col_i_adjusted).1; } @@ -64,7 +64,7 @@ pub trait LineFormatter { // 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); - let (mut y, x) = self.index_to_v2d(buf.get_line(line_i).grapheme_iter_between_indices(line_block*LINE_BLOCK_LENGTH, line_block*(LINE_BLOCK_LENGTH+1)), col_i_adjusted); + 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 @@ -72,7 +72,7 @@ pub trait LineFormatter { 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*(LINE_BLOCK_LENGTH+1))); + let (h, _) = self.dimensions(line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, (line_block+1) * LINE_BLOCK_LENGTH)); if new_y >= 0 && new_y < h as isize { y = new_y as usize; @@ -110,7 +110,7 @@ pub trait LineFormatter { else { block_index -= 1; } - let (h, _) = self.dimensions(line.grapheme_iter_between_indices(line_block*LINE_BLOCK_LENGTH, line_block*(LINE_BLOCK_LENGTH+1))); + let (h, _) = self.dimensions(line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, (line_block+1) * LINE_BLOCK_LENGTH)); new_y += h as isize; } else { @@ -121,12 +121,15 @@ 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*(LINE_BLOCK_LENGTH+1)), (y, x), rounding); + 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); 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, + /// but offset to have the desired horizontal position. fn index_set_horizontal_v2d(&self, buf: &Buffer, index: usize, horizontal: usize, rounding: RoundingBehavior) -> usize { let (line_i, col_i) = buf.index_to_line_col(index); let line = buf.get_line(line_i); diff --git a/src/term_ui/formatter.rs b/src/term_ui/formatter.rs index 1566d7a..d7e84d5 100644 --- a/src/term_ui/formatter.rs +++ b/src/term_ui/formatter.rs @@ -80,9 +80,10 @@ impl LineFormatter for ConsoleLineFormatter { { // TODO: handle rounding modes let mut i = 0; - + for (_, pos, _) in self.iter(g_iter) { if pos.0 > v2d.0 { + i -= 1; break; } else if pos.0 == v2d.0 && pos.1 >= v2d.1 { @@ -162,3 +163,279 @@ fn grapheme_vis_width_at_vis_pos(g: &str, pos: usize, tab_width: usize) -> usize } } } + + + +#[cfg(test)] +mod tests { + #![allow(unused_imports)] + use super::*; + use formatter::LineFormatter; + use formatter::RoundingBehavior::{Round, Floor, Ceiling}; + use buffer::Buffer; + + + #[test] + fn dimensions_1() { + let text = "Hello there, stranger!"; // 22 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 80; + + assert_eq!(f.dimensions(text.graphemes(true)), (1, 22)); + } + + + #[test] + fn dimensions_2() { + let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 12; + + assert_eq!(f.dimensions(text.graphemes(true)), (5, 12)); + } + + + #[test] + fn index_to_v2d_1() { + let text = "Hello there, stranger!"; // 22 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 80; + + assert_eq!(f.index_to_v2d(text.graphemes(true), 0), (0, 0)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 5), (0, 5)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 22), (0, 22)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 23), (0, 22)); + } + + + #[test] + fn index_to_v2d_2() { + let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 12; + + assert_eq!(f.index_to_v2d(text.graphemes(true), 0), (0, 0)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 5), (0, 5)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 11), (0, 11)); + + assert_eq!(f.index_to_v2d(text.graphemes(true), 12), (1, 0)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 15), (1, 3)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 23), (1, 11)); + + assert_eq!(f.index_to_v2d(text.graphemes(true), 24), (2, 0)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 28), (2, 4)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 35), (2, 11)); + + assert_eq!(f.index_to_v2d(text.graphemes(true), 36), (3, 0)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 43), (3, 7)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 47), (3, 11)); + + assert_eq!(f.index_to_v2d(text.graphemes(true), 48), (4, 0)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 50), (4, 2)); + assert_eq!(f.index_to_v2d(text.graphemes(true), 56), (4, 8)); + + assert_eq!(f.index_to_v2d(text.graphemes(true), 57), (4, 8)); + } + + + #[test] + fn v2d_to_index_1() { + let text = "Hello there, stranger!"; // 22 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 80; + + assert_eq!(f.v2d_to_index(text.graphemes(true), (0,0), (Floor, Floor)), 0); + assert_eq!(f.v2d_to_index(text.graphemes(true), (0,5), (Floor, Floor)), 5); + assert_eq!(f.v2d_to_index(text.graphemes(true), (0,22), (Floor, Floor)), 22); + assert_eq!(f.v2d_to_index(text.graphemes(true), (0,23), (Floor, Floor)), 22); + assert_eq!(f.v2d_to_index(text.graphemes(true), (1,0), (Floor, Floor)), 22); + assert_eq!(f.v2d_to_index(text.graphemes(true), (1,1), (Floor, Floor)), 22); + } + + + #[test] + fn v2d_to_index_2() { + let text = "Hello there, stranger! How are you doing this fine day?"; // 56 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 12; + + assert_eq!(f.v2d_to_index(text.graphemes(true), (0,0), (Floor, Floor)), 0); + assert_eq!(f.v2d_to_index(text.graphemes(true), (0,11), (Floor, Floor)), 11); + assert_eq!(f.v2d_to_index(text.graphemes(true), (0,12), (Floor, Floor)), 11); + + assert_eq!(f.v2d_to_index(text.graphemes(true), (1,0), (Floor, Floor)), 12); + assert_eq!(f.v2d_to_index(text.graphemes(true), (1,11), (Floor, Floor)), 23); + assert_eq!(f.v2d_to_index(text.graphemes(true), (1,12), (Floor, Floor)), 23); + + assert_eq!(f.v2d_to_index(text.graphemes(true), (2,0), (Floor, Floor)), 24); + assert_eq!(f.v2d_to_index(text.graphemes(true), (2,11), (Floor, Floor)), 35); + assert_eq!(f.v2d_to_index(text.graphemes(true), (2,12), (Floor, Floor)), 35); + + assert_eq!(f.v2d_to_index(text.graphemes(true), (3,0), (Floor, Floor)), 36); + assert_eq!(f.v2d_to_index(text.graphemes(true), (3,11), (Floor, Floor)), 47); + assert_eq!(f.v2d_to_index(text.graphemes(true), (3,12), (Floor, Floor)), 47); + + assert_eq!(f.v2d_to_index(text.graphemes(true), (4,0), (Floor, Floor)), 48); + assert_eq!(f.v2d_to_index(text.graphemes(true), (4,7), (Floor, Floor)), 55); + assert_eq!(f.v2d_to_index(text.graphemes(true), (4,8), (Floor, Floor)), 56); + assert_eq!(f.v2d_to_index(text.graphemes(true), (4,9), (Floor, Floor)), 56); + } + + + #[test] + fn index_to_horizontal_v2d_1() { + let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 80; + + assert_eq!(f.index_to_horizontal_v2d(&b, 0), 0); + assert_eq!(f.index_to_horizontal_v2d(&b, 5), 5); + assert_eq!(f.index_to_horizontal_v2d(&b, 26), 3); + assert_eq!(f.index_to_horizontal_v2d(&b, 55), 32); + assert_eq!(f.index_to_horizontal_v2d(&b, 56), 32); + } + + + #[test] + fn index_to_horizontal_v2d_2() { + let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 12; + + assert_eq!(f.index_to_horizontal_v2d(&b, 0), 0); + assert_eq!(f.index_to_horizontal_v2d(&b, 11), 11); + + assert_eq!(f.index_to_horizontal_v2d(&b, 12), 0); + assert_eq!(f.index_to_horizontal_v2d(&b, 22), 10); + + assert_eq!(f.index_to_horizontal_v2d(&b, 23), 0); + assert_eq!(f.index_to_horizontal_v2d(&b, 34), 11); + + assert_eq!(f.index_to_horizontal_v2d(&b, 35), 0); + assert_eq!(f.index_to_horizontal_v2d(&b, 46), 11); + + assert_eq!(f.index_to_horizontal_v2d(&b, 47), 0); + assert_eq!(f.index_to_horizontal_v2d(&b, 55), 8); + assert_eq!(f.index_to_horizontal_v2d(&b, 56), 8); + } + + + #[test] + fn index_set_horizontal_v2d_1() { + let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 80; + + assert_eq!(f.index_set_horizontal_v2d(&b, 0, 0, Floor), 0); + assert_eq!(f.index_set_horizontal_v2d(&b, 0, 22, Floor), 22); + assert_eq!(f.index_set_horizontal_v2d(&b, 0, 23, Floor), 22); + + assert_eq!(f.index_set_horizontal_v2d(&b, 8, 0, Floor), 0); + assert_eq!(f.index_set_horizontal_v2d(&b, 8, 22, Floor), 22); + assert_eq!(f.index_set_horizontal_v2d(&b, 8, 23, Floor), 22); + + assert_eq!(f.index_set_horizontal_v2d(&b, 22, 0, Floor), 0); + assert_eq!(f.index_set_horizontal_v2d(&b, 22, 22, Floor), 22); + assert_eq!(f.index_set_horizontal_v2d(&b, 22, 23, Floor), 22); + + assert_eq!(f.index_set_horizontal_v2d(&b, 23, 0, Floor), 23); + assert_eq!(f.index_set_horizontal_v2d(&b, 23, 32, Floor), 55); + assert_eq!(f.index_set_horizontal_v2d(&b, 23, 33, Floor), 55); + + assert_eq!(f.index_set_horizontal_v2d(&b, 28, 0, Floor), 23); + assert_eq!(f.index_set_horizontal_v2d(&b, 28, 32, Floor), 55); + assert_eq!(f.index_set_horizontal_v2d(&b, 28, 33, Floor), 55); + + assert_eq!(f.index_set_horizontal_v2d(&b, 55, 0, Floor), 23); + assert_eq!(f.index_set_horizontal_v2d(&b, 55, 32, Floor), 55); + assert_eq!(f.index_set_horizontal_v2d(&b, 55, 33, Floor), 55); + } + + + #[test] + fn index_set_horizontal_v2d_2() { + let b = Buffer::new_from_str("Hello there, stranger! How are you doing this fine day?"); // 55 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 12; + + assert_eq!(f.index_set_horizontal_v2d(&b, 0, 0, Floor), 0); + assert_eq!(f.index_set_horizontal_v2d(&b, 0, 11, Floor), 11); + assert_eq!(f.index_set_horizontal_v2d(&b, 0, 12, Floor), 11); + + assert_eq!(f.index_set_horizontal_v2d(&b, 8, 0, Floor), 0); + assert_eq!(f.index_set_horizontal_v2d(&b, 8, 11, Floor), 11); + assert_eq!(f.index_set_horizontal_v2d(&b, 8, 12, Floor), 11); + + assert_eq!(f.index_set_horizontal_v2d(&b, 11, 0, Floor), 0); + assert_eq!(f.index_set_horizontal_v2d(&b, 11, 11, Floor), 11); + assert_eq!(f.index_set_horizontal_v2d(&b, 11, 12, Floor), 11); + + assert_eq!(f.index_set_horizontal_v2d(&b, 12, 0, Floor), 12); + assert_eq!(f.index_set_horizontal_v2d(&b, 12, 11, Floor), 23); + assert_eq!(f.index_set_horizontal_v2d(&b, 12, 12, Floor), 23); + + assert_eq!(f.index_set_horizontal_v2d(&b, 17, 0, Floor), 12); + assert_eq!(f.index_set_horizontal_v2d(&b, 17, 11, Floor), 23); + assert_eq!(f.index_set_horizontal_v2d(&b, 17, 12, Floor), 23); + + assert_eq!(f.index_set_horizontal_v2d(&b, 23, 0, Floor), 12); + assert_eq!(f.index_set_horizontal_v2d(&b, 23, 11, Floor), 23); + assert_eq!(f.index_set_horizontal_v2d(&b, 23, 12, Floor), 23); + } + + + #[test] + fn index_offset_vertical_v2d_1() { + let b = Buffer::new_from_str("Hello there, stranger!\nHow are you doing this fine day?"); // 55 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 80; + + assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0); + assert_eq!(f.index_offset_vertical_v2d(&b, 0, 1, (Floor, Floor)), 23); + assert_eq!(f.index_offset_vertical_v2d(&b, 23, -1, (Floor, Floor)), 0); + + assert_eq!(f.index_offset_vertical_v2d(&b, 2, 0, (Floor, Floor)), 2); + assert_eq!(f.index_offset_vertical_v2d(&b, 2, 1, (Floor, Floor)), 25); + assert_eq!(f.index_offset_vertical_v2d(&b, 25, -1, (Floor, Floor)), 2); + + assert_eq!(f.index_offset_vertical_v2d(&b, 22, 0, (Floor, Floor)), 22); + assert_eq!(f.index_offset_vertical_v2d(&b, 22, 1, (Floor, Floor)), 45); + assert_eq!(f.index_offset_vertical_v2d(&b, 45, -1, (Floor, Floor)), 22); + + assert_eq!(f.index_offset_vertical_v2d(&b, 54, 0, (Floor, Floor)), 54); + assert_eq!(f.index_offset_vertical_v2d(&b, 54, 1, (Floor, Floor)), 55); + assert_eq!(f.index_offset_vertical_v2d(&b, 54, -1, (Floor, Floor)), 22); + } + + + #[test] + fn index_offset_vertical_v2d_2() { + let b = Buffer::new_from_str("Hello there, stranger! How are you doing this fine day?"); // 55 graphemes long + let mut f = ConsoleLineFormatter::new(4); + f.wrap_width = 12; + + assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0); + assert_eq!(f.index_offset_vertical_v2d(&b, 0, 1, (Floor, Floor)), 12); + assert_eq!(f.index_offset_vertical_v2d(&b, 0, 2, (Floor, Floor)), 24); + + assert_eq!(f.index_offset_vertical_v2d(&b, 0, 0, (Floor, Floor)), 0); + assert_eq!(f.index_offset_vertical_v2d(&b, 12, -1, (Floor, Floor)), 0); + assert_eq!(f.index_offset_vertical_v2d(&b, 24, -2, (Floor, Floor)), 0); + + assert_eq!(f.index_offset_vertical_v2d(&b, 4, 0, (Floor, Floor)), 4); + assert_eq!(f.index_offset_vertical_v2d(&b, 4, 1, (Floor, Floor)), 16); + assert_eq!(f.index_offset_vertical_v2d(&b, 4, 2, (Floor, Floor)), 28); + + assert_eq!(f.index_offset_vertical_v2d(&b, 4, 0, (Floor, Floor)), 4); + assert_eq!(f.index_offset_vertical_v2d(&b, 16, -1, (Floor, Floor)), 4); + assert_eq!(f.index_offset_vertical_v2d(&b, 28, -2, (Floor, Floor)), 4); + + assert_eq!(f.index_offset_vertical_v2d(&b, 11, 0, (Floor, Floor)), 11); + assert_eq!(f.index_offset_vertical_v2d(&b, 11, 1, (Floor, Floor)), 23); + assert_eq!(f.index_offset_vertical_v2d(&b, 11, 2, (Floor, Floor)), 35); + + assert_eq!(f.index_offset_vertical_v2d(&b, 11, 0, (Floor, Floor)), 11); + assert_eq!(f.index_offset_vertical_v2d(&b, 23, -1, (Floor, Floor)), 11); + assert_eq!(f.index_offset_vertical_v2d(&b, 35, -2, (Floor, Floor)), 11); + } +} \ No newline at end of file diff --git a/todo.md b/todo.md index 0a9c1e0..3e9575c 100644 --- a/todo.md +++ b/todo.md @@ -15,11 +15,6 @@ - Loading/saving code for different encodings. - Auto-detecting text encodings from file data (this one will be tricky). -- Switch back to using a Rope directly, instead of using a tree of lines. - The code for everything is sufficiently clear now that doing this shouldn't - be difficult, as the needed API's are pretty obvious. And then we have - fewer moving parts. Yay! - - Word wrap. - Get non-wrapping text working again. - File opening by entering path