working but some functionality is missing now

This commit is contained in:
filipriec
2025-02-28 20:39:24 +01:00
parent 9b290e1ad9
commit 55895cd4c5
4 changed files with 126 additions and 28 deletions

View File

@@ -6,6 +6,8 @@ quit = [":q", "ctrl+q"]
force_quit = [":q!", "ctrl+shift+q"] force_quit = [":q!", "ctrl+shift+q"]
save_and_quit = [":wq", "ctrl+shift+s"] save_and_quit = [":wq", "ctrl+shift+s"]
# [keybindings.common]
# MODE SPECIFIC # MODE SPECIFIC
# READ ONLY MODE # READ ONLY MODE
[keybindings.read_only] [keybindings.read_only]

View File

@@ -1,4 +1,4 @@
// client/src/config.rs // client/src/config/config.rs
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
@@ -31,6 +31,9 @@ pub struct ModeKeybindings {
pub edit: HashMap<String, Vec<String>>, pub edit: HashMap<String, Vec<String>>,
#[serde(default)] #[serde(default)]
pub command: HashMap<String, Vec<String>>, pub command: HashMap<String, Vec<String>>,
// Add other fields for standalone global keybindings as needed
#[serde(flatten)]
pub global: HashMap<String, Vec<String>>,
} }
impl Config { impl Config {
@@ -60,12 +63,12 @@ impl Config {
} }
/// Helper function to get an action for a key in a specific mode. /// 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, &self,
mode_bindings: &HashMap<String, Vec<String>>, mode_bindings: &'a HashMap<String, Vec<String>>,
key: KeyCode, key: KeyCode,
modifiers: KeyModifiers, modifiers: KeyModifiers,
) -> Option<&str> { ) -> Option<&'a str> {
for (action, bindings) in mode_bindings { for (action, bindings) in mode_bindings {
for binding in bindings { for binding in bindings {
if Self::matches_keybinding(binding, key, modifiers) { if Self::matches_keybinding(binding, key, modifiers) {
@@ -95,7 +98,8 @@ impl Config {
return None; 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 (action, bindings) in &self.keybindings.read_only {
for binding in bindings { for binding in bindings {
if binding == &sequence_str { if binding == &sequence_str {
@@ -103,6 +107,7 @@ impl Config {
} }
} }
} }
// Then check edit mode
for (action, bindings) in &self.keybindings.edit { for (action, bindings) in &self.keybindings.edit {
for binding in bindings { for binding in bindings {
if binding == &sequence_str { if binding == &sequence_str {
@@ -110,6 +115,7 @@ impl Config {
} }
} }
} }
// Then check command mode
for (action, bindings) in &self.keybindings.command { for (action, bindings) in &self.keybindings.command {
for binding in bindings { for binding in bindings {
if binding == &sequence_str { 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 None
} }
@@ -249,15 +263,34 @@ impl Config {
key == KeyCode::Backspace && modifiers.is_empty() key == KeyCode::Backspace && modifiers.is_empty()
} }
} }
/// Checks if a key is bound to a specific action. /// Checks if a key is bound to a specific action.
pub fn has_key_for_action(&self, action: &str, key_char: char) -> bool { pub fn has_key_for_action(&self, action: &str, key_char: char) -> bool {
if let Some(bindings) = self.keybindings.get(action) { // Check all mode-specific keybindings for the action
for binding in bindings { if let Some(bindings) = self.keybindings.read_only.get(action) {
if binding == &key_char.to_string() { if bindings.iter().any(|binding| binding == &key_char.to_string()) {
return true; 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 false
} }
@@ -279,8 +312,39 @@ impl Config {
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("+"); .join("+");
// Check for matches in all binding formats // Check for matches in all binding formats across all modes
for (action, bindings) in &self.keybindings { // 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<String, Vec<String>>,
sequence_str: &str,
sequence_plus: &str,
sequence: &[KeyCode]
) -> Option<&'a str> {
for (action, bindings) in mode_bindings {
for binding in bindings { for binding in bindings {
let normalized_binding = binding.to_lowercase(); let normalized_binding = binding.to_lowercase();
@@ -309,7 +373,6 @@ impl Config {
} }
} }
} }
None None
} }
@@ -325,13 +388,39 @@ impl Config {
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(""); .join("");
// Check all bindings to see if our sequence is a prefix // Check in each mode if our sequence is a prefix
for (_, bindings) in &self.keybindings { 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<String, Vec<String>>,
sequence_str: &str,
sequence: &[KeyCode]
) -> bool {
for (_, bindings) in mode_bindings {
for binding in bindings { for binding in bindings {
let normalized_binding = binding.to_lowercase(); let normalized_binding = binding.to_lowercase();
// Check standard format // Check standard format
if normalized_binding.starts_with(&sequence_str) && if normalized_binding.starts_with(sequence_str) &&
normalized_binding.len() > sequence_str.len() { normalized_binding.len() > sequence_str.len() {
return true; return true;
} }
@@ -355,7 +444,6 @@ impl Config {
} }
} }
} }
false false
} }
} }

View File

@@ -78,7 +78,7 @@ impl EventHandler {
// Mode transitions between edit mode and read-only mode // Mode transitions between edit mode and read-only mode
if self.is_edit_mode { if self.is_edit_mode {
// Check for exiting 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 { if form_state.has_unsaved_changes {
self.command_message = "Unsaved changes! Use :w to save or :q! to discard".to_string(); self.command_message = "Unsaved changes! Use :w to save or :q! to discard".to_string();
return Ok((false, self.command_message.clone())); return Ok((false, self.command_message.clone()));
@@ -109,20 +109,26 @@ impl EventHandler {
return Ok((false, result)); return Ok((false, result));
} else { } else {
// Check for entering edit mode from read-only mode // 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) {
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;
}
}
self.is_edit_mode = true; self.is_edit_mode = true;
self.edit_mode_cooldown = true; self.edit_mode_cooldown = true;
self.command_message = "Edit mode".to_string(); self.command_message = "Edit mode".to_string();
app_terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?; app_terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
return Ok((false, self.command_message.clone())); 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 // Handle read-only mode events
return read_only::handle_read_only_event( return read_only::handle_read_only_event(

View File

@@ -1,3 +1,5 @@
// src/modes/handlers/read_only.rs
use crossterm::event::{KeyEvent}; use crossterm::event::{KeyEvent};
use crate::config::config::Config; use crate::config::config::Config;
use crate::ui::handlers::form::FormState; use crate::ui::handlers::form::FormState;
@@ -24,13 +26,13 @@ pub async fn handle_read_only_event(
ideal_cursor_column: &mut usize, ideal_cursor_column: &mut usize,
) -> Result<(bool, String), Box<dyn std::error::Error>> { ) -> Result<(bool, String), Box<dyn std::error::Error>> {
// Check for entering Edit mode from Read-Only mode // 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; *edit_mode_cooldown = true;
*command_message = "Entering Edit mode".to_string(); *command_message = "Entering Edit mode".to_string();
return Ok((false, command_message.clone())); 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(); let current_input = form_state.get_current_input();
if !current_input.is_empty() && form_state.current_cursor_pos < current_input.len() { if !current_input.is_empty() && form_state.current_cursor_pos < current_input.len() {
form_state.current_cursor_pos += 1; form_state.current_cursor_pos += 1;