Add move down

This commit is contained in:
Adrian Woźniak 2019-05-19 19:41:03 +02:00
parent 8bda7f99e4
commit efcdcac1fb
8 changed files with 293 additions and 19 deletions

View File

@ -13,9 +13,7 @@ pub fn move_caret_right(file_editor: &mut FileEditor) {
let pos = file_editor.caret().position(); let pos = file_editor.caret().position();
let d = c.dest().clone(); let d = c.dest().clone();
let p = pos.moved(1, 0, 0); let p = pos.moved(1, 0, 0);
file_editor file_editor.caret_mut().move_caret(p, d.top_left());
.caret_mut()
.move_caret(p, Point::new(d.x(), d.y()));
} }
pub fn move_caret_left(file_editor: &mut FileEditor) { 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 pos = file_editor.caret().position();
let character_destination = text_character.dest().clone(); let character_destination = text_character.dest().clone();
let p = pos.moved(-1, 0, 0); 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( file_editor.caret_mut().move_caret(
p, CaretPosition::new(pos, current_line_number + 1, next_line_position),
Point::new(character_destination.x(), character_destination.y()), character_destination.top_left(),
); );
} }

View File

@ -1,7 +1,7 @@
use crate::app::*; use crate::app::*;
use crate::renderer::renderer::Renderer; use crate::renderer::renderer::Renderer;
use crate::ui::*; use crate::ui::*;
use sdl2::rect::{Point, Rect}; use sdl2::rect::Point;
use std::sync::*; use std::sync::*;
pub fn current_file_path(file_editor: &mut FileEditor) -> String { 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::FontDetails;
use crate::renderer::managers::TextDetails; use crate::renderer::managers::TextDetails;
use crate::tests::support; use crate::tests::support;
use sdl2::rect::Rect;
use sdl2::render::Texture; use sdl2::render::Texture;
use sdl2::ttf::Font; use sdl2::ttf::Font;
use std::rc::Rc; use std::rc::Rc;

View File

@ -24,7 +24,7 @@ pub mod ui;
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
fn init_logger(directories: &Directories) { fn init_logger(directories: &Directories) {
use simplelog::SharedLogger; // use simplelog::SharedLogger;
let mut log_file_path = directories.log_dir.clone(); let mut log_file_path = directories.log_dir.clone();
log_file_path.push("rider.log"); log_file_path.push("rider.log");

View File

@ -111,7 +111,7 @@ impl<'l> Renderer for CanvasRenderer<'l> {
details: &mut TextDetails, details: &mut TextDetails,
font_details: FontDetails, font_details: FontDetails,
) -> Result<Rc<Texture>, String> { ) -> Result<Rc<Texture>, String> {
use crate::renderer::managers::TextTextureManager; use crate::renderer::managers::*;
let font = self let font = self
.font_manager() .font_manager()
.load(&font_details) .load(&font_details)

View File

@ -73,6 +73,10 @@ impl EditorFile {
section.update_positions(&mut current); section.update_positions(&mut current);
} }
} }
pub fn iter_char(&self) -> EditorFileIterator {
EditorFileIterator::new(self)
}
} }
impl TextCollection for EditorFile { 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<Self::Item> {
let token = self.get_token(self.get_section()?)?;
self.get_character(token)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::tests::support; use crate::tests::support;
use crate::tests::support::SimpleRendererMock;
use crate::ui::*; use crate::ui::*;
use sdl2::rect::{Point, Rect}; 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 // path
//################################################## //##################################################

View File

@ -79,6 +79,14 @@ impl EditorFileSection {
token.prepare_ui(renderer); token.prepare_ui(renderer);
} }
} }
fn iter_char(&self) -> EditorFileSectionIterator {
EditorFileSectionIterator::new(self)
}
pub fn tokens(&self) -> &Vec<EditorFileToken> {
&self.tokens
}
} }
impl TextWidget for EditorFileSection { 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::Item> {
self.get_character(self.get_token()?)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::tests::support::build_config; use crate::tests::support::{build_config, SimpleRendererMock};
impl EditorFileSection { impl EditorFileSection {
pub fn tokens(&self) -> Vec<EditorFileToken> {
self.tokens.clone()
}
pub fn tokens_count(&self) -> usize { pub fn tokens_count(&self) -> usize {
self.tokens.len() self.tokens.len()
} }
@ -218,4 +265,47 @@ mod tests {
assert_eq!(widget.language(), Language::Rust); assert_eq!(widget.language(), Language::Rust);
assert_eq!(widget.tokens_count(), 8); 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"),
}
}
}
} }

View File

@ -40,6 +40,10 @@ impl EditorFileToken {
} }
} }
pub fn characters(&self) -> &Vec<TextCharacter> {
&self.characters
}
fn token_to_color(&self, config: &Arc<RwLock<Config>>) -> Color { fn token_to_color(&self, config: &Arc<RwLock<Config>>) -> Color {
let config = config.read().unwrap(); let config = config.read().unwrap();
let ch = config.theme().code_highlighting(); let ch = config.theme().code_highlighting();
@ -54,6 +58,10 @@ impl EditorFileToken {
&TokenType::Separator { .. } => ch.separator().color().into(), &TokenType::Separator { .. } => ch.separator().color().into(),
} }
} }
fn iter_char(&self) -> EditorFileTokenIterator {
EditorFileTokenIterator::new(self)
}
} }
impl TextWidget for EditorFileToken { 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::Item> {
self.current_character += 1;
self.editor_file_token
.characters
.get(self.current_character)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -243,11 +277,6 @@ mod tests {
unimplemented!("load_font") unimplemented!("load_font")
} }
#[cfg_attr(tarpaulin, skip)]
fn load_image(&mut self, _path: String) -> Result<Rc<Texture>, String> {
unimplemented!()
}
fn load_text_tex( fn load_text_tex(
&mut self, &mut self,
_details: &mut TextDetails, _details: &mut TextDetails,
@ -265,6 +294,11 @@ mod tests {
// }, Ok) // }, Ok)
Err("".to_owned()) Err("".to_owned())
} }
#[cfg_attr(tarpaulin, skip)]
fn load_image(&mut self, _path: String) -> Result<Rc<Texture>, String> {
unimplemented!()
}
} }
impl<'l> CharacterSizeManager for RendererMock<'l> { 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 // token_to_color
//################################################## //##################################################

View File

@ -221,7 +221,7 @@ impl CaretAccess for FileEditor {
MoveDirection::Left => caret_manager::move_caret_left(self), MoveDirection::Left => caret_manager::move_caret_left(self),
MoveDirection::Right => caret_manager::move_caret_right(self), MoveDirection::Right => caret_manager::move_caret_right(self),
MoveDirection::Up => {} MoveDirection::Up => {}
MoveDirection::Down => {} MoveDirection::Down => caret_manager::move_caret_down(self),
} }
} }