From 91ad2b0caf85100cdbacd37ace55b0edaf10f2d8 Mon Sep 17 00:00:00 2001 From: filipriec Date: Tue, 15 Apr 2025 22:28:12 +0200 Subject: [PATCH] FIXED CRUCIAL BUG of two same shortcuts defined in the config --- client/config.toml | 5 +- client/src/modes/canvas/edit.rs | 134 +++++++++++++++++------------ client/src/modes/handlers/event.rs | 110 +++++++++++------------ 3 files changed, 141 insertions(+), 108 deletions(-) diff --git a/client/config.toml b/client/config.toml index 84de712..a2433d8 100644 --- a/client/config.toml +++ b/client/config.toml @@ -56,7 +56,9 @@ enter_highlight_mode = ["v"] exit_highlight_mode = ["esc"] [keybindings.edit] -exit_edit_mode = ["esc","ctrl+e"] +# exit_edit_mode = ["esc","ctrl+e"] +# exit_suggestion_mode = ["esc"] +exit = ["esc", "ctrl+e"] delete_char_forward = ["delete"] delete_char_backward = ["backspace"] next_field = ["enter"] @@ -66,7 +68,6 @@ move_right = ["right"] suggestion_down = ["ctrl+n", "tab"] suggestion_up = ["ctrl+p", "shift+tab"] select_suggestion = ["enter"] -exit_suggestion_mode = ["esc"] [keybindings.command] exit_command_mode = ["ctrl+g", "esc"] diff --git a/client/src/modes/canvas/edit.rs b/client/src/modes/canvas/edit.rs index 13af474..7a901d5 100644 --- a/client/src/modes/canvas/edit.rs +++ b/client/src/modes/canvas/edit.rs @@ -1,14 +1,19 @@ // src/modes/canvas/edit.rs - use crate::config::binds::config::Config; use crate::services::grpc_client::GrpcClient; -use crate::state::pages::{auth::{LoginState, RegisterState}}; -use crate::state::pages::canvas_state::CanvasState; +use crate::state::pages::{auth::{LoginState, RegisterState}, canvas_state::CanvasState}; use crate::state::pages::form::FormState; -use crate::functions::modes::edit::{auth_e, form_e}; use crate::modes::handlers::event::EventOutcome; +use crate::functions::modes::edit::{auth_e, form_e}; use crate::state::app::state::AppState; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +// Removed duplicate/unused imports + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EditEventOutcome { + Message(String), // Return a message, stay in Edit mode + ExitEditMode, // Signal to exit Edit mode +} pub async fn handle_edit_event( key: KeyEvent, @@ -17,12 +22,12 @@ pub async fn handle_edit_event( login_state: &mut LoginState, register_state: &mut RegisterState, ideal_cursor_column: &mut usize, - command_message: &mut String, + // command_message: &mut String, // Removed as messages are returned current_position: &mut u64, total_count: u64, grpc_client: &mut GrpcClient, app_state: &AppState, -) -> Result> { +) -> Result> { // Global command mode check if let Some("enter_command_mode") = config.get_action_for_key_in_mode( @@ -30,8 +35,7 @@ pub async fn handle_edit_event( key.code, key.modifiers ) { - *command_message = "Switching to Command Mode...".to_string(); - return Ok(command_message.clone()); + return Ok(EditEventOutcome::Message("Switching to Command Mode...".to_string())); } // Common actions (save/revert) @@ -41,14 +45,15 @@ pub async fn handle_edit_event( key.modifiers ) { if matches!(action, "save" | "revert") { - let message = if app_state.ui.show_login { + // Ensure all branches result in Result before the final Ok(...) wrap + let message_string: String = if app_state.ui.show_login { auth_e::execute_common_action( action, login_state, grpc_client, current_position, total_count - ).await + ).await? // Results in String on success } else if app_state.ui.show_register { auth_e::execute_common_action( action, @@ -56,46 +61,66 @@ pub async fn handle_edit_event( grpc_client, current_position, total_count - ).await + ).await? // Results in String on success } else { - form_e::execute_common_action( + let outcome = form_e::execute_common_action( action, - form_state, // Concrete FormState + form_state, grpc_client, current_position, total_count - ).await - .map(|outcome| match outcome { + ).await?; // This returns EventOutcome on success + + // Extract the message string from the EventOutcome + match outcome { EventOutcome::Ok(msg) => msg, - EventOutcome::Exit(msg) => format!("Exit requested: {}", msg), - EventOutcome::DataSaved(save_outcome, msg) => format!("Data saved ({:?}): {}", save_outcome, msg), - EventOutcome::ButtonSelected { context, index } => { - "Unexpected action in edit mode".to_string() - } - }) - }?; - return Ok(message); + EventOutcome::DataSaved(_, msg) => msg, + _ => format!("Unexpected outcome from common action: {:?}", outcome), + } + }; + // Wrap the resulting String message + return Ok(EditEventOutcome::Message(message_string)); } } // Edit-specific actions if let Some(action) = config.get_edit_action_for_key(key.code, key.modifiers) { - // --- Special Handling for Tab/Shift+Tab in Role Field --- + if action == "exit" { + if app_state.ui.show_register && register_state.in_suggestion_mode { + // Call the action, get Result + let msg = auth_e::execute_edit_action( + "exit_suggestion_mode", + key, + register_state, + ideal_cursor_column, + grpc_client, + current_position, + total_count, + ).await?; // Results in String on success + // Wrap the String message + return Ok(EditEventOutcome::Message(msg)); + } else { + // Signal exit + return Ok(EditEventOutcome::ExitEditMode); + } + } + + // Special handling for role field suggestions if app_state.ui.show_register && register_state.current_field() == 4 { if !register_state.in_suggestion_mode && key.code == KeyCode::Tab && key.modifiers == KeyModifiers::NONE { register_state.update_role_suggestions(); if !register_state.role_suggestions.is_empty() { register_state.in_suggestion_mode = true; - register_state.selected_suggestion_index = Some(0); // Select first suggestion - return Ok("Suggestions shown".to_string()); - } else { - return Ok("No suggestions available".to_string()); + register_state.selected_suggestion_index = Some(0); + return Ok(EditEventOutcome::Message("Suggestions shown".to_string())); + } else { // Added else here for clarity + return Ok(EditEventOutcome::Message("No suggestions available".to_string())); } } } - // --- End Special Handling --- - return if app_state.ui.show_login { + // Execute other edit actions + let msg = if app_state.ui.show_login { auth_e::execute_edit_action( action, key, @@ -104,7 +129,7 @@ pub async fn handle_edit_event( grpc_client, current_position, total_count - ).await + ).await? // Results in String } else if app_state.ui.show_register { auth_e::execute_edit_action( action, @@ -114,7 +139,7 @@ pub async fn handle_edit_event( grpc_client, current_position, total_count - ).await + ).await? // Results in String } else { form_e::execute_edit_action( action, @@ -124,22 +149,22 @@ pub async fn handle_edit_event( grpc_client, current_position, total_count - ).await + ).await? // Results in String }; + // Wrap the resulting String message + return Ok(EditEventOutcome::Message(msg)); } // Character insertion if let KeyCode::Char(_) = key.code { - // If in suggestion mode, exit it before inserting char if app_state.ui.show_register && register_state.in_suggestion_mode { register_state.in_suggestion_mode = false; register_state.show_role_suggestions = false; register_state.selected_suggestion_index = None; } - let is_role_field = app_state.ui.show_register && register_state.current_field() == 4; - // --- End Autocomplete Trigger --- - - return if app_state.ui.show_login { + + // Execute insert_char action + let msg = if app_state.ui.show_login { auth_e::execute_edit_action( "insert_char", key, @@ -148,7 +173,7 @@ pub async fn handle_edit_event( grpc_client, current_position, total_count - ).await + ).await? // Results in String } else if app_state.ui.show_register { auth_e::execute_edit_action( "insert_char", @@ -158,7 +183,7 @@ pub async fn handle_edit_event( grpc_client, current_position, total_count - ).await + ).await? // Results in String } else { form_e::execute_edit_action( "insert_char", @@ -168,23 +193,23 @@ pub async fn handle_edit_event( grpc_client, current_position, total_count - ).await + ).await? // Results in String }; + // Wrap the resulting String message + return Ok(EditEventOutcome::Message(msg)); } - // Handle Backspace/Delete for Autocomplete Trigger + // Handle Backspace/Delete if matches!(key.code, KeyCode::Backspace | KeyCode::Delete) { - // If in suggestion mode, exit it before deleting char if app_state.ui.show_register && register_state.in_suggestion_mode { register_state.in_suggestion_mode = false; register_state.show_role_suggestions = false; register_state.selected_suggestion_index = None; } - let is_role_field = app_state.ui.show_register && register_state.current_field() == 4; - let action_str = if key.code == KeyCode::Backspace { "backspace" } else { "delete_char" }; - // Execute the action first - let result = if app_state.ui.show_register { + let action_str = if key.code == KeyCode::Backspace { "backspace" } else { "delete_char" }; + // Ensure both branches result in a String *before* wrapping + let result_msg: String = if app_state.ui.show_register { auth_e::execute_edit_action( action_str, key, @@ -193,14 +218,17 @@ pub async fn handle_edit_event( grpc_client, current_position, total_count - ).await + ).await? // Results in String } else { - // Handle for login/form if needed, assuming auth_e covers RegisterState - Ok("Action not applicable here".to_string()) // Placeholder - }?; + // Return String directly, not Ok(String) + "Action not applicable here".to_string() + }; // Semicolon here ends the if/else expression - return Ok(result); + // Wrap the resulting String message + return Ok(EditEventOutcome::Message(result_msg)); } - Ok(command_message.clone()) + // Default return if no other handler matched + Ok(EditEventOutcome::Message("".to_string())) } + diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index c69b80d..0791ccf 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -341,55 +341,14 @@ impl EventHandler { } AppMode::Edit => { - if config.get_edit_action_for_key(key_code, modifiers) == Some("exit_edit_mode") { - self.is_edit_mode = false; - self.edit_mode_cooldown = true; - - let has_changes = if app_state.ui.show_login || app_state.ui.show_register{ - login_state.has_unsaved_changes() - } else { - form_state.has_unsaved_changes() - }; - - self.command_message = if has_changes { - "Exited edit mode (unsaved changes remain)".to_string() - } else { - "Read-only mode".to_string() - }; - - terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?; - - let current_input = if app_state.ui.show_login || app_state.ui.show_register{ - login_state.get_current_input() - } else { - form_state.get_current_input() - }; - let current_cursor_pos = if app_state.ui.show_login || app_state.ui.show_register{ - login_state.current_cursor_pos() - } else { - form_state.current_cursor_pos() - }; - - if !current_input.is_empty() && current_cursor_pos >= current_input.len() { - let new_pos = current_input.len() - 1; - if app_state.ui.show_login || app_state.ui.show_register{ - login_state.set_current_cursor_pos(new_pos); - self.ideal_cursor_column = login_state.current_cursor_pos(); - } else { - form_state.set_current_cursor_pos(new_pos); - self.ideal_cursor_column = form_state.current_cursor_pos(); - } - } - return Ok(EventOutcome::Ok(self.command_message.clone())); - } - - if let Some(action) = config.get_action_for_key_in_mode( - &config.keybindings.common, - key_code, - modifiers - ) { + // First, check for common actions (save, revert, etc.) that apply in Edit mode + // These might take precedence or have different behavior than the edit handler + if let Some(action) = config.get_common_action(key_code, modifiers) { + // Handle common actions like save, revert, force_quit, save_and_quit + // Ensure these actions return EventOutcome directly if they might exit the app match action { "save" | "force_quit" | "save_and_quit" | "revert" => { + // This call likely returns EventOutcome, handle it directly return common_mode::handle_core_action( action, form_state, @@ -404,27 +363,72 @@ impl EventHandler { total_count, ).await; }, + // Handle other common actions if necessary _ => {} } + // If a common action was handled but didn't return/exit, + // we might want to stop further processing for this key event. + // Depending on the action, you might return Ok(EventOutcome::Ok(...)) here. + // For now, assume common actions either exit or don't prevent further processing. } - let message = edit::handle_edit_event( + // If no common action took precedence, delegate to the edit-specific handler + let edit_result = edit::handle_edit_event( key, config, form_state, login_state, register_state, &mut self.ideal_cursor_column, - &mut self.command_message, current_position, total_count, grpc_client, app_state, - ).await?; + ).await; - self.key_sequence_tracker.reset(); - return Ok(EventOutcome::Ok(message)); - }, + match edit_result { + Ok(edit::EditEventOutcome::ExitEditMode) => { + // The edit handler signaled to exit the mode + self.is_edit_mode = false; + self.edit_mode_cooldown = true; + let has_changes = if app_state.ui.show_login { login_state.has_unsaved_changes() } + else if app_state.ui.show_register { register_state.has_unsaved_changes() } + else { form_state.has_unsaved_changes() }; + self.command_message = if has_changes { + "Exited edit mode (unsaved changes remain)".to_string() + } else { + "Read-only mode".to_string() + }; + terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?; + // Adjust cursor position if needed + let current_input = if app_state.ui.show_login { login_state.get_current_input() } + else if app_state.ui.show_register { register_state.get_current_input() } + else { form_state.get_current_input() }; + let current_cursor_pos = if app_state.ui.show_login { login_state.current_cursor_pos() } + else if app_state.ui.show_register { register_state.current_cursor_pos() } + else { form_state.current_cursor_pos() }; + if !current_input.is_empty() && current_cursor_pos >= current_input.len() { + let new_pos = current_input.len() - 1; + let target_state: &mut dyn CanvasState = if app_state.ui.show_login { login_state } else if app_state.ui.show_register { register_state } else { form_state }; + target_state.set_current_cursor_pos(new_pos); + self.ideal_cursor_column = new_pos; + } + return Ok(EventOutcome::Ok(self.command_message.clone())); + } + Ok(edit::EditEventOutcome::Message(msg)) => { + // Stay in edit mode, update message if not empty + if !msg.is_empty() { + self.command_message = msg; + } + self.key_sequence_tracker.reset(); // Reset sequence tracker on successful edit action + return Ok(EventOutcome::Ok(self.command_message.clone())); + } + Err(e) => { + // Handle error from the edit handler + return Err(e); + } + } + }, // End AppMode::Edit AppMode::Command => { let outcome = command_mode::handle_command_event(