diff --git a/client/config.toml b/client/config.toml index 9032f47..ee41e7f 100644 --- a/client/config.toml +++ b/client/config.toml @@ -7,8 +7,8 @@ save_and_quit = [":wq", "ctrl+shift+s"] enter_edit_mode_before = ["i"] enter_edit_mode_after = ["a"] exit_edit_mode = ["esc", "ctrl+e"] -previous_position = ["Left", "9"] -next_position = ["Right"] +previous_entry = ["Left", "q"] # Changed from previous_position +next_entry = ["Right", "1"] # Changed from next_position move_left = ["h"] move_right = ["l"] @@ -21,7 +21,7 @@ move_word_end_prev = ["ge"] # Move to end of previous word move_line_start = ["0"] # Move to beginning of line move_line_end = ["$"] # Move to end of line move_first_line = ["gg"] # Move to first line of form -move_last_line = ["x", "8"] +move_last_line = ["x"] [colors] theme = "dark" diff --git a/client/src/modes/handlers/read_only.rs b/client/src/modes/handlers/read_only.rs index ad335c4..c1c884e 100644 --- a/client/src/modes/handlers/read_only.rs +++ b/client/src/modes/handlers/read_only.rs @@ -25,9 +25,93 @@ pub async fn handle_read_only_event( edit_mode_cooldown: &mut bool, ideal_cursor_column: &mut usize, ) -> Result<(bool, String), Box> { - // Handle navigation between entries - match key.code { - KeyCode::Left => { + // Handle key sequences and actions + if let KeyCode::Char(_) = key.code { + if key.modifiers.is_empty() { + key_sequence_tracker.add_key(key.code); + let sequence = key_sequence_tracker.get_sequence(); + let sequence_str = key_sequence_tracker.sequence_to_string(); + + if let Some(action) = config.matches_key_sequence(&sequence) { + let result = execute_action( + action, + form_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + current_position, + total_count, + app_terminal, + ).await?; + key_sequence_tracker.reset(); + return Ok((false, result)); + } + + if sequence.len() == 1 { + let is_prefix = is_prefix_of_multikey_binding(&sequence_str, config); + if !is_prefix { + if let Some(action) = config.get_action_for_key(key.code, key.modifiers) { + let result = execute_action( + action, + form_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + current_position, + total_count, + app_terminal, + ).await?; + key_sequence_tracker.reset(); + return Ok((false, result)); + } + } + } + } else { + key_sequence_tracker.reset(); + } + } else { + key_sequence_tracker.reset(); + + // Handle special keys through config + if let Some(action) = config.get_action_for_key(key.code, key.modifiers) { + let result = execute_action( + action, + form_state, + ideal_cursor_column, + key_sequence_tracker, + command_message, + current_position, + total_count, + app_terminal, + ).await?; + return Ok((false, result)); + } + + // Provide feedback when not in edit mode and cooldown expired + if !*edit_mode_cooldown { + let default_key = "i".to_string(); + let edit_key = config.keybindings.get("enter_edit_mode") + .and_then(|keys| keys.first()) + .unwrap_or(&default_key); + *command_message = format!("Read-only mode - press {} to edit", edit_key); + } + } + + Ok((false, command_message.clone())) +} + +async fn execute_action( + action: &str, + form_state: &mut FormState, + ideal_cursor_column: &mut usize, + key_sequence_tracker: &mut KeySequenceTracker, + command_message: &mut String, + current_position: &mut u64, + total_count: u64, + app_terminal: &mut AppTerminal, +) -> Result> { + match action { + "previous_entry" => { let new_position = current_position.saturating_sub(1); if new_position >= 1 { *current_position = new_position; @@ -45,7 +129,6 @@ pub async fn handle_read_only_event( let max_cursor_pos = if !current_input.is_empty() { current_input.len() - 1 } else { 0 }; - // Fix type mismatch by dereferencing and using std::cmp::min form_state.current_cursor_pos = std::cmp::min(*ideal_cursor_column, max_cursor_pos); form_state.has_unsaved_changes = false; *command_message = format!("Loaded entry {}", *current_position); @@ -56,9 +139,9 @@ pub async fn handle_read_only_event( } key_sequence_tracker.reset(); } - return Ok((false, command_message.clone())); + Ok(command_message.clone()) } - KeyCode::Right => { + "next_entry" => { if *current_position <= total_count { *current_position += 1; if *current_position <= total_count { @@ -76,7 +159,6 @@ pub async fn handle_read_only_event( let max_cursor_pos = if !current_input.is_empty() { current_input.len() - 1 } else { 0 }; - // Fix type mismatch form_state.current_cursor_pos = std::cmp::min(*ideal_cursor_column, max_cursor_pos); form_state.has_unsaved_changes = false; *command_message = format!("Loaded entry {}", *current_position); @@ -89,126 +171,31 @@ pub async fn handle_read_only_event( form_state.reset_to_empty(); form_state.current_field = 0; form_state.current_cursor_pos = 0; - *ideal_cursor_column = 0; // Reset ideal column as well + *ideal_cursor_column = 0; *command_message = "New entry mode".to_string(); } key_sequence_tracker.reset(); } - return Ok((false, command_message.clone())); + Ok(command_message.clone()) } - KeyCode::Esc => { - // Reset key sequence tracker and message on Escape + "exit_edit_mode" => { key_sequence_tracker.reset(); - *command_message = "".to_string(); - return Ok((false, "".to_string())); + command_message.clear(); + Ok("".to_string()) } - _ => {} - } - - // Handle key sequences and actions - if let KeyCode::Char(_) = key.code { - if key.modifiers.is_empty() { - key_sequence_tracker.add_key(key.code); - let sequence = key_sequence_tracker.get_sequence(); - let sequence_str = key_sequence_tracker.sequence_to_string(); - - if let Some(action) = config.matches_key_sequence(&sequence) { - let result = execute_action(action, form_state, ideal_cursor_column)?; - key_sequence_tracker.reset(); - return Ok((false, result)); - } - - if sequence.len() == 1 { - let is_prefix = is_prefix_of_multikey_binding(&sequence_str, config); - if !is_prefix { - if let Some(action) = config.get_action_for_key(key.code, key.modifiers) { - let result = execute_action(action, form_state, ideal_cursor_column)?; - key_sequence_tracker.reset(); - return Ok((false, result)); - } - } - } - } else { - key_sequence_tracker.reset(); - } - } else { - key_sequence_tracker.reset(); - - // Provide feedback when not in edit mode and cooldown expired - if !*edit_mode_cooldown { - let default_key = "i".to_string(); - let edit_key = config.keybindings.get("enter_edit_mode") - .and_then(|keys| keys.first()) - .unwrap_or(&default_key); - *command_message = format!("Read-only mode - press {} to edit", edit_key); - } - } - - Ok((false, command_message.clone())) -} - -fn execute_action( - action: &str, - form_state: &mut FormState, - ideal_cursor_column: &mut usize, -) -> Result> { - let current_input = form_state.get_current_input(); - let current_pos = form_state.current_cursor_pos; - - match action { "move_left" => { + let current_input = form_state.get_current_input(); + let current_pos = form_state.current_cursor_pos; form_state.current_cursor_pos = current_pos.saturating_sub(1); - *ideal_cursor_column = form_state.current_cursor_pos; // Update ideal column + *ideal_cursor_column = form_state.current_cursor_pos; Ok("".to_string()) } "move_right" => { + let current_input = form_state.get_current_input(); + let current_pos = form_state.current_cursor_pos; if !current_input.is_empty() && current_pos < current_input.len() - 1 { form_state.current_cursor_pos += 1; - *ideal_cursor_column = form_state.current_cursor_pos; // Update ideal column - } - Ok("".to_string()) - } - "move_word_next" => { - if !current_input.is_empty() { - let new_pos = find_next_word_start(current_input, current_pos); - form_state.current_cursor_pos = new_pos.min(current_input.len().saturating_sub(1)); - *ideal_cursor_column = form_state.current_cursor_pos; // Update ideal column - } - Ok("".to_string()) - } - "move_word_end" => { - if !current_input.is_empty() { - let new_pos = find_word_end(current_input, current_pos); - form_state.current_cursor_pos = new_pos.min(current_input.len().saturating_sub(1)); - *ideal_cursor_column = form_state.current_cursor_pos; // Update ideal column - } - Ok("".to_string()) - } - "move_word_prev" => { - if !current_input.is_empty() { - let new_pos = find_prev_word_start(current_input, current_pos); - form_state.current_cursor_pos = new_pos; - *ideal_cursor_column = form_state.current_cursor_pos; // Update ideal column - } - Ok("".to_string()) - } - "move_word_end_prev" => { - if !current_input.is_empty() { - let new_pos = find_prev_word_end(current_input, current_pos); - form_state.current_cursor_pos = new_pos; - *ideal_cursor_column = form_state.current_cursor_pos; // Update ideal column - } - Ok("Moved to previous word end".to_string()) - } - "move_line_start" => { - form_state.current_cursor_pos = 0; - *ideal_cursor_column = form_state.current_cursor_pos; // Update ideal column - Ok("".to_string()) - } - "move_line_end" => { - if !current_input.is_empty() { - form_state.current_cursor_pos = current_input.len() - 1; - *ideal_cursor_column = form_state.current_cursor_pos; // Update ideal column + *ideal_cursor_column = form_state.current_cursor_pos; } Ok("".to_string()) } @@ -227,8 +214,7 @@ fn execute_action( } else { 0 }; - // Fix type mismatch - form_state.current_cursor_pos = std::cmp::min(*ideal_cursor_column, max_cursor_pos); + form_state.current_cursor_pos = (*ideal_cursor_column).min(max_cursor_pos); Ok("".to_string()) } "move_down" => { @@ -242,8 +228,56 @@ fn execute_action( } else { 0 }; - // Fix type mismatch - form_state.current_cursor_pos = std::cmp::min(*ideal_cursor_column, max_cursor_pos); + form_state.current_cursor_pos = (*ideal_cursor_column).min(max_cursor_pos); + Ok("".to_string()) + } + "move_word_next" => { + let current_input = form_state.get_current_input(); + if !current_input.is_empty() { + let new_pos = find_next_word_start(current_input, form_state.current_cursor_pos); + form_state.current_cursor_pos = new_pos.min(current_input.len().saturating_sub(1)); + *ideal_cursor_column = form_state.current_cursor_pos; + } + Ok("".to_string()) + } + "move_word_end" => { + let current_input = form_state.get_current_input(); + if !current_input.is_empty() { + let new_pos = find_word_end(current_input, form_state.current_cursor_pos); + form_state.current_cursor_pos = new_pos.min(current_input.len().saturating_sub(1)); + *ideal_cursor_column = form_state.current_cursor_pos; + } + Ok("".to_string()) + } + "move_word_prev" => { + let current_input = form_state.get_current_input(); + if !current_input.is_empty() { + let new_pos = find_prev_word_start(current_input, form_state.current_cursor_pos); + form_state.current_cursor_pos = new_pos; + *ideal_cursor_column = form_state.current_cursor_pos; + } + Ok("".to_string()) + } + "move_word_end_prev" => { + let current_input = form_state.get_current_input(); + if !current_input.is_empty() { + let new_pos = find_prev_word_end(current_input, form_state.current_cursor_pos); + form_state.current_cursor_pos = new_pos; + *ideal_cursor_column = form_state.current_cursor_pos; + } + Ok("Moved to previous word end".to_string()) + } + "move_line_start" => { + form_state.current_cursor_pos = 0; + *ideal_cursor_column = form_state.current_cursor_pos; + Ok("".to_string()) + } + "move_line_end" => { + let current_input = form_state.get_current_input(); + if !current_input.is_empty() { + form_state.current_cursor_pos = current_input.len() - 1; + *ideal_cursor_column = form_state.current_cursor_pos; + } Ok("".to_string()) } "move_first_line" => { @@ -255,10 +289,9 @@ fn execute_action( let max_cursor_pos = if !current_input.is_empty() { current_input.len() - 1 } else { - 0 + current_input.len() }; - // Fix type mismatch - form_state.current_cursor_pos = std::cmp::min(*ideal_cursor_column, max_cursor_pos); + form_state.current_cursor_pos = (*ideal_cursor_column).min(max_cursor_pos); Ok("Moved to first line".to_string()) } "move_last_line" => { @@ -270,13 +303,12 @@ fn execute_action( let max_cursor_pos = if !current_input.is_empty() { current_input.len() - 1 } else { - 0 + current_input.len() }; - // Fix type mismatch - form_state.current_cursor_pos = std::cmp::min(*ideal_cursor_column, max_cursor_pos); + form_state.current_cursor_pos = (*ideal_cursor_column).min(max_cursor_pos); Ok("Moved to last line".to_string()) } - _ => Ok("Unknown action".to_string()), + _ => Ok(format!("Unknown action: {}", action)), } }