diff --git a/TODO.txt b/TODO.txt index ccb0a92..9d3e171 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,4 +2,6 @@ - multiple cursors - cmd-Z - find/replace - - write tests \ No newline at end of file + - write tests + - debounce ()) + - incremente suggestion in autofill \ No newline at end of file diff --git a/src/contextual_menu.rs b/src/contextual_menu.rs index 679eb0b..1a8c59e 100644 --- a/src/contextual_menu.rs +++ b/src/contextual_menu.rs @@ -146,7 +146,7 @@ impl ContextualMenu { | VirtualKeyCode::RWin | VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => {}, // Prevent closing the menu while pressing a modifier - _ => self.close() + _ => self.close(), } } @@ -212,7 +212,6 @@ impl ContextualMenu { } pub fn get_focused_item(&mut self) -> &mut MenuItem { - assert!(self.is_focus()); &mut self.items[self.focus_index as usize] } diff --git a/src/editor.rs b/src/editor.rs index b998ba5..4e6d427 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -2,6 +2,7 @@ use std::{cmp, env, fs}; use std::cell::RefCell; use std::rc::Rc; use std::path::{Path, PathBuf}; +use std::time::Instant; use speedy2d::color::Color; use speedy2d::dimen::Vector2; @@ -29,6 +30,7 @@ use crate::line::Line; use crate::range::Range; use crate::selection::Selection; use crate::editable::Editable; +use crate::stats::Stats; pub const EDITOR_PADDING: f32 = 10.; pub const EDITOR_OFFSET_TOP: f32 = 55.; @@ -65,6 +67,7 @@ pub struct Editor { pub bold_buffer: Vec, pub menu: ContextualMenu, pub cached_prefs: Option, + pub stats: Stats, } impl Editor { @@ -91,6 +94,7 @@ impl Editor { offset, padding, font, + stats: Stats::default() } } } @@ -99,6 +103,7 @@ impl Editable for Editor { fn add_char(&mut self, c: String) { if self.modifiers.logo() { let chars: Vec = c.chars().collect(); + dbg!(&chars); return self.shortcut(chars[0]); } // matching template @@ -161,7 +166,7 @@ impl Editable for Editor { VirtualKeyCode::Down => self.move_cursor_relative(0, 1), VirtualKeyCode::Backspace => self.delete_char(), VirtualKeyCode::Delete => { self.move_cursor_relative(1, 0); self.delete_char(); }, - VirtualKeyCode::Return => if self.modifiers.alt() { self.toggle_contextual_menu() } else { self.new_line() }, + VirtualKeyCode::Return => if self.modifiers.alt() { self.toggle_ai_contextual_menu() } else { self.new_line() }, VirtualKeyCode::Escape => self.menu.close(), VirtualKeyCode::Tab => if self.modifiers.alt() { self.menu.open() }, _ => { return; }, @@ -350,20 +355,8 @@ impl Editable for Editor { fn copy(&mut self) { if !self.selection.is_valid() { return; } let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap(); - let mut buffer = vec![]; - let lines_index = self.selection.get_lines_index(&self.lines); - let initial_y = self.selection.start().unwrap().y; - for (i, (start, end)) in lines_index.iter().enumerate() { - let y = initial_y as usize + i; - let mut text = String::new(); - for j in *start .. *end { - let buffer_text = self.lines[y].buffer.get(j as usize); - if let Some(bt) = buffer_text { text.push_str(bt); } - } - text.push('\n'); - buffer.push(text); - } - ctx.set_contents(buffer.join("")).unwrap(); + let selection_text = self.get_selected_text(); + ctx.set_contents(selection_text).unwrap(); } fn paste(&mut self) { @@ -444,6 +437,23 @@ impl Editor { &mut self.get_current_line().buffer } + fn get_selected_text(&mut self) -> String { + let mut buffer = vec![]; + let lines_index = self.selection.get_lines_index(&self.lines); + let initial_y = self.selection.start().unwrap().y; + for (i, (start, end)) in lines_index.iter().enumerate() { + let y = initial_y as usize + i; + let mut text = String::new(); + for j in *start .. *end { + let buffer_text = self.lines[y].buffer.get(j as usize); + if let Some(bt) = buffer_text { text.push_str(bt); } + } + text.push('\n'); + buffer.push(text); + } + buffer.join("") + } + pub fn new_line(&mut self) { self.delete_selection(); let mut new_line = Line::new(Rc::clone(&self.font)); @@ -522,6 +532,15 @@ impl Editor { self.menu.open_with(items); } + pub fn toggle_ai_contextual_menu(&mut self) { + if !self.selection.is_valid() { self.select_current_word(); } + let selected_text = self.get_selected_text(); + self.menu.open_with(vec![ + MenuItem::new("AI Correct", MenuAction::AICorrect), + MenuItem::new("AI Action >", MenuAction::AIQuestionWithInput), + ]); + } + pub fn get_menu(&mut self, id: MenuId) -> &mut ContextualMenu { // (0, -1, -1, -1) // let mut menu = &mut self.menu; @@ -634,11 +653,15 @@ impl Editor { fn get_stats(&self) -> Vec { let words_count = self.lines.iter().fold(0, |acc, line| acc + line.get_word_count()); let char_count = self.lines.iter().fold(0, |acc, line| acc + line.buffer.len()); + let update_duration = self.stats.update_duration.as_micros() as f64 / 1000.; + let draw_duration = self.stats.draw_duration.as_micros() as f64 / 1000.; vec![ iformat!("Nombre de mots: {words_count}"), iformat!("Nombre de carapaces: {char_count}"), iformat!("Nombre de lignes: {self.lines.len()}"), iformat!("Position du curseur: ({self.cursor.x}, {self.cursor.y})"), + iformat!("Update time: {update_duration:.1}ms"), + iformat!("Draw time: {draw_duration:.1}ms"), ] } @@ -825,6 +848,7 @@ impl Editor { } pub fn update(&mut self, dt: f32) { + let start_time = Instant::now(); let animations = self.get_animations(); for animation in animations { if let Some(anim) = animation { @@ -835,9 +859,11 @@ impl Editor { if anim.is_ended { *animation = Option::None; } } } + self.stats.update_duration = start_time.elapsed(); } pub fn render(&mut self, graphics: &mut Graphics2D) { + let start_time = Instant::now(); let char_width = self.font.borrow().char_width; let char_height = self.font.borrow().char_height; @@ -899,5 +925,6 @@ impl Editor { Color::GRAY ); } + self.stats.draw_duration = start_time.elapsed(); } } diff --git a/src/input.rs b/src/input.rs index 8289a3d..6b22428 100644 --- a/src/input.rs +++ b/src/input.rs @@ -24,6 +24,7 @@ pub const MAX_INPUT_WIDTH: f32 = 500.; const ANIMATION_DURATION: f32 = 100.; #[allow(unused)] +#[derive(PartialEq)] pub enum Validator { File, Path, @@ -73,7 +74,7 @@ impl Editable for Input { fn move_cursor_relative(&mut self, rel_x: i32, _rel_y: i32) { if self.editor.cursor.x as i32 + rel_x < 0 { return self.unfocus(); } let line = &mut self.editor.lines[0]; - if self.editor.cursor.x >= line.buffer.len() as u32 && rel_x > 0 { + if self.editor.cursor.x >= line.buffer.len() as u32 && rel_x > 0 && self.validator != Validator::None { line.add_text(&self.suggestion); self.editor.move_cursor_relative(self.suggestion.len() as i32, 0); return; diff --git a/src/main.rs b/src/main.rs index 75b4eef..af89f2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ mod render_helper; mod input; mod editable; mod menu_actions; +mod stats; use std::thread; use std::env; @@ -44,7 +45,7 @@ pub enum FocusElement { Editor, Menu(MenuId), MenuInput(MenuId) } #[derive(PartialEq, Debug, Clone)] pub enum EditorEvent { - Update, Redraw, Focus(FocusElement), MenuItemSelected(MenuAction), LoadFile(String) + Update, Redraw, Focus(FocusElement), MenuItemSelected(MenuAction), MenuItemUnselected(MenuAction, String), LoadFile(String) } struct EditorWindowHandler { @@ -95,6 +96,7 @@ impl WindowHandler for EditorWindowHandler { MenuAction::FindAndJump(text) => self.editor.find(&text), _ => {} }, + EditorEvent::MenuItemUnselected(item, key) => self.editor.add_char(key), EditorEvent::LoadFile(path) => set_app_title(helper, &path), } } @@ -165,16 +167,23 @@ impl WindowHandler for EditorWindowHandler { if unicode_codepoint >= ' ' && unicode_codepoint <= '~' || unicode_codepoint >= 'ยก' { match self.focus { FocusElement::Editor => { - self.editor.add_char(unicode_codepoint.to_string()); - self.editor.update_text_layout(); - } - + self.editor.add_char(unicode_codepoint.to_string()); + self.editor.update_text_layout(); + } FocusElement::MenuInput(id) => { let input = self.editor.get_menu(id).get_focused_item().input.as_mut().unwrap(); input.add_char(unicode_codepoint.to_string()); input.update_text_layout(); } - _ => {} + FocusElement::Menu(id) => { + // Cancel chip should disapear on keydown but the char should be added anyway + // Ugly + let menu = self.editor.get_menu(id); + if menu.items[0].action == MenuAction::CancelChip { + self.editor.add_char(unicode_codepoint.to_string()); + self.editor.update_text_layout(); + } + } } helper.request_redraw(); } diff --git a/src/menu_actions.rs b/src/menu_actions.rs index f9086c4..a248594 100644 --- a/src/menu_actions.rs +++ b/src/menu_actions.rs @@ -24,7 +24,10 @@ pub enum MenuAction { NewFile(String), NewFileWithInput(String), FindAndJumpWithInput, - FindAndJump(String) + FindAndJump(String), + AICorrect, + AIQuestion(String), + AIQuestionWithInput, } impl fmt::Display for MenuAction { @@ -39,6 +42,7 @@ impl MenuAction { MenuAction::PrintWithInput => MenuAction::Print, MenuAction::NewFileWithInput(_) => MenuAction::NewFile, MenuAction::FindAndJumpWithInput => MenuAction::FindAndJump, + MenuAction::AIQuestionWithInput => MenuAction::AIQuestion, _ => MenuAction::Print } } diff --git a/src/stats.rs b/src/stats.rs new file mode 100644 index 0000000..0fc69ee --- /dev/null +++ b/src/stats.rs @@ -0,0 +1,7 @@ +use std::time::Duration; + +#[derive(Debug, Clone, Default)] +pub struct Stats { + pub update_duration: Duration, + pub draw_duration: Duration +} \ No newline at end of file