From 55895cd4c5da3171addaa86f20a8a8f835b3d527 Mon Sep 17 00:00:00 2001 From: filipriec Date: Fri, 28 Feb 2025 20:39:24 +0100 Subject: [PATCH] working but some functionality is missing now --- client/config.toml | 2 + client/src/config/config.rs | 122 +++++++++++++++++++++---- client/src/modes/handlers/event.rs | 24 +++-- client/src/modes/handlers/read_only.rs | 6 +- 4 files changed, 126 insertions(+), 28 deletions(-) diff --git a/client/config.toml b/client/config.toml index ac169dc..922e2c0 100644 --- a/client/config.toml +++ b/client/config.toml @@ -6,6 +6,8 @@ quit = [":q", "ctrl+q"] force_quit = [":q!", "ctrl+shift+q"] save_and_quit = [":wq", "ctrl+shift+s"] +# [keybindings.common] + # MODE SPECIFIC # READ ONLY MODE [keybindings.read_only] diff --git a/client/src/config/config.rs b/client/src/config/config.rs index 22eef9f..cd01f0e 100644 --- a/client/src/config/config.rs +++ b/client/src/config/config.rs @@ -1,4 +1,4 @@ -// client/src/config.rs +// client/src/config/config.rs use serde::Deserialize; use std::collections::HashMap; @@ -31,6 +31,9 @@ pub struct ModeKeybindings { pub edit: HashMap>, #[serde(default)] pub command: HashMap>, + // Add other fields for standalone global keybindings as needed + #[serde(flatten)] + pub global: HashMap>, } impl Config { @@ -60,12 +63,12 @@ impl Config { } /// Helper function to get an action for a key in a specific mode. - fn get_action_for_key_in_mode( + fn get_action_for_key_in_mode<'a>( &self, - mode_bindings: &HashMap>, + mode_bindings: &'a HashMap>, key: KeyCode, modifiers: KeyModifiers, - ) -> Option<&str> { + ) -> Option<&'a str> { for (action, bindings) in mode_bindings { for binding in bindings { if Self::matches_keybinding(binding, key, modifiers) { @@ -95,7 +98,8 @@ impl Config { return None; } - // Check if this sequence matches any binding. + // Check if this sequence matches any binding across all modes. + // First check read_only mode for (action, bindings) in &self.keybindings.read_only { for binding in bindings { if binding == &sequence_str { @@ -103,6 +107,7 @@ impl Config { } } } + // Then check edit mode for (action, bindings) in &self.keybindings.edit { for binding in bindings { if binding == &sequence_str { @@ -110,6 +115,7 @@ impl Config { } } } + // Then check command mode for (action, bindings) in &self.keybindings.command { for binding in bindings { if binding == &sequence_str { @@ -117,6 +123,14 @@ impl Config { } } } + // Finally check global bindings + for (action, bindings) in &self.keybindings.global { + for binding in bindings { + if binding == &sequence_str { + return Some(action); + } + } + } None } @@ -249,15 +263,34 @@ impl Config { key == KeyCode::Backspace && modifiers.is_empty() } } + /// Checks if a key is bound to a specific action. pub fn has_key_for_action(&self, action: &str, key_char: char) -> bool { - if let Some(bindings) = self.keybindings.get(action) { - for binding in bindings { - if binding == &key_char.to_string() { - return true; - } + // Check all mode-specific keybindings for the action + if let Some(bindings) = self.keybindings.read_only.get(action) { + if bindings.iter().any(|binding| binding == &key_char.to_string()) { + return true; } } + + if let Some(bindings) = self.keybindings.edit.get(action) { + if bindings.iter().any(|binding| binding == &key_char.to_string()) { + return true; + } + } + + if let Some(bindings) = self.keybindings.command.get(action) { + if bindings.iter().any(|binding| binding == &key_char.to_string()) { + return true; + } + } + + if let Some(bindings) = self.keybindings.global.get(action) { + if bindings.iter().any(|binding| binding == &key_char.to_string()) { + return true; + } + } + false } @@ -279,8 +312,39 @@ impl Config { .collect::>() .join("+"); - // Check for matches in all binding formats - for (action, bindings) in &self.keybindings { + // Check for matches in all binding formats across all modes + // First check read_only mode + if let Some(action) = self.check_bindings_for_sequence(&self.keybindings.read_only, &sequence_str, &sequence_plus, sequence) { + return Some(action); + } + + // Then check edit mode + if let Some(action) = self.check_bindings_for_sequence(&self.keybindings.edit, &sequence_str, &sequence_plus, sequence) { + return Some(action); + } + + // Then check command mode + if let Some(action) = self.check_bindings_for_sequence(&self.keybindings.command, &sequence_str, &sequence_plus, sequence) { + return Some(action); + } + + // Finally check global bindings + if let Some(action) = self.check_bindings_for_sequence(&self.keybindings.global, &sequence_str, &sequence_plus, sequence) { + return Some(action); + } + + None + } + + /// Helper method to check a specific mode's bindings against a key sequence + fn check_bindings_for_sequence<'a>( + &self, + mode_bindings: &'a HashMap>, + sequence_str: &str, + sequence_plus: &str, + sequence: &[KeyCode] + ) -> Option<&'a str> { + for (action, bindings) in mode_bindings { for binding in bindings { let normalized_binding = binding.to_lowercase(); @@ -309,7 +373,6 @@ impl Config { } } } - None } @@ -325,13 +388,39 @@ impl Config { .collect::>() .join(""); - // Check all bindings to see if our sequence is a prefix - for (_, bindings) in &self.keybindings { + // Check in each mode if our sequence is a prefix + if self.is_prefix_in_mode(&self.keybindings.read_only, &sequence_str, sequence) { + return true; + } + + if self.is_prefix_in_mode(&self.keybindings.edit, &sequence_str, sequence) { + return true; + } + + if self.is_prefix_in_mode(&self.keybindings.command, &sequence_str, sequence) { + return true; + } + + if self.is_prefix_in_mode(&self.keybindings.global, &sequence_str, sequence) { + return true; + } + + false + } + + /// Helper method to check if a sequence is a prefix in a specific mode + fn is_prefix_in_mode( + &self, + mode_bindings: &HashMap>, + sequence_str: &str, + sequence: &[KeyCode] + ) -> bool { + for (_, bindings) in mode_bindings { for binding in bindings { let normalized_binding = binding.to_lowercase(); // Check standard format - if normalized_binding.starts_with(&sequence_str) && + if normalized_binding.starts_with(sequence_str) && normalized_binding.len() > sequence_str.len() { return true; } @@ -355,7 +444,6 @@ impl Config { } } } - false } } diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 36c850d..dfdc8dd 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -78,7 +78,7 @@ impl EventHandler { // Mode transitions between edit mode and read-only mode if self.is_edit_mode { // Check for exiting edit mode - if config.get_edit_action_for_key(key.code, key.modifiers) == Some("exit_edit_mode") { + if config.is_exit_edit_mode(key.code, key.modifiers) { if form_state.has_unsaved_changes { self.command_message = "Unsaved changes! Use :w to save or :q! to discard".to_string(); return Ok((false, self.command_message.clone())); @@ -109,20 +109,26 @@ impl EventHandler { return Ok((false, result)); } else { // Check for entering edit mode from read-only mode - if config.get_read_only_action_for_key(key.code, key.modifiers) == Some("enter_edit_mode_before") { - if config.get_read_only_action_for_key(key.code, key.modifiers) == Some("enter_edit_mode_after") { - let current_input = form_state.get_current_input(); - if !current_input.is_empty() && form_state.current_cursor_pos < current_input.len() { - form_state.current_cursor_pos += 1; - self.ideal_cursor_column = form_state.current_cursor_pos; - } - } + if config.is_enter_edit_mode_before(key.code, key.modifiers) { self.is_edit_mode = true; self.edit_mode_cooldown = true; self.command_message = "Edit mode".to_string(); app_terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?; return Ok((false, self.command_message.clone())); } + + if config.is_enter_edit_mode_after(key.code, key.modifiers) { + let current_input = form_state.get_current_input(); + if !current_input.is_empty() && form_state.current_cursor_pos < current_input.len() { + form_state.current_cursor_pos += 1; + self.ideal_cursor_column = form_state.current_cursor_pos; + } + self.is_edit_mode = true; + self.edit_mode_cooldown = true; + self.command_message = "Edit mode (after cursor)".to_string(); + app_terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?; + return Ok((false, self.command_message.clone())); + } // Handle read-only mode events return read_only::handle_read_only_event( diff --git a/client/src/modes/handlers/read_only.rs b/client/src/modes/handlers/read_only.rs index a15ed15..7753364 100644 --- a/client/src/modes/handlers/read_only.rs +++ b/client/src/modes/handlers/read_only.rs @@ -1,3 +1,5 @@ +// src/modes/handlers/read_only.rs + use crossterm::event::{KeyEvent}; use crate::config::config::Config; use crate::ui::handlers::form::FormState; @@ -24,13 +26,13 @@ pub async fn handle_read_only_event( ideal_cursor_column: &mut usize, ) -> Result<(bool, String), Box> { // Check for entering Edit mode from Read-Only mode - if config.get_read_only_action_for_key(key.code, key.modifiers) == Some("enter_edit_mode_before") { + if config.is_enter_edit_mode_before(key.code, key.modifiers) { *edit_mode_cooldown = true; *command_message = "Entering Edit mode".to_string(); return Ok((false, command_message.clone())); } - if config.get_read_only_action_for_key(key.code, key.modifiers) == Some("enter_edit_mode_after") { + if config.is_enter_edit_mode_after(key.code, key.modifiers) { let current_input = form_state.get_current_input(); if !current_input.is_empty() && form_state.current_cursor_pos < current_input.len() { form_state.current_cursor_pos += 1;