From 4caad17e567224e1536fe68abc1aad4bcef63b02 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sat, 24 Jan 2015 21:00:30 -0800 Subject: [PATCH] Console drawing is almost back up to snuff. Just missing drawing the cursors if it's at the end of the text buffer. --- src/buffer/mod.rs | 25 +++++--------- src/buffer/node.rs | 72 +++++++++++++++++++++++++++++++++------ src/editor/mod.rs | 10 +++--- src/line_formatter.rs | 9 +++++ src/term_ui.rs | 78 +++++++++++++++++++++++++++++++------------ 5 files changed, 140 insertions(+), 54 deletions(-) diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 6c9a4b0..979a3f2 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -288,7 +288,7 @@ impl Buffer { /// 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); + return self.text.index_to_line_col_recursive(pos); } @@ -300,7 +300,7 @@ impl Buffer { /// 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); + return self.text.line_col_to_index_recursive(pos); } @@ -309,11 +309,11 @@ impl Buffer { /// 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) -> (usize, usize) { - // TODO: update this to use the new LineFormatter stuff - //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, self.tab_width); - //return (v, vis_h); - return (0, 0); + let mut index = pos; + if index > self.grapheme_count() { + index = self.grapheme_count(); + } + return self.text.index_to_v2d_recursive(&self.formatter, index); } @@ -324,16 +324,7 @@ impl Buffer { /// 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), rounding: (RoundingBehavior, RoundingBehavior)) -> usize { - // TODO: update this to use the new LineFormatter stuff - //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, self.tab_width); - // return gs + h; - //} - return 0; + return self.text.v2d_to_index_recursive(&self.formatter, pos, rounding); } diff --git a/src/buffer/node.rs b/src/buffer/node.rs index 20828e7..854732b 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; +use line_formatter::{LineFormatter, RoundingBehavior}; use string_utils::is_line_ending; use super::line::{Line, LineEnding, LineGraphemeIter, str_to_line_ending}; @@ -234,7 +234,26 @@ impl BufferNode { } - pub fn pos_2d_to_closest_1d_recursive(&self, pos: (usize, usize)) -> usize { + pub fn index_to_line_col_recursive(&self, index: usize) -> (usize, usize) { + match self.data { + BufferNodeData::Leaf(_) => { + return (0, min(index, self.grapheme_count)); + }, + + BufferNodeData::Branch(ref left, ref right) => { + if index < left.grapheme_count { + return left.index_to_line_col_recursive(index); + } + else { + let (v, h) = right.index_to_line_col_recursive((index - left.grapheme_count)); + return (v + left.line_count, h); + } + } + } + } + + + pub fn line_col_to_index_recursive(&self, pos: (usize, usize)) -> usize { match self.data { BufferNodeData::Leaf(ref line) => { if pos.0 != 0 { @@ -256,29 +275,60 @@ impl BufferNode { BufferNodeData::Branch(ref left, ref right) => { if pos.0 < left.line_count { - return left.pos_2d_to_closest_1d_recursive(pos); + return left.line_col_to_index_recursive(pos); } else { - return left.grapheme_count + right.pos_2d_to_closest_1d_recursive((pos.0 - left.line_count, pos.1)); + return left.grapheme_count + right.line_col_to_index_recursive((pos.0 - left.line_count, pos.1)); } } } } - pub fn pos_1d_to_closest_2d_recursive(&self, pos: usize) -> (usize, usize) { + pub fn index_to_v2d_recursive(&self, f: &T, index: usize) -> (usize, usize) { match self.data { - BufferNodeData::Leaf(_) => { - return (0, min(pos, self.grapheme_count)); + BufferNodeData::Leaf(ref line) => { + return f.index_to_v2d(line, index); }, BufferNodeData::Branch(ref left, ref right) => { - if pos < left.grapheme_count { - return left.pos_1d_to_closest_2d_recursive(pos); + if index < left.grapheme_count { + return left.index_to_v2d_recursive(f, index); } else { - let (v, h) = right.pos_1d_to_closest_2d_recursive((pos - left.grapheme_count)); - return (v + left.line_count, h); + let (y, x) = right.index_to_v2d_recursive(f, (index - left.grapheme_count)); + return (y + left.vis_dim.0, x); + } + } + } + } + + + pub fn v2d_to_index_recursive(&self, f: &T, pos: (usize, usize), rounding: (RoundingBehavior, RoundingBehavior)) -> usize { + match self.data { + BufferNodeData::Leaf(ref line) => { + return f.v2d_to_index(line, pos, rounding); + }, + + BufferNodeData::Branch(ref left, ref right) => { + let lh = f.single_line_height(); + + if rounding.0 == RoundingBehavior::Round { + // TODO + return 0; + + } + else if rounding.0 == RoundingBehavior::Floor { + if pos.0 < left.vis_dim.0 { + return left.v2d_to_index_recursive(f, pos, rounding); + } + else { + return left.grapheme_count + right.v2d_to_index_recursive(f, (pos.0 - left.vis_dim.0, pos.1), rounding); + } + } + else { // RoundingBehavior::Ceiling + // TODO + return 0; } } } diff --git a/src/editor/mod.rs b/src/editor/mod.rs index d37cf9b..d4e2e63 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -58,11 +58,11 @@ impl Editor { }; // For multiple-cursor testing - let mut cur = Cursor::new(); - cur.range.0 = 30; - cur.range.1 = 30; - cur.update_vis_start(&(ed.buffer)); - ed.cursors.add_cursor(cur); + //let mut cur = Cursor::new(); + //cur.range.0 = 30; + //cur.range.1 = 30; + //cur.update_vis_start(&(ed.buffer)); + //ed.cursors.add_cursor(cur); ed.auto_detect_indentation_style(); diff --git a/src/line_formatter.rs b/src/line_formatter.rs index 0ae9658..6cf50dd 100644 --- a/src/line_formatter.rs +++ b/src/line_formatter.rs @@ -1,6 +1,7 @@ use buffer::line::{Line, LineGraphemeIter}; use string_utils::{is_line_ending}; +#[derive(Copy, PartialEq)] pub enum RoundingBehavior { Round, Floor, @@ -8,6 +9,8 @@ pub enum RoundingBehavior { } 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); @@ -36,8 +39,10 @@ impl<'a> Iterator for ConsoleLineFormatterVisIter<'a> { fn next(&mut self) -> Option<(&'a str, 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)); } else { @@ -84,6 +89,10 @@ impl ConsoleLineFormatter { impl<'a> LineFormatter for ConsoleLineFormatter { + fn single_line_height(&self) -> usize { + return 1; + } + fn dimensions(&self, line: &Line) -> (usize, usize) { return (1, self.vis_width(line)); } diff --git a/src/term_ui.rs b/src/term_ui.rs index 1a99d33..c0cb534 100644 --- a/src/term_ui.rs +++ b/src/term_ui.rs @@ -7,7 +7,7 @@ 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; +use line_formatter::{LineFormatter, RoundingBehavior}; // Key codes const K_ENTER: u16 = 13; @@ -335,31 +335,67 @@ impl TermUI { fn draw_editor_text(&self, editor: &Editor, c1: (usize, usize), c2: (usize, usize)) { - let mut line_iter = editor.buffer.line_iter_at_index(editor.view_pos.0); + // Calculate all the starting info + let editor_corner_index = editor.buffer.v2d_to_index(editor.view_pos, (RoundingBehavior::Floor, RoundingBehavior::Floor)); + let (starting_line, _) = editor.buffer.index_to_line_col(editor_corner_index); + let mut grapheme_index = editor.buffer.line_col_to_index((starting_line, 0)); + let (vis_starting_line, _) = editor.buffer.index_to_v2d(grapheme_index); + + let mut screen_line = c1.0 as isize + vis_starting_line as isize; + let screen_col = c1.1 as isize; - //let mut grapheme_index; - - let mut vis_line_num = editor.view_pos.0; - let mut vis_col_num = editor.view_pos.1; - - let mut print_line_num = c1.0; - let mut print_col_num = c1.1; - - let max_print_line = c2.0; - let max_print_col = c2.1; - - loop { - if let Some(line) = line_iter.next() { - let mut g_iter = editor.buffer.formatter.vis_grapheme_iter(line); + let mut line_iter = editor.buffer.line_iter_at_index(starting_line); + for line in line_iter { + let mut g_iter = editor.buffer.formatter.vis_grapheme_iter(line); + let mut last_y = 0; + + // Loop through the graphemes of the line and print them to + // the screen. + for (g, pos_y, pos_x) in g_iter { + last_y = pos_y; - for (g, pos_y, pos_x) in g_iter { - self.rb.print(pos_x, pos_y, rustbox::RB_NORMAL, Color::White, Color::Black, g); + // Calculate the cell coordinates at which to draw the grapheme + let px = pos_x as isize + screen_col - editor.view_pos.1 as isize; + let py = pos_y as isize + screen_line - editor.view_pos.0 as isize; + + // If we're off the bottom, we're done + if py > c2.0 as isize { + return; } + + // Draw the grapheme to the screen if it's in bounds + if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) { + // Check if the character is within a cursor + let mut at_cursor = false; + for c in editor.cursors.iter() { + if grapheme_index >= c.range.0 && grapheme_index <= c.range.1 { + at_cursor = true; + } + } + + // Actually print the character + if is_line_ending(g) || g == "\t" { + if at_cursor { + self.rb.print(px as usize, py as usize, rustbox::RB_NORMAL, Color::Black, Color::White, " "); + } + } + else { + if at_cursor { + self.rb.print(px as usize, py as usize, rustbox::RB_NORMAL, Color::Black, Color::White, g); + } + else { + self.rb.print(px as usize, py as usize, rustbox::RB_NORMAL, Color::White, Color::Black, g); + } + } + } + + grapheme_index += 1; } - else { - break; - } + + screen_line += last_y as isize + 1; } + + // TODO: handle printing the cursor when it's at the end of the buffer. }