FIXED CRUCIAL BUG of two same shortcuts defined in the config

This commit is contained in:
filipriec
2025-04-15 22:28:12 +02:00
parent bc6471fa54
commit 91ad2b0caf
3 changed files with 141 additions and 108 deletions

View File

@@ -56,7 +56,9 @@ enter_highlight_mode = ["v"]
exit_highlight_mode = ["esc"] exit_highlight_mode = ["esc"]
[keybindings.edit] [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_forward = ["delete"]
delete_char_backward = ["backspace"] delete_char_backward = ["backspace"]
next_field = ["enter"] next_field = ["enter"]
@@ -66,7 +68,6 @@ move_right = ["right"]
suggestion_down = ["ctrl+n", "tab"] suggestion_down = ["ctrl+n", "tab"]
suggestion_up = ["ctrl+p", "shift+tab"] suggestion_up = ["ctrl+p", "shift+tab"]
select_suggestion = ["enter"] select_suggestion = ["enter"]
exit_suggestion_mode = ["esc"]
[keybindings.command] [keybindings.command]
exit_command_mode = ["ctrl+g", "esc"] exit_command_mode = ["ctrl+g", "esc"]

View File

@@ -1,14 +1,19 @@
// src/modes/canvas/edit.rs // src/modes/canvas/edit.rs
use crate::config::binds::config::Config; use crate::config::binds::config::Config;
use crate::services::grpc_client::GrpcClient; use crate::services::grpc_client::GrpcClient;
use crate::state::pages::{auth::{LoginState, RegisterState}}; use crate::state::pages::{auth::{LoginState, RegisterState}, canvas_state::CanvasState};
use crate::state::pages::canvas_state::CanvasState;
use crate::state::pages::form::FormState; use crate::state::pages::form::FormState;
use crate::functions::modes::edit::{auth_e, form_e};
use crate::modes::handlers::event::EventOutcome; use crate::modes::handlers::event::EventOutcome;
use crate::functions::modes::edit::{auth_e, form_e};
use crate::state::app::state::AppState; use crate::state::app::state::AppState;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; 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( pub async fn handle_edit_event(
key: KeyEvent, key: KeyEvent,
@@ -17,12 +22,12 @@ pub async fn handle_edit_event(
login_state: &mut LoginState, login_state: &mut LoginState,
register_state: &mut RegisterState, register_state: &mut RegisterState,
ideal_cursor_column: &mut usize, ideal_cursor_column: &mut usize,
command_message: &mut String, // command_message: &mut String, // Removed as messages are returned
current_position: &mut u64, current_position: &mut u64,
total_count: u64, total_count: u64,
grpc_client: &mut GrpcClient, grpc_client: &mut GrpcClient,
app_state: &AppState, app_state: &AppState,
) -> Result<String, Box<dyn std::error::Error>> { ) -> Result<EditEventOutcome, Box<dyn std::error::Error>> {
// Global command mode check // Global command mode check
if let Some("enter_command_mode") = config.get_action_for_key_in_mode( 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.code,
key.modifiers key.modifiers
) { ) {
*command_message = "Switching to Command Mode...".to_string(); return Ok(EditEventOutcome::Message("Switching to Command Mode...".to_string()));
return Ok(command_message.clone());
} }
// Common actions (save/revert) // Common actions (save/revert)
@@ -41,14 +45,15 @@ pub async fn handle_edit_event(
key.modifiers key.modifiers
) { ) {
if matches!(action, "save" | "revert") { if matches!(action, "save" | "revert") {
let message = if app_state.ui.show_login { // Ensure all branches result in Result<String, Error> before the final Ok(...) wrap
let message_string: String = if app_state.ui.show_login {
auth_e::execute_common_action( auth_e::execute_common_action(
action, action,
login_state, login_state,
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await? // Results in String on success
} else if app_state.ui.show_register { } else if app_state.ui.show_register {
auth_e::execute_common_action( auth_e::execute_common_action(
action, action,
@@ -56,46 +61,66 @@ pub async fn handle_edit_event(
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await? // Results in String on success
} else { } else {
form_e::execute_common_action( let outcome = form_e::execute_common_action(
action, action,
form_state, // Concrete FormState form_state,
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await?; // This returns EventOutcome on success
.map(|outcome| match outcome {
// Extract the message string from the EventOutcome
match outcome {
EventOutcome::Ok(msg) => msg, EventOutcome::Ok(msg) => msg,
EventOutcome::Exit(msg) => format!("Exit requested: {}", msg), EventOutcome::DataSaved(_, msg) => msg,
EventOutcome::DataSaved(save_outcome, msg) => format!("Data saved ({:?}): {}", save_outcome, msg), _ => format!("Unexpected outcome from common action: {:?}", outcome),
EventOutcome::ButtonSelected { context, index } => {
"Unexpected action in edit mode".to_string()
} }
}) };
}?; // Wrap the resulting String message
return Ok(message); return Ok(EditEventOutcome::Message(message_string));
} }
} }
// Edit-specific actions // Edit-specific actions
if let Some(action) = config.get_edit_action_for_key(key.code, key.modifiers) { 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<String, Error>
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 app_state.ui.show_register && register_state.current_field() == 4 {
if !register_state.in_suggestion_mode && key.code == KeyCode::Tab && key.modifiers == KeyModifiers::NONE { if !register_state.in_suggestion_mode && key.code == KeyCode::Tab && key.modifiers == KeyModifiers::NONE {
register_state.update_role_suggestions(); register_state.update_role_suggestions();
if !register_state.role_suggestions.is_empty() { if !register_state.role_suggestions.is_empty() {
register_state.in_suggestion_mode = true; register_state.in_suggestion_mode = true;
register_state.selected_suggestion_index = Some(0); // Select first suggestion register_state.selected_suggestion_index = Some(0);
return Ok("Suggestions shown".to_string()); return Ok(EditEventOutcome::Message("Suggestions shown".to_string()));
} else { } else { // Added else here for clarity
return Ok("No suggestions available".to_string()); 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( auth_e::execute_edit_action(
action, action,
key, key,
@@ -104,7 +129,7 @@ pub async fn handle_edit_event(
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await? // Results in String
} else if app_state.ui.show_register { } else if app_state.ui.show_register {
auth_e::execute_edit_action( auth_e::execute_edit_action(
action, action,
@@ -114,7 +139,7 @@ pub async fn handle_edit_event(
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await? // Results in String
} else { } else {
form_e::execute_edit_action( form_e::execute_edit_action(
action, action,
@@ -124,22 +149,22 @@ pub async fn handle_edit_event(
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await? // Results in String
}; };
// Wrap the resulting String message
return Ok(EditEventOutcome::Message(msg));
} }
// Character insertion // Character insertion
if let KeyCode::Char(_) = key.code { 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 { if app_state.ui.show_register && register_state.in_suggestion_mode {
register_state.in_suggestion_mode = false; register_state.in_suggestion_mode = false;
register_state.show_role_suggestions = false; register_state.show_role_suggestions = false;
register_state.selected_suggestion_index = None; 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( auth_e::execute_edit_action(
"insert_char", "insert_char",
key, key,
@@ -148,7 +173,7 @@ pub async fn handle_edit_event(
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await? // Results in String
} else if app_state.ui.show_register { } else if app_state.ui.show_register {
auth_e::execute_edit_action( auth_e::execute_edit_action(
"insert_char", "insert_char",
@@ -158,7 +183,7 @@ pub async fn handle_edit_event(
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await? // Results in String
} else { } else {
form_e::execute_edit_action( form_e::execute_edit_action(
"insert_char", "insert_char",
@@ -168,23 +193,23 @@ pub async fn handle_edit_event(
grpc_client, grpc_client,
current_position, current_position,
total_count 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 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 { if app_state.ui.show_register && register_state.in_suggestion_mode {
register_state.in_suggestion_mode = false; register_state.in_suggestion_mode = false;
register_state.show_role_suggestions = false; register_state.show_role_suggestions = false;
register_state.selected_suggestion_index = None; 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 action_str = if key.code == KeyCode::Backspace { "backspace" } else { "delete_char" };
let result = if app_state.ui.show_register { // Ensure both branches result in a String *before* wrapping
let result_msg: String = if app_state.ui.show_register {
auth_e::execute_edit_action( auth_e::execute_edit_action(
action_str, action_str,
key, key,
@@ -193,14 +218,17 @@ pub async fn handle_edit_event(
grpc_client, grpc_client,
current_position, current_position,
total_count total_count
).await ).await? // Results in String
} else { } else {
// Handle for login/form if needed, assuming auth_e covers RegisterState // Return String directly, not Ok(String)
Ok("Action not applicable here".to_string()) // Placeholder "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()))
} }

View File

@@ -341,55 +341,14 @@ impl EventHandler {
} }
AppMode::Edit => { AppMode::Edit => {
if config.get_edit_action_for_key(key_code, modifiers) == Some("exit_edit_mode") { // First, check for common actions (save, revert, etc.) that apply in Edit mode
self.is_edit_mode = false; // These might take precedence or have different behavior than the edit handler
self.edit_mode_cooldown = true; if let Some(action) = config.get_common_action(key_code, modifiers) {
// Handle common actions like save, revert, force_quit, save_and_quit
let has_changes = if app_state.ui.show_login || app_state.ui.show_register{ // Ensure these actions return EventOutcome directly if they might exit the app
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
) {
match action { match action {
"save" | "force_quit" | "save_and_quit" | "revert" => { "save" | "force_quit" | "save_and_quit" | "revert" => {
// This call likely returns EventOutcome, handle it directly
return common_mode::handle_core_action( return common_mode::handle_core_action(
action, action,
form_state, form_state,
@@ -404,27 +363,72 @@ impl EventHandler {
total_count, total_count,
).await; ).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, key,
config, config,
form_state, form_state,
login_state, login_state,
register_state, register_state,
&mut self.ideal_cursor_column, &mut self.ideal_cursor_column,
&mut self.command_message,
current_position, current_position,
total_count, total_count,
grpc_client, grpc_client,
app_state, app_state,
).await?; ).await;
self.key_sequence_tracker.reset(); match edit_result {
return Ok(EventOutcome::Ok(message)); 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 => { AppMode::Command => {
let outcome = command_mode::handle_command_event( let outcome = command_mode::handle_command_event(