From f2b426851be1bf2915674eb2c7c023ea666b9aea Mon Sep 17 00:00:00 2001 From: Priec Date: Thu, 21 Aug 2025 13:23:21 +0200 Subject: [PATCH] compiled still not working --- canvas/src/editor/suggestions.rs | 15 ++ client/Cargo.toml | 2 +- client/src/config/binds/config.rs | 40 ++++++ client/src/modes/handlers/event.rs | 219 +++++++---------------------- 4 files changed, 108 insertions(+), 168 deletions(-) diff --git a/canvas/src/editor/suggestions.rs b/canvas/src/editor/suggestions.rs index 9bd9c9f..bd40da0 100644 --- a/canvas/src/editor/suggestions.rs +++ b/canvas/src/editor/suggestions.rs @@ -133,6 +133,21 @@ impl FormEditor { self.update_inline_completion(); } + pub fn suggestions_prev(&mut self) { + if !self.ui_state.suggestions.is_active || self.suggestions.is_empty() { + return; + } + + let current = self.ui_state.suggestions.selected_index.unwrap_or(0); + let prev = if current == 0 { + self.suggestions.len() - 1 + } else { + current - 1 + }; + self.ui_state.suggestions.selected_index = Some(prev); + self.update_inline_completion(); + } + pub fn apply_suggestion(&mut self) -> Option { if let Some(selected_index) = self.ui_state.suggestions.selected_index { if let Some(suggestion) = self.suggestions.get(selected_index).cloned() diff --git a/client/Cargo.toml b/client/Cargo.toml index 878786a..5d28c14 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -8,7 +8,7 @@ license.workspace = true anyhow = { workspace = true } async-trait = "0.1.88" common = { path = "../common" } -canvas = { path = "../canvas", features = ["gui", "suggestions", "cursor-style"] } +canvas = { path = "../canvas", features = ["gui", "suggestions", "cursor-style", "keymap"] } ratatui = { workspace = true } crossterm = { workspace = true } diff --git a/client/src/config/binds/config.rs b/client/src/config/binds/config.rs index af2da0e..70d0f2d 100644 --- a/client/src/config/binds/config.rs +++ b/client/src/config/binds/config.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use std::path::Path; use anyhow::{Context, Result}; use crossterm::event::{KeyCode, KeyModifiers}; +use canvas::CanvasKeyMap; // NEW: Editor Keybinding Mode Enum #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -760,4 +761,43 @@ impl Config { } false } + + /// Unified action resolver for app-level actions + pub fn get_app_action( + &self, + key_code: crossterm::event::KeyCode, + modifiers: crossterm::event::KeyModifiers, + ) -> Option<&str> { + // First check common actions + if let Some(action) = self.get_common_action(key_code, modifiers) { + return Some(action); + } + + // Then check read-only mode actions + if let Some(action) = self.get_read_only_action_for_key(key_code, modifiers) { + return Some(action); + } + + // Then check highlight mode actions + if let Some(action) = self.get_highlight_action_for_key(key_code, modifiers) { + return Some(action); + } + + // Then check edit mode actions + if let Some(action) = self.get_edit_action_for_key(key_code, modifiers) { + return Some(action); + } + + None + } + + pub fn build_canvas_keymap(&self) -> CanvasKeyMap { + CanvasKeyMap::from_mode_maps( + &self.keybindings.read_only, + &self.keybindings.edit, + &self.keybindings.highlight, + ) + } } + + diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 977f7e4..861e3c6 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -39,6 +39,7 @@ use crate::tui::{ }; use crate::ui::handlers::context::UiContext; use crate::ui::handlers::rat_state::UiStateHandler; +use canvas::KeyEventOutcome; use anyhow::Result; use common::proto::komp_ac::search::search_response::Hit; use crossterm::event::KeyModifiers; @@ -650,80 +651,29 @@ impl EventHandler { } AppMode::ReadOnly => { - // Handle highlight mode transitions (delegated to FormEditor) - if config.get_read_only_action_for_key(key_code, modifiers) - == Some("enter_highlight_mode_linewise") - && ModeManager::can_enter_highlight_mode(current_mode) - { + // First let the canvas editor try to handle the key + if app_state.ui.show_form { if let Some(editor) = &mut app_state.form_editor { - editor.enter_highlight_line_mode(); + match editor.handle_key_event(key_event) { + KeyEventOutcome::Consumed(Some(msg)) => { + return Ok(EventOutcome::Ok(msg)); + } + KeyEventOutcome::Consumed(None) => { + return Ok(EventOutcome::Ok(String::new())); + } + KeyEventOutcome::Pending => { + // Waiting for multi-key sequence (e.g. after pressing 'g') + return Ok(EventOutcome::Ok(String::new())); + } + KeyEventOutcome::NotMatched => { + // Fall through to client-level actions + } + } } - self.command_message = "-- LINE HIGHLIGHT --".to_string(); - return Ok(EventOutcome::Ok(self.command_message.clone())); - } else if config.get_read_only_action_for_key(key_code, modifiers) - == Some("enter_highlight_mode") - && ModeManager::can_enter_highlight_mode(current_mode) - { - if let Some(editor) = &mut app_state.form_editor { - editor.enter_highlight_mode(); - } - self.command_message = "-- HIGHLIGHT --".to_string(); - return Ok(EventOutcome::Ok(self.command_message.clone())); } - // Handle edit mode transitions - else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() - == Some("enter_edit_mode_before") - && ModeManager::can_enter_edit_mode(current_mode) - { - if let Some(editor) = &mut app_state.form_editor { - editor.enter_edit_mode(); - } - self.is_edit_mode = true; - self.edit_mode_cooldown = true; - self.command_message = "Edit mode".to_string(); - return Ok(EventOutcome::Ok(self.command_message.clone())); - } else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() - == Some("enter_edit_mode_after") - && ModeManager::can_enter_edit_mode(current_mode) - { - let current_input = Self::get_current_input_for_state( - app_state, - login_state, - register_state, - form_state, - ); - let current_cursor_pos = - Self::get_cursor_pos_for_mixed_state(app_state, login_state, form_state); - - // Move cursor forward if possible - if !current_input.is_empty() && current_cursor_pos < current_input.len() { - let new_cursor_pos = current_cursor_pos + 1; - Self::set_current_cursor_pos_for_state( - app_state, - login_state, - register_state, - form_state, - new_cursor_pos, - ); - self.ideal_cursor_column = Self::get_current_cursor_pos_for_state( - app_state, - login_state, - register_state, - form_state, - ); - } - - if let Some(editor) = &mut app_state.form_editor { - editor.enter_edit_mode(); - } - self.is_edit_mode = true; - self.edit_mode_cooldown = true; - app_state.ui.focus_outside_canvas = false; - self.command_message = "Edit mode (after cursor)".to_string(); - return Ok(EventOutcome::Ok(self.command_message.clone())); - } else if config.get_read_only_action_for_key(key_code, modifiers) - == Some("enter_command_mode") + // Entering command mode is still a client-level action + if config.get_app_action(key_code, modifiers) == Some("enter_command_mode") && ModeManager::can_enter_command_mode(current_mode) { if let Some(editor) = &mut app_state.form_editor { @@ -736,7 +686,7 @@ impl EventHandler { } // Handle common actions (save, quit, etc.) - if let Some(action) = config.get_common_action(key_code, modifiers) { + if let Some(action) = config.get_app_action(key_code, modifiers) { match action { "save" | "force_quit" | "save_and_quit" | "revert" => { return self @@ -755,37 +705,27 @@ impl EventHandler { } } - // Try canvas action for form first - if app_state.ui.show_form { - if let Some(editor) = &mut app_state.form_editor { - if let Ok(Some(canvas_message)) = - self.handle_form_canvas_action(key_event, editor, config, false).await - { - return Ok(EventOutcome::Ok(canvas_message)); - } - } - } - return Ok(EventOutcome::Ok(self.command_message.clone())); } AppMode::Highlight => { - if config.get_highlight_action_for_key(key_code, modifiers) - == Some("exit_highlight_mode") - { + if app_state.ui.show_form { if let Some(editor) = &mut app_state.form_editor { - editor.exit_highlight_mode(); + match editor.handle_key_event(key_event) { + KeyEventOutcome::Consumed(Some(msg)) => { + return Ok(EventOutcome::Ok(msg)); + } + KeyEventOutcome::Consumed(None) => { + return Ok(EventOutcome::Ok(String::new())); + } + KeyEventOutcome::Pending => { + return Ok(EventOutcome::Ok(String::new())); + } + KeyEventOutcome::NotMatched => { + // Fall through to client-level actions + } + } } - self.command_message = "Exited highlight mode".to_string(); - return Ok(EventOutcome::Ok(self.command_message.clone())); - } else if config.get_highlight_action_for_key(key_code, modifiers) - == Some("enter_highlight_mode_linewise") - { - if let Some(editor) = &mut app_state.form_editor { - editor.enter_highlight_line_mode(); - } - self.command_message = "-- LINE HIGHLIGHT --".to_string(); - return Ok(EventOutcome::Ok(self.command_message.clone())); } return Ok(EventOutcome::Ok(self.command_message.clone())); @@ -793,7 +733,7 @@ impl EventHandler { AppMode::Edit => { // Handle common actions (save, quit, etc.) - if let Some(action) = config.get_common_action(key_code, modifiers) { + if let Some(action) = config.get_app_action(key_code, modifiers) { match action { "save" | "force_quit" | "save_and_quit" | "revert" => { return self @@ -812,19 +752,24 @@ impl EventHandler { } } - // Try canvas action for form first + // Let the canvas editor handle edit-mode keys if app_state.ui.show_form { if let Some(editor) = &mut app_state.form_editor { - if let Ok(Some(canvas_message)) = self.handle_form_canvas_action( - key_event, - editor, - config, - true, - ).await { - if !canvas_message.is_empty() { - self.command_message = canvas_message.clone(); + match editor.handle_key_event(key_event) { + KeyEventOutcome::Consumed(Some(msg)) => { + self.command_message = msg.clone(); + return Ok(EventOutcome::Ok(msg)); + } + KeyEventOutcome::Consumed(None) => { + return Ok(EventOutcome::Ok(String::new())); + } + KeyEventOutcome::Pending => { + // Rare in edit mode, but allow multi-key sequences if defined + return Ok(EventOutcome::Ok(String::new())); + } + KeyEventOutcome::NotMatched => { + // Fall through to client-level actions } - return Ok(EventOutcome::Ok(canvas_message)); } } } @@ -973,66 +918,6 @@ impl EventHandler { matches!(command, "w" | "q" | "q!" | "wq" | "r") } - async fn handle_form_canvas_action( - &mut self, - key_event: KeyEvent, - editor: &mut FormEditor, - config: &Config, - is_edit_mode: bool, - ) -> Result> { - use crossterm::event::KeyCode; - - if is_edit_mode { - if let KeyCode::Char(c) = key_event.code { - if key_event.modifiers.is_empty() || key_event.modifiers == KeyModifiers::SHIFT { - editor.insert_char(c)?; - return Ok(Some(format!("Inserted '{}'", c))); - } - } - } - - // Use your config to resolve actions - if let Some(action) = config.get_edit_action_for_key(key_event.code, key_event.modifiers) { - match action { - "delete_char_backward" => { editor.delete_backward()?; return Ok(Some("Deleted backward".to_string())); } - "delete_char_forward" => { editor.delete_forward()?; return Ok(Some("Deleted forward".to_string())); } - "move_left" => { editor.move_left()?; return Ok(Some("Moved left".to_string())); } - "move_right" => { editor.move_right()?; return Ok(Some("Moved right".to_string())); } - "move_up" => { editor.move_up()?; return Ok(Some("Moved up".to_string())); } - "move_down" => { editor.move_down()?; return Ok(Some("Moved down".to_string())); } - - "move_line_start" => { editor.move_line_start(); return Ok(Some("Line start".to_string())); } - "move_line_end" => { editor.move_line_end(); return Ok(Some("Line end".to_string())); } - "move_word_next" => { editor.move_word_next(); return Ok(Some("Next word".to_string())); } - "move_word_prev" => { editor.move_word_prev(); return Ok(Some("Prev word".to_string())); } - "move_word_end" => { editor.move_word_end(); return Ok(Some("Word end".to_string())); } - "move_word_end_prev" => { editor.move_word_end_prev(); return Ok(Some("Prev word end".to_string())); } - - "next_field" => { editor.next_field()?; return Ok(Some("Next field".to_string())); } - "prev_field" => { editor.prev_field()?; return Ok(Some("Prev field".to_string())); } - "open_suggestions" => { - let field_index = editor.current_field(); - editor.open_suggestions(field_index); - return Ok(Some("Opened suggestions".to_string())); - } - "apply_suggestion" | "enter_decider" => { - if let Some(s) = editor.apply_suggestion() { - return Ok(Some(format!("Applied suggestion: {}", s))); - } else { - return Ok(Some("No suggestion applied".to_string())); - } - } - "exit" | "exit_edit_mode" => { - editor.exit_edit_mode()?; - return Ok(Some("Exited edit mode".to_string())); - } - _ => {} - } - } - - Ok(None) - } - async fn handle_core_action( &mut self, action: &str,