Add move down
This commit is contained in:
parent
8bda7f99e4
commit
efcdcac1fb
@ -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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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");
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
//##################################################
|
//##################################################
|
||||||
|
@ -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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
//##################################################
|
//##################################################
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user