diff --git a/src/edit/buffer.rs b/src/edit/buffer.rs index a553d839988c16d0e90e2f42f0fd86b495a83d6b..514ff4e53d4de8d9ddc7f2920305ba179240d01a 100644 --- a/src/edit/buffer.rs +++ b/src/edit/buffer.rs @@ -59,6 +59,9 @@ pub trait TextBuffer<'a> { /// Get an iterator over the lines in the buffer. fn lines(&'a self) -> Self::LineIter; + /// Get an iterator over the line starting from a certain line + fn lines_from(&'a self, from: usize) -> Self::LineIter; + /// Get the leading whitespaces of the nth line. Used for autoindenting. fn get_indent(&self, n: usize) -> &str; } @@ -202,6 +205,13 @@ impl<'a> TextBuffer<'a> for SplitBuffer { } } + fn lines_from(&'a self, from: usize) -> SplitBufIter<'a> { + SplitBufIter { + buffer: self, + line: from, + } + } + fn get_indent(&self, n: usize) -> &str { if let Some(ln) = self.get_line(n) { let mut len = 0; diff --git a/src/io/graphics.rs b/src/io/graphics.rs index e73010dac04c54ab2a31f6caa0060946d7674907..c5e932452dba2e55acff77d4d2ad857dfeaaee0f 100644 --- a/src/io/graphics.rs +++ b/src/io/graphics.rs @@ -9,60 +9,106 @@ use state::mode::{Mode, PrimitiveMode}; #[cfg(feature = "orbital")] use orbclient::{Color, Renderer}; +use std::iter; + #[cfg(feature = "orbital")] impl Editor { /// Redraw the window pub fn redraw(&mut self) { - // TODO: Only draw when relevant for the window - let (pos_x, pos_y) = self.pos(); + let w = self.window.width() as usize; + let h = self.window.height() as usize; - let w = self.window.width(); - let h = self.window.height(); + let vert_offset: usize = 0; - if self.buffers.current_buffer_info().scroll_y > 0 - && pos_y <= self.buffers.current_buffer_info().scroll_y - { - self.buffers.current_buffer_info_mut().scroll_y = pos_y - 1; - } + let horz_offset: usize = if self.options.line_numbers { + let len = self.buffers.current_buffer_info().raw_buffer.len(); + let mut ret: usize = 3; + while len >= 10usize.pow((ret - 1) as u32) { + ret += 1; + } + ret + } else { + 0 + }; - let window_lines = (h as usize / 16) - 2; + let max_vert_chars = h/self.char_height - 2 - vert_offset; + let max_horz_chars = w/self.char_width - horz_offset; - if pos_y > self.buffers.current_buffer_info().scroll_y + window_lines { - self.buffers.current_buffer_info_mut().scroll_y = pos_y - window_lines; - } + // Redraw window + self.window.set(Color::rgb(25, 25, 25)); + + let mut scr_lines: usize = 0; + let mut scr_chars: usize = 0; - let (scroll_x, scroll_y) = { + self.cursor_in_window(max_horz_chars, max_vert_chars); + + let (_scroll_x, scroll_y) = { let current_buffer = self.buffers.current_buffer_info(); (current_buffer.scroll_x, current_buffer.scroll_y) }; - // Redraw window + let (pos_x, pos_y) = self.pos(); + + let (window_pos_x, window_pos_y) = self.coords_to_window_coords((pos_x, pos_y), max_horz_chars); + self.window.set(Color::rgb(25, 25, 25)); if self.options.line_marker { self.window.rect( 0, - (pos_y - scroll_y) as i32 * 16, - w, + ((window_pos_y + vert_offset) * self.char_height) as i32, + w as u32, 16, Color::rgb(45, 45, 45), ); } self.window.rect( - 8 * (pos_x - scroll_x) as i32, - 16 * (pos_y - scroll_y) as i32, - 8, - 16, + ((window_pos_x + horz_offset) * self.char_width) as i32, + ((window_pos_y + vert_offset) * self.char_height) as i32, + self.char_width as u32, + self.char_height as u32, Color::rgb(255, 255, 255), ); let mut string = false; + 'outer: for (y, row) in self.buffers.current_buffer().lines_from(scroll_y).enumerate() { + // Print line numbers + if self.options.line_numbers { + let mut line_number = scroll_y + y as usize + 1; + // The amount of digits for this line number + let mut digit_nr: usize = 0; + while line_number >= 10usize.pow(digit_nr as u32) { + digit_nr += 1; + } + // Print the digits for this line number + for i in 1..digit_nr + 1 { + let digit = ((line_number % 10) as u8 + ('0' as u32) as u8) as char; + line_number = (line_number - line_number % 10)/10 as usize; + self.window.char( + (self.char_width * (horz_offset - 1 - i)) as i32, + (self.char_height * (scr_lines + vert_offset)) as i32, + digit, + Color::rgb(255, 255, 0), + ); + } + } + for (x, c) in row.chars().flat_map(|c| if c == '\t' { + iter::repeat(' ').take(4) + } else { + iter::repeat(c).take(1) + }).enumerate() { + // New screen line + if scr_chars >= max_horz_chars { + scr_chars = 0; + scr_lines += 1; + if scr_lines > max_vert_chars { + break 'outer; + } + } - for (y, row) in self.buffers.current_buffer().lines().enumerate() { - for (x, c) in row.chars().enumerate() { // TODO: Move outta here let color = if self.options.highlight { match c { @@ -96,31 +142,86 @@ impl Editor { (255, 255, 255) }; - let c = if c == '\t' { ' ' } else { c }; - - if pos_x == x && pos_y == y { + if pos_x == x && (pos_y - scroll_y) == y { self.window.char( - 8 * (x as isize - scroll_x as isize) as i32, - 16 * (y as isize - scroll_y as isize) as i32, + (self.char_width * (scr_chars + horz_offset)) as i32, + (self.char_height * (scr_lines + vert_offset)) as i32, c, Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3), ); } else { self.window.char( - 8 * (x as isize - scroll_x as isize) as i32, - 16 * (y as isize - scroll_y as isize) as i32, + (self.char_width * (scr_chars + horz_offset)) as i32, + (self.char_height * (scr_lines + vert_offset)) as i32, c, Color::rgb(color.0, color.1, color.2), ); } + scr_chars += 1; + } + scr_lines += 1; + scr_chars = 0; + if scr_lines > max_vert_chars { + break; } } - + self.redraw_status_bar(); self.redraw_task = RedrawTask::None; + self.window.sync(); + } + fn coords_to_window_coords(&mut self, point: (usize, usize), max_horz_chars: usize) -> (usize, usize) { + let (_, scroll_y) = { + let current_buffer = self.buffers.current_buffer_info(); - self.redraw_status_bar(); - self.window.sync(); + (current_buffer.scroll_x, current_buffer.scroll_y) + }; + + let to_y = point.1 - scroll_y; + + let mut ret_y = 0; + + let ret_x = point.0 % max_horz_chars; + for (y, row) in self.buffers.current_buffer().lines_from(scroll_y).enumerate() { + if to_y > y { + ret_y += row.len() / max_horz_chars + 1; + } else { + ret_y += point.0 / max_horz_chars; + break; + } + } + (ret_x, ret_y) + } + + // Ensure that the cursor is visible + fn cursor_in_window(&mut self, max_horz_chars: usize, max_vert_chars: usize) { + let (_pos_x, pos_y) = self.pos(); + if self.buffers.current_buffer_info().scroll_y > 0 + && pos_y <= self.buffers.current_buffer_info().scroll_y + { + self.buffers.current_buffer_info_mut().scroll_y = if pos_y == 0 { + pos_y + } else { + pos_y - 1 + }; + return; + } + + let scroll_y = self.buffers.current_buffer_info().scroll_y; + let mut line_counter = 0; + let mut result_y = 0; + + for (y, row) in self.buffers.current_buffer().lines_from(pos_y+1).rev().enumerate() { + if pos_y - y < scroll_y { + return; + } + line_counter += row.len() / max_horz_chars + 1; + if line_counter > max_vert_chars { + result_y = pos_y - y; + break; + } + } + self.buffers.current_buffer_info_mut().scroll_y = result_y; } /// Redraw the status bar diff --git a/src/state/editor.rs b/src/state/editor.rs index 5950e441bd0642c3e0ff2f9eb9b8a7877758cc2b..a7712e4a68e04550613ff50e39905c899287e424 100644 --- a/src/state/editor.rs +++ b/src/state/editor.rs @@ -177,6 +177,10 @@ pub struct Editor { pub redraw_task: RedrawTask, /// The previous instruction pub previous_instruction: Option<Inst>, + /// The character width in pixels + pub char_width: usize, + /// The character height in pixels + pub char_height: usize, } impl Editor { @@ -196,6 +200,8 @@ impl Editor { key_state: KeyState::new(), redraw_task: RedrawTask::None, previous_instruction: None, + char_width: 8, + char_height: 16, }; #[cfg(not(feature = "orbital"))] @@ -207,6 +213,8 @@ impl Editor { key_state: KeyState::new(), redraw_task: RedrawTask::None, previous_instruction: None, + char_width: 8, + char_height: 16, }; let mut files: Vec<String> = Vec::new(); diff --git a/src/state/options.rs b/src/state/options.rs index f6ab37c12980b965e6c1dfb2df5da019d34185c1..43ce9a681d68d43a2f6e492a68ec6e0547b18010 100644 --- a/src/state/options.rs +++ b/src/state/options.rs @@ -10,6 +10,8 @@ pub struct Options { pub line_marker: bool, /// enables read-only mode pub readonly: bool, + /// Enable linenumbers + pub line_numbers: bool, } impl Options { @@ -21,6 +23,7 @@ impl Options { highlight: true, line_marker: true, readonly: false, + line_numbers: false, } } @@ -32,6 +35,7 @@ impl Options { "highlight" | "hl" => Some(&mut self.highlight), "line_marker" | "linemarker" | "linemark" | "lm" => Some(&mut self.line_marker), "readonly" | "ro" => Some(&mut self.readonly), + "line_numbers" | "ln" => Some(&mut self.line_numbers), _ => None, } } @@ -44,6 +48,7 @@ impl Options { "highlight" | "hl" => Some(self.highlight), "line_marker" | "linemarker" | "linemark" | "lm" => Some(self.line_marker), "readonly" | "ro" => Some(self.readonly), + "line_numbers" | "ln" => Some(self.line_numbers), _ => None, } }