From efcdcac1fbb2b8496982a3ad30ffb90aa3727f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Sun, 19 May 2019 19:41:03 +0200 Subject: [PATCH] Add move down --- rider-editor/src/app/caret_manager.rs | 51 ++++++++- rider-editor/src/app/file_content_manager.rs | 3 +- rider-editor/src/main.rs | 2 +- rider-editor/src/renderer/renderer.rs | 2 +- rider-editor/src/ui/file/editor_file.rs | 86 +++++++++++++++ .../src/ui/file/editor_file_section.rs | 100 +++++++++++++++++- rider-editor/src/ui/file/editor_file_token.rs | 66 +++++++++++- rider-editor/src/ui/file_editor/mod.rs | 2 +- 8 files changed, 293 insertions(+), 19 deletions(-) diff --git a/rider-editor/src/app/caret_manager.rs b/rider-editor/src/app/caret_manager.rs index 5e225db..661f09a 100644 --- a/rider-editor/src/app/caret_manager.rs +++ b/rider-editor/src/app/caret_manager.rs @@ -13,9 +13,7 @@ pub fn move_caret_right(file_editor: &mut FileEditor) { let pos = file_editor.caret().position(); let d = c.dest().clone(); let p = pos.moved(1, 0, 0); - file_editor - .caret_mut() - .move_caret(p, Point::new(d.x(), d.y())); + file_editor.caret_mut().move_caret(p, d.top_left()); } pub fn move_caret_left(file_editor: &mut FileEditor) { @@ -34,9 +32,52 @@ pub fn move_caret_left(file_editor: &mut FileEditor) { let pos = file_editor.caret().position(); let character_destination = text_character.dest().clone(); let p = pos.moved(-1, 0, 0); + file_editor + .caret_mut() + .move_caret(p, character_destination.top_left()); +} + +pub fn move_caret_down(file_editor: &mut FileEditor) { + let file: &EditorFile = match file_editor.file() { + None => return, + Some(f) => f, + }; + if file_editor.caret().text_position() == 0 { + return; + } + let current_line_number = file_editor.caret().line_number(); + let mut next_line_position = 0; + let mut desired_line_position = 0; + let mut text_character: Option<&TextCharacter> = None; + for c in file.iter_char() { + match c.line() { + line if c.position() < file_editor.caret().text_position() + && current_line_number == line => + { + desired_line_position += 1; + } + line if line == current_line_number + 1 => { + text_character = Some(c); + if next_line_position == desired_line_position { + break; + } + next_line_position += 1; + } + line if line == current_line_number + 2 => { + break; + } + _ => {} + } + } + let text_character: &TextCharacter = match text_character { + Some(text_character) => text_character, + None => return, // EOF + }; + let character_destination = text_character.dest().clone(); + let pos = text_character.position().clone(); file_editor.caret_mut().move_caret( - p, - Point::new(character_destination.x(), character_destination.y()), + CaretPosition::new(pos, current_line_number + 1, next_line_position), + character_destination.top_left(), ); } diff --git a/rider-editor/src/app/file_content_manager.rs b/rider-editor/src/app/file_content_manager.rs index d0d8ebb..49c7878 100644 --- a/rider-editor/src/app/file_content_manager.rs +++ b/rider-editor/src/app/file_content_manager.rs @@ -1,7 +1,7 @@ use crate::app::*; use crate::renderer::renderer::Renderer; use crate::ui::*; -use sdl2::rect::{Point, Rect}; +use sdl2::rect::Point; use std::sync::*; pub fn current_file_path(file_editor: &mut FileEditor) -> String { @@ -142,6 +142,7 @@ mod tests { use crate::renderer::managers::FontDetails; use crate::renderer::managers::TextDetails; use crate::tests::support; + use sdl2::rect::Rect; use sdl2::render::Texture; use sdl2::ttf::Font; use std::rc::Rc; diff --git a/rider-editor/src/main.rs b/rider-editor/src/main.rs index f864617..b4f2cd0 100644 --- a/rider-editor/src/main.rs +++ b/rider-editor/src/main.rs @@ -24,7 +24,7 @@ pub mod ui; #[cfg_attr(tarpaulin, skip)] fn init_logger(directories: &Directories) { - use simplelog::SharedLogger; + // use simplelog::SharedLogger; let mut log_file_path = directories.log_dir.clone(); log_file_path.push("rider.log"); diff --git a/rider-editor/src/renderer/renderer.rs b/rider-editor/src/renderer/renderer.rs index 998104f..7df8e58 100644 --- a/rider-editor/src/renderer/renderer.rs +++ b/rider-editor/src/renderer/renderer.rs @@ -111,7 +111,7 @@ impl<'l> Renderer for CanvasRenderer<'l> { details: &mut TextDetails, font_details: FontDetails, ) -> Result, String> { - use crate::renderer::managers::TextTextureManager; + use crate::renderer::managers::*; let font = self .font_manager() .load(&font_details) diff --git a/rider-editor/src/ui/file/editor_file.rs b/rider-editor/src/ui/file/editor_file.rs index f11534d..e03c21a 100644 --- a/rider-editor/src/ui/file/editor_file.rs +++ b/rider-editor/src/ui/file/editor_file.rs @@ -73,6 +73,10 @@ impl EditorFile { section.update_positions(&mut current); } } + + pub fn iter_char(&self) -> EditorFileIterator { + EditorFileIterator::new(self) + } } impl TextCollection for EditorFile { @@ -208,12 +212,94 @@ impl RenderBox for EditorFile { } } +pub struct EditorFileIterator<'a> { + current_section: usize, + current_token: usize, + current_character: usize, + file: &'a EditorFile, +} + +impl<'a> EditorFileIterator<'a> { + pub fn new(file: &'a EditorFile) -> Self { + Self { + file, + current_section: 0, + current_token: 0, + current_character: 0, + } + } + + fn get_section(&self) -> Option<&'a EditorFileSection> { + self.file.sections().get(self.current_section) + } + + fn get_token(&mut self, section: &'a EditorFileSection) -> Option<&'a EditorFileToken> { + section.tokens().get(self.current_token).or_else(|| { + self.current_section += 1; + self.current_token = 0; + self.current_character = 0; + self.get_token(self.get_section()?) + }) + } + + fn get_character(&mut self, token: &'a EditorFileToken) -> Option<&'a TextCharacter> { + token + .characters() + .get(self.current_character) + .or_else(|| { + self.current_character = 0; + self.current_token += 1; + let token = self.get_token(self.get_section()?)?; + self.get_character(token) + }) + .and_then(|c| { + self.current_character += 1; + Some(c) + }) + } +} + +impl<'a> Iterator for EditorFileIterator<'a> { + type Item = &'a TextCharacter; + + fn next(&mut self) -> Option { + let token = self.get_token(self.get_section()?)?; + self.get_character(token) + } +} + #[cfg(test)] mod tests { use crate::tests::support; + use crate::tests::support::SimpleRendererMock; use crate::ui::*; use sdl2::rect::{Point, Rect}; + //################################################## + // iterator + //################################################## + + #[test] + fn assert_simple_iterations() { + let config = support::build_config(); + let mut renderer = SimpleRendererMock::new(config.clone()); + let mut file = + EditorFile::new("./foo.txt".to_owned(), "a b c d".to_owned(), config.clone()); + file.prepare_ui(&mut renderer); + for (index, c) in file.iter_char().enumerate() { + match index { + 0 => assert_eq!(c.text_character(), 'a'), + 1 => assert_eq!(c.text_character(), ' '), + 2 => assert_eq!(c.text_character(), 'b'), + 3 => assert_eq!(c.text_character(), ' '), + 4 => assert_eq!(c.text_character(), 'c'), + 5 => assert_eq!(c.text_character(), ' '), + 6 => assert_eq!(c.text_character(), 'd'), + _ => assert_eq!("must have 7 entries", "have more than 7 entries"), + } + } + } + //################################################## // path //################################################## diff --git a/rider-editor/src/ui/file/editor_file_section.rs b/rider-editor/src/ui/file/editor_file_section.rs index 300d1e7..1d7fc52 100644 --- a/rider-editor/src/ui/file/editor_file_section.rs +++ b/rider-editor/src/ui/file/editor_file_section.rs @@ -79,6 +79,14 @@ impl EditorFileSection { token.prepare_ui(renderer); } } + + fn iter_char(&self) -> EditorFileSectionIterator { + EditorFileSectionIterator::new(self) + } + + pub fn tokens(&self) -> &Vec { + &self.tokens + } } impl TextWidget for EditorFileSection { @@ -188,16 +196,55 @@ impl ClickHandler for EditorFileSection { } } +pub struct EditorFileSectionIterator<'a> { + section: &'a EditorFileSection, + current_token: usize, + current_character: usize, +} + +impl<'a> EditorFileSectionIterator<'a> { + pub fn new(section: &'a EditorFileSection) -> Self { + Self { + section, + current_token: 0, + current_character: 0, + } + } + + fn get_token(&self) -> Option<&'a EditorFileToken> { + self.section.tokens.get(self.current_token) + } + + fn get_character(&mut self, token: &'a EditorFileToken) -> Option<&'a TextCharacter> { + token + .characters() + .get(self.current_character) + .or_else(|| { + self.current_character = 0; + self.current_token += 1; + self.get_character(self.get_token()?) + }) + .and_then(|c| { + self.current_character += 1; + Some(c) + }) + } +} + +impl<'a> std::iter::Iterator for EditorFileSectionIterator<'a> { + type Item = &'a TextCharacter; + + fn next(&mut self) -> Option { + self.get_character(self.get_token()?) + } +} + #[cfg(test)] mod tests { use super::*; - use crate::tests::support::build_config; + use crate::tests::support::{build_config, SimpleRendererMock}; impl EditorFileSection { - pub fn tokens(&self) -> Vec { - self.tokens.clone() - } - pub fn tokens_count(&self) -> usize { self.tokens.len() } @@ -218,4 +265,47 @@ mod tests { assert_eq!(widget.language(), Language::Rust); assert_eq!(widget.tokens_count(), 8); } + + #[test] + fn assert_simple_char_iteration() { + let config = build_config(); + let mut renderer = SimpleRendererMock::new(config.clone()); + let mut section = EditorFileSection::new("a b c d".to_owned(), ".txt".to_owned(), config); + section.prepare_ui(&mut renderer); + for (index, c) in section.iter_char().enumerate() { + match index { + 0 => assert_eq!(c.text_character(), 'a'), + 1 => assert_eq!(c.text_character(), ' '), + 2 => assert_eq!(c.text_character(), 'b'), + 3 => assert_eq!(c.text_character(), ' '), + 4 => assert_eq!(c.text_character(), 'c'), + 5 => assert_eq!(c.text_character(), ' '), + 6 => assert_eq!(c.text_character(), 'd'), + _ => assert_eq!("must have 7 entries", "have more than 7 entries"), + } + } + } + + #[test] + fn assert_complex_char_iteration() { + let config = build_config(); + let mut renderer = SimpleRendererMock::new(config.clone()); + let mut section = EditorFileSection::new("let a = 1".to_owned(), ".rs".to_owned(), config); + section.prepare_ui(&mut renderer); + assert_eq!(section.tokens.len(), 7); + for (index, c) in section.iter_char().enumerate() { + match index { + 0 => assert_eq!(c.text_character(), 'l'), + 1 => assert_eq!(c.text_character(), 'e'), + 2 => assert_eq!(c.text_character(), 't'), + 3 => assert_eq!(c.text_character(), ' '), + 4 => assert_eq!(c.text_character(), 'a'), + 5 => assert_eq!(c.text_character(), ' '), + 6 => assert_eq!(c.text_character(), '='), + 7 => assert_eq!(c.text_character(), ' '), + 8 => assert_eq!(c.text_character(), '1'), + _ => assert_eq!("must have 9 entries", "have more than 9 entries"), + } + } + } } diff --git a/rider-editor/src/ui/file/editor_file_token.rs b/rider-editor/src/ui/file/editor_file_token.rs index 60fe7c4..4ac4aae 100644 --- a/rider-editor/src/ui/file/editor_file_token.rs +++ b/rider-editor/src/ui/file/editor_file_token.rs @@ -40,6 +40,10 @@ impl EditorFileToken { } } + pub fn characters(&self) -> &Vec { + &self.characters + } + fn token_to_color(&self, config: &Arc>) -> Color { let config = config.read().unwrap(); let ch = config.theme().code_highlighting(); @@ -54,6 +58,10 @@ impl EditorFileToken { &TokenType::Separator { .. } => ch.separator().color().into(), } } + + fn iter_char(&self) -> EditorFileTokenIterator { + EditorFileTokenIterator::new(self) + } } impl TextWidget for EditorFileToken { @@ -196,6 +204,32 @@ impl ClickHandler for EditorFileToken { } } +#[derive(Clone)] +pub struct EditorFileTokenIterator<'a> { + editor_file_token: &'a EditorFileToken, + current_character: usize, +} + +impl<'a> EditorFileTokenIterator<'a> { + pub fn new(editor_file_token: &'a EditorFileToken) -> Self { + Self { + editor_file_token, + current_character: 0, + } + } +} + +impl<'a> std::iter::Iterator for EditorFileTokenIterator<'a> { + type Item = &'a TextCharacter; + + fn next(&mut self) -> Option { + self.current_character += 1; + self.editor_file_token + .characters + .get(self.current_character) + } +} + #[cfg(test)] mod tests { use super::*; @@ -243,11 +277,6 @@ mod tests { unimplemented!("load_font") } - #[cfg_attr(tarpaulin, skip)] - fn load_image(&mut self, _path: String) -> Result, String> { - unimplemented!() - } - fn load_text_tex( &mut self, _details: &mut TextDetails, @@ -265,6 +294,11 @@ mod tests { // }, Ok) Err("".to_owned()) } + + #[cfg_attr(tarpaulin, skip)] + fn load_image(&mut self, _path: String) -> Result, String> { + unimplemented!() + } } impl<'l> CharacterSizeManager for RendererMock<'l> { @@ -279,6 +313,28 @@ mod tests { } } + //################################################## + // iterator + //################################################## + + #[test] + fn assert_iterator() { + let config = build_config(); + let token_type = TokenType::String { + token: Token::new("abcd".to_owned(), 0, 0, 0, 3), + }; + let token = EditorFileToken::new(&token_type, true, config.clone()); + for (i, c) in token.iter_char().enumerate() { + match i { + 0 => assert_eq!(c.text_character(), 'a'), + 1 => assert_eq!(c.text_character(), 'b'), + 2 => assert_eq!(c.text_character(), 'c'), + 3 => assert_eq!(c.text_character(), 'd'), + _ => assert_eq!("must have 4 characters", "have more than 4 characters"), + } + } + } + //################################################## // token_to_color //################################################## diff --git a/rider-editor/src/ui/file_editor/mod.rs b/rider-editor/src/ui/file_editor/mod.rs index 5e0d0b6..664f670 100644 --- a/rider-editor/src/ui/file_editor/mod.rs +++ b/rider-editor/src/ui/file_editor/mod.rs @@ -221,7 +221,7 @@ impl CaretAccess for FileEditor { MoveDirection::Left => caret_manager::move_caret_left(self), MoveDirection::Right => caret_manager::move_caret_right(self), MoveDirection::Up => {} - MoveDirection::Down => {} + MoveDirection::Down => caret_manager::move_caret_down(self), } }