From 0557f5f6ce890fbf84f78e49d3f73c555ff0cf95 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sun, 25 Jan 2015 13:20:16 -0800 Subject: [PATCH] Reorganized the code a bit, and started updating unit tests. --- src/buffer/line_formatter.rs | 61 ++++++++++++ src/buffer/mod.rs | 78 ++++++++-------- src/buffer/node.rs | 2 +- src/editor/cursor.rs | 2 +- src/editor/mod.rs | 5 +- src/files.rs | 2 +- src/main.rs | 21 ++--- .../formatter.rs} | 93 ++++++++----------- src/{term_ui.rs => term_ui/mod.rs} | 4 +- 9 files changed, 160 insertions(+), 108 deletions(-) create mode 100644 src/buffer/line_formatter.rs rename src/{line_formatter.rs => term_ui/formatter.rs} (84%) rename src/{term_ui.rs => term_ui/mod.rs} (99%) diff --git a/src/buffer/line_formatter.rs b/src/buffer/line_formatter.rs new file mode 100644 index 0000000..b89c36d --- /dev/null +++ b/src/buffer/line_formatter.rs @@ -0,0 +1,61 @@ +use buffer::line::Line; +use std::cmp::min; + +#[derive(Copy, PartialEq)] +pub enum RoundingBehavior { + Round, + Floor, + Ceiling, +} + + +pub trait LineFormatter { + fn single_line_height(&self) -> usize; + + fn dimensions(&self, line: &Line) -> (usize, usize); + + fn index_to_v2d(&self, line: &Line, index: usize) -> (usize, usize); + + fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize; +} + + + +//================================================================ +// A simple implementation of LineFormatter, for testing purposes +//================================================================ + +pub struct TestLineFormatter { + tab_width: u8 +} + +impl TestLineFormatter { + pub fn new() -> TestLineFormatter { + TestLineFormatter { + tab_width: 4, + } + } +} + +impl LineFormatter for TestLineFormatter { + fn single_line_height(&self) -> usize { + 1 + } + + fn dimensions(&self, line: &Line) -> (usize, usize) { + (1, line.grapheme_count()) + } + + fn index_to_v2d(&self, line: &Line, index: usize) -> (usize, usize) { + (0, min(line.grapheme_count(), index)) + } + + fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize { + if v2d.0 > 0 { + line.grapheme_count() + } + else { + min(line.grapheme_count(), v2d.1) + } + } +} \ No newline at end of file diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 979a3f2..b5e56c9 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -6,10 +6,11 @@ use self::line::{Line, LineEnding}; use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter}; use self::undo_stack::{UndoStack}; use self::undo_stack::Operation::*; -use line_formatter::{LineFormatter, RoundingBehavior}; +use self::line_formatter::{LineFormatter, RoundingBehavior}; use string_utils::{is_line_ending, grapheme_count}; pub mod line; +pub mod line_formatter; mod node; mod undo_stack; @@ -503,11 +504,12 @@ impl<'a> Iterator for BufferLineIter<'a> { #[cfg(test)] mod tests { + use super::line_formatter::TestLineFormatter; use super::{Buffer, BufferGraphemeIter, BufferLineIter}; #[test] fn insert_text() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello 世界!", 0); @@ -530,7 +532,7 @@ mod tests { #[test] fn insert_text_with_newlines() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\n 世界\r\n!", 0); @@ -555,7 +557,7 @@ mod tests { #[test] fn insert_text_in_non_empty_buffer_1() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\n 世界\r\n!", 0); buf.insert_text("Again ", 0); @@ -587,7 +589,7 @@ mod tests { #[test] fn insert_text_in_non_empty_buffer_2() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\n 世界\r\n!", 0); buf.insert_text(" again", 5); @@ -619,7 +621,7 @@ mod tests { #[test] fn insert_text_in_non_empty_buffer_3() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\n 世界\r\n!", 0); buf.insert_text("again", 6); @@ -650,7 +652,7 @@ mod tests { #[test] fn insert_text_in_non_empty_buffer_4() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\n 世界\r\n!", 0); buf.insert_text("again", 11); @@ -681,7 +683,7 @@ mod tests { #[test] fn insert_text_in_non_empty_buffer_5() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\n 世界\r\n!", 0); buf.insert_text("again", 2); @@ -713,7 +715,7 @@ mod tests { #[test] fn insert_text_in_non_empty_buffer_6() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\n 世界\r\n!", 0); buf.insert_text("again", 8); @@ -745,7 +747,7 @@ mod tests { #[test] fn insert_text_in_non_empty_buffer_7() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\n 世界\r\n!", 0); buf.insert_text("\nag\n\nain\n", 2); @@ -781,7 +783,7 @@ mod tests { #[test] fn remove_text_1() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); assert!(buf.grapheme_count() == 29); @@ -825,7 +827,7 @@ mod tests { #[test] fn remove_text_2() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); assert!(buf.grapheme_count() == 29); @@ -860,7 +862,7 @@ mod tests { #[test] fn remove_text_3() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); assert!(buf.grapheme_count() == 29); @@ -895,7 +897,7 @@ mod tests { #[test] fn remove_text_4() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); assert!(buf.grapheme_count() == 29); @@ -936,7 +938,7 @@ mod tests { #[test] fn remove_text_5() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); assert!(buf.grapheme_count() == 29); @@ -971,7 +973,7 @@ mod tests { #[test] fn remove_text_6() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\nworld!", 0); assert!(buf.grapheme_count() == 12); @@ -992,7 +994,7 @@ mod tests { #[test] fn remove_text_7() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\nworld!", 0); assert!(buf.grapheme_count() == 15); @@ -1015,7 +1017,7 @@ mod tests { #[test] fn remove_text_8() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\nworld!", 0); assert!(buf.grapheme_count() == 12); @@ -1037,7 +1039,7 @@ mod tests { #[test] fn remove_text_9() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hello\nworld!", 0); assert!(buf.grapheme_count() == 12); @@ -1063,7 +1065,7 @@ mod tests { #[test] fn remove_text_10() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("12\n34\n56\n78", 0); assert!(buf.grapheme_count() == 11); @@ -1085,7 +1087,7 @@ mod tests { #[test] fn remove_text_11() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("1234567890", 0); assert!(buf.grapheme_count() == 10); @@ -1112,7 +1114,7 @@ mod tests { #[test] fn move_text_1() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); @@ -1157,7 +1159,7 @@ mod tests { #[test] fn move_text_2() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); @@ -1202,7 +1204,7 @@ mod tests { #[test] fn move_text_3() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); @@ -1247,7 +1249,7 @@ mod tests { #[test] fn move_text_4() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); @@ -1292,7 +1294,7 @@ mod tests { #[test] fn move_text_5() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); @@ -1337,7 +1339,7 @@ mod tests { #[test] fn remove_lines_1() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); assert!(buf.grapheme_count() == 29); @@ -1368,7 +1370,7 @@ mod tests { #[test] fn remove_lines_2() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); assert!(buf.grapheme_count() == 29); @@ -1399,7 +1401,7 @@ mod tests { #[test] fn remove_lines_3() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); assert!(buf.grapheme_count() == 29); @@ -1432,7 +1434,7 @@ mod tests { #[test] fn line_col_to_index_1() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let pos = buf.line_col_to_index((2, 3)); @@ -1443,7 +1445,7 @@ mod tests { #[test] fn line_col_to_index_2() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let pos = buf.line_col_to_index((2, 10)); @@ -1453,7 +1455,7 @@ mod tests { #[test] fn line_col_to_index_3() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let pos = buf.line_col_to_index((10, 2)); @@ -1464,7 +1466,7 @@ mod tests { #[test] fn index_to_line_col_1() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let pos = buf.index_to_line_col(5); @@ -1475,7 +1477,7 @@ mod tests { #[test] fn index_to_line_col_2() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let pos = buf.index_to_line_col(50); @@ -1486,7 +1488,7 @@ mod tests { #[test] fn string_from_range_1() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let s = buf.string_from_range(1, 12); @@ -1497,7 +1499,7 @@ mod tests { #[test] fn string_from_range_2() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let s = buf.string_from_range(0, 29); @@ -1508,7 +1510,7 @@ mod tests { #[test] fn grapheme_iter_at_index_1() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let mut iter = buf.grapheme_iter_at_index(16); @@ -1532,7 +1534,7 @@ mod tests { #[test] fn grapheme_iter_at_index_2() { - let mut buf = Buffer::new(); + let mut buf = Buffer::new(TestLineFormatter::new()); buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0); let mut iter = buf.grapheme_iter_at_index(29); diff --git a/src/buffer/node.rs b/src/buffer/node.rs index a5b3518..73acf86 100644 --- a/src/buffer/node.rs +++ b/src/buffer/node.rs @@ -1,7 +1,7 @@ use std::mem; use std::cmp::{min, max}; -use line_formatter::{LineFormatter, RoundingBehavior}; +use super::line_formatter::{LineFormatter, RoundingBehavior}; use string_utils::is_line_ending; use super::line::{Line, LineEnding, LineGraphemeIter, str_to_line_ending}; diff --git a/src/editor/cursor.rs b/src/editor/cursor.rs index a6d4567..6bbe61f 100644 --- a/src/editor/cursor.rs +++ b/src/editor/cursor.rs @@ -5,7 +5,7 @@ use std::ops::{Index, IndexMut}; use std::cmp::Ordering; use buffer::Buffer; -use line_formatter::LineFormatter; +use buffer::line_formatter::LineFormatter; /// A text cursor. Also represents selections when range.0 != range.1. /// diff --git a/src/editor/mod.rs b/src/editor/mod.rs index 0b4712f..e3bf138 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -1,8 +1,9 @@ #![allow(dead_code)] use buffer::Buffer; -use line_formatter::{LineFormatter, ConsoleLineFormatter}; -use line_formatter::RoundingBehavior::*; +use buffer::line_formatter::LineFormatter; +use buffer::line_formatter::RoundingBehavior::*; +use term_ui::formatter::ConsoleLineFormatter; use std::path::Path; use std::cmp::min; use files::{load_file_to_buffer, save_buffer_to_file}; diff --git a/src/files.rs b/src/files.rs index f009e83..0215c4f 100644 --- a/src/files.rs +++ b/src/files.rs @@ -3,7 +3,7 @@ use std::io::fs::File; use std::path::Path; use buffer::line::{Line, LineEnding, line_ending_to_str}; -use line_formatter::LineFormatter; +use buffer::line_formatter::LineFormatter; use buffer::Buffer as TextBuffer; pub fn load_file_to_buffer(path: &Path, lf: T) -> IoResult> { diff --git a/src/main.rs b/src/main.rs index ae4e656..fc16abd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,6 @@ use term_ui::TermUI; mod string_utils; mod buffer; -mod line_formatter; mod files; mod editor; mod term_ui; @@ -59,18 +58,16 @@ fn main() { }; // Initialize and start UI -// if args.flag_gui { -// // GUI -// sdl2::init(sdl2::INIT_VIDEO); -// let mut ui = GUI::new_from_editor(editor); -// ui.main_ui_loop(); -// sdl2::quit(); -// } -// else { + //if args.flag_gui { + // // GUI + // sdl2::init(sdl2::INIT_VIDEO); + // let mut ui = GUI::new_from_editor(editor); + // ui.main_ui_loop(); + // sdl2::quit(); + //} + //else { // Console UI let mut ui = TermUI::new_from_editor(editor); ui.main_ui_loop(); -// } - - //println!("{}", editor.buffer.root.tree_height); + //} } diff --git a/src/line_formatter.rs b/src/term_ui/formatter.rs similarity index 84% rename from src/line_formatter.rs rename to src/term_ui/formatter.rs index 53021c8..1f8f2ee 100644 --- a/src/line_formatter.rs +++ b/src/term_ui/formatter.rs @@ -1,56 +1,10 @@ -use buffer::line::{Line, LineGraphemeIter}; use string_utils::{is_line_ending}; +use buffer::line::{Line, LineGraphemeIter}; +use buffer::line_formatter::{LineFormatter, RoundingBehavior}; -#[derive(Copy, PartialEq)] -pub enum RoundingBehavior { - Round, - Floor, - Ceiling, -} - -pub trait LineFormatter { - fn single_line_height(&self) -> usize; - - fn dimensions(&self, line: &Line) -> (usize, usize); - - fn index_to_v2d(&self, line: &Line, index: usize) -> (usize, usize); - - fn v2d_to_index(&self, line: &Line, v2d: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize; -} - - - - - -//============================================================ -// An implementation of the LineFormatter stuff for consoles - -pub struct ConsoleLineFormatterVisIter<'a> { - grapheme_iter: LineGraphemeIter<'a>, - f: &'a ConsoleLineFormatter, - pos: (usize, usize), -} - - - -impl<'a> Iterator for ConsoleLineFormatterVisIter<'a> { - type Item = (&'a str, (usize, usize), usize); - - fn next(&mut self) -> Option<(&'a str, (usize, usize), usize)> { - if let Some(g) = self.grapheme_iter.next() { - let pos = self.pos; - - let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize); - self.pos = (self.pos.0, self.pos.1 + width); - - return Some((g, (pos.0, pos.1), width)); - } - else { - return None; - } - } -} - +//=================================================================== +// LineFormatter implementation for terminals/consoles. +//=================================================================== pub struct ConsoleLineFormatter { pub tab_width: u8, @@ -147,6 +101,41 @@ impl<'a> LineFormatter for ConsoleLineFormatter { } +//=================================================================== +// An iterator that iterates over the graphemes in a line in a +// manner consistent with the ConsoleFormatter. +//=================================================================== +pub struct ConsoleLineFormatterVisIter<'a> { + grapheme_iter: LineGraphemeIter<'a>, + f: &'a ConsoleLineFormatter, + pos: (usize, usize), +} + + + +impl<'a> Iterator for ConsoleLineFormatterVisIter<'a> { + type Item = (&'a str, (usize, usize), usize); + + fn next(&mut self) -> Option<(&'a str, (usize, usize), usize)> { + if let Some(g) = self.grapheme_iter.next() { + let pos = self.pos; + + let width = grapheme_vis_width_at_vis_pos(g, self.pos.1, self.f.tab_width as usize); + self.pos = (self.pos.0, self.pos.1 + width); + + return Some((g, (pos.0, pos.1), width)); + } + else { + return None; + } + } +} + + + +//=================================================================== +// Helper functions +//=================================================================== /// Returns the visual width of a grapheme given a starting /// position on a line. @@ -166,4 +155,4 @@ fn grapheme_vis_width_at_vis_pos(g: &str, pos: usize, tab_width: usize) -> usize } } } -} \ No newline at end of file +} diff --git a/src/term_ui.rs b/src/term_ui/mod.rs similarity index 99% rename from src/term_ui.rs rename to src/term_ui/mod.rs index 46944c9..a903fd1 100644 --- a/src/term_ui.rs +++ b/src/term_ui/mod.rs @@ -7,7 +7,9 @@ use std::char; use std::time::duration::Duration; use string_utils::{is_line_ending}; use buffer::line::{line_ending_to_str, LineEnding}; -use line_formatter::{LineFormatter, RoundingBehavior}; +use buffer::line_formatter::{LineFormatter, RoundingBehavior}; + +pub mod formatter; // Key codes const K_ENTER: u16 = 13;