spliting the config file completely
This commit is contained in:
@@ -8,6 +8,7 @@ save_and_quit = [":wq", "ctrl+shift+s"]
|
|||||||
|
|
||||||
# MODE SPECIFIC
|
# MODE SPECIFIC
|
||||||
# READ ONLY MODE
|
# READ ONLY MODE
|
||||||
|
[keybindings.read_only]
|
||||||
enter_edit_mode_before = ["i"]
|
enter_edit_mode_before = ["i"]
|
||||||
enter_edit_mode_after = ["a"]
|
enter_edit_mode_after = ["a"]
|
||||||
previous_entry = ["left","q"]
|
previous_entry = ["left","q"]
|
||||||
@@ -26,14 +27,14 @@ move_line_end = ["$"]
|
|||||||
move_first_line = ["gg"]
|
move_first_line = ["gg"]
|
||||||
move_last_line = ["x"]
|
move_last_line = ["x"]
|
||||||
|
|
||||||
# EDIT MODE
|
[keybindings.edit]
|
||||||
exit_edit_mode = ["esc","ctrl+e"]
|
exit_edit_mode = ["esc","ctrl+e"]
|
||||||
delete_char_forward = ["delete"]
|
delete_char_forward = ["delete"]
|
||||||
delete_char_backward = ["backspace"]
|
delete_char_backward = ["backspace"]
|
||||||
next_field = ["tab", "enter"]
|
next_field = ["tab", "enter"]
|
||||||
prev_field = ["shift+tab", "backtab"]
|
prev_field = ["shift+tab", "backtab"]
|
||||||
|
|
||||||
# COMMAND MODE
|
[keybindings.command]
|
||||||
enter_command_mode = [":", "ctrl+;"]
|
enter_command_mode = [":", "ctrl+;"]
|
||||||
exit_command_mode = ["ctrl+g", "esc"]
|
exit_command_mode = ["ctrl+g", "esc"]
|
||||||
command_execute = ["enter"]
|
command_execute = ["enter"]
|
||||||
|
|||||||
@@ -17,16 +17,25 @@ fn default_theme() -> String {
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub keybindings: HashMap<String, Vec<String>>,
|
#[serde(rename = "keybindings")]
|
||||||
|
pub keybindings: ModeKeybindings,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub colors: ColorsConfig,
|
pub colors: ColorsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ModeKeybindings {
|
||||||
|
#[serde(default)]
|
||||||
|
pub read_only: HashMap<String, Vec<String>>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub edit: HashMap<String, Vec<String>>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub command: HashMap<String, Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Loads the configuration from "config.toml" in the client crate directory.
|
/// Loads the configuration from "config.toml" in the client crate directory.
|
||||||
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
|
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
// env!("CARGO_MANIFEST_DIR") resolves to the directory where the Cargo.toml of
|
|
||||||
// the client crate is located.
|
|
||||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||||
let config_path = Path::new(manifest_dir).join("config.toml");
|
let config_path = Path::new(manifest_dir).join("config.toml");
|
||||||
let config_str = std::fs::read_to_string(&config_path)
|
let config_str = std::fs::read_to_string(&config_path)
|
||||||
@@ -35,32 +44,45 @@ impl Config {
|
|||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_action_for_key(
|
/// Gets an action for a key in Read-Only mode.
|
||||||
|
pub fn get_read_only_action_for_key(&self, key: KeyCode, modifiers: KeyModifiers) -> Option<&str> {
|
||||||
|
self.get_action_for_key_in_mode(&self.keybindings.read_only, key, modifiers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets an action for a key in Edit mode.
|
||||||
|
pub fn get_edit_action_for_key(&self, key: KeyCode, modifiers: KeyModifiers) -> Option<&str> {
|
||||||
|
self.get_action_for_key_in_mode(&self.keybindings.edit, key, modifiers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets an action for a key in Command mode.
|
||||||
|
pub fn get_command_action_for_key(&self, key: KeyCode, modifiers: KeyModifiers) -> Option<&str> {
|
||||||
|
self.get_action_for_key_in_mode(&self.keybindings.command, key, modifiers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to get an action for a key in a specific mode.
|
||||||
|
fn get_action_for_key_in_mode(
|
||||||
&self,
|
&self,
|
||||||
|
mode_bindings: &HashMap<String, Vec<String>>,
|
||||||
key: KeyCode,
|
key: KeyCode,
|
||||||
modifiers: KeyModifiers,
|
modifiers: KeyModifiers,
|
||||||
) -> Option<&str> {
|
) -> Option<&str> {
|
||||||
for (action, bindings) in &self.keybindings {
|
for (action, bindings) in mode_bindings {
|
||||||
// Skip mode toggle actions
|
|
||||||
if action == "enter_edit_mode" || action == "exit_edit_mode" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for binding in bindings {
|
for binding in bindings {
|
||||||
if Self::matches_keybinding(binding, key, modifiers) {
|
if Self::matches_keybinding(binding, key, modifiers) {
|
||||||
return Some(action);
|
return Some(action.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a sequence of keys matches any keybinding
|
/// Checks if a sequence of keys matches any keybinding.
|
||||||
pub fn matches_key_sequence(&self, sequence: &[KeyCode]) -> Option<&str> {
|
pub fn matches_key_sequence(&self, sequence: &[KeyCode]) -> Option<&str> {
|
||||||
if sequence.is_empty() {
|
if sequence.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert key sequence to a string (for simple character sequences)
|
// Convert key sequence to a string (for simple character sequences).
|
||||||
let sequence_str: String = sequence.iter().filter_map(|key| {
|
let sequence_str: String = sequence.iter().filter_map(|key| {
|
||||||
if let KeyCode::Char(c) = key {
|
if let KeyCode::Char(c) = key {
|
||||||
Some(*c)
|
Some(*c)
|
||||||
@@ -68,46 +90,56 @@ impl Config {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
if sequence_str.is_empty() {
|
if sequence_str.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this sequence matches any binding
|
// Check if this sequence matches any binding.
|
||||||
for (action, bindings) in &self.keybindings {
|
for (action, bindings) in &self.keybindings.read_only {
|
||||||
for binding in bindings {
|
for binding in bindings {
|
||||||
// Skip bindings with modifiers (those contain '+')
|
|
||||||
if binding.contains('+') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if binding == &sequence_str {
|
if binding == &sequence_str {
|
||||||
return Some(action);
|
return Some(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (action, bindings) in &self.keybindings.edit {
|
||||||
|
for binding in bindings {
|
||||||
|
if binding == &sequence_str {
|
||||||
|
return Some(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (action, bindings) in &self.keybindings.command {
|
||||||
|
for binding in bindings {
|
||||||
|
if binding == &sequence_str {
|
||||||
|
return Some(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a keybinding matches a key and modifiers.
|
||||||
fn matches_keybinding(
|
fn matches_keybinding(
|
||||||
binding: &str,
|
binding: &str,
|
||||||
key: KeyCode,
|
key: KeyCode,
|
||||||
modifiers: KeyModifiers,
|
modifiers: KeyModifiers,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// For multi-character bindings without modifiers, we handle them in matches_key_sequence
|
// For multi-character bindings without modifiers, handle them in matches_key_sequence.
|
||||||
if binding.len() > 1 && !binding.contains('+') {
|
if binding.len() > 1 && !binding.contains('+') {
|
||||||
return match binding.to_lowercase().as_str() {
|
return match binding.to_lowercase().as_str() {
|
||||||
"left" => key == KeyCode::Left,
|
"left" => key == KeyCode::Left,
|
||||||
"right" => key == KeyCode::Right,
|
"right" => key == KeyCode::Right,
|
||||||
"up" => key == KeyCode::Up,
|
"up" => key == KeyCode::Up,
|
||||||
"down" => key == KeyCode::Down,
|
"down" => key == KeyCode::Down,
|
||||||
"esc" => key == KeyCode::Esc,
|
"esc" => key == KeyCode::Esc,
|
||||||
"enter" => key == KeyCode::Enter,
|
"enter" => key == KeyCode::Enter,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let parts: Vec<&str> = binding.split('+').collect();
|
let parts: Vec<&str> = binding.split('+').collect();
|
||||||
let mut expected_modifiers = KeyModifiers::empty();
|
let mut expected_modifiers = KeyModifiers::empty();
|
||||||
let mut expected_key = None;
|
let mut expected_key = None;
|
||||||
@@ -136,8 +168,9 @@ impl Config {
|
|||||||
modifiers == expected_modifiers && Some(key) == expected_key
|
modifiers == expected_modifiers && Some(key) == expected_key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets an action for a command string.
|
||||||
pub fn get_action_for_command(&self, command: &str) -> Option<&str> {
|
pub fn get_action_for_command(&self, command: &str) -> Option<&str> {
|
||||||
for (action, bindings) in &self.keybindings {
|
for (action, bindings) in &self.keybindings.command {
|
||||||
for binding in bindings {
|
for binding in bindings {
|
||||||
if binding.starts_with(':') && binding.trim_start_matches(':') == command {
|
if binding.starts_with(':') && binding.trim_start_matches(':') == command {
|
||||||
return Some(action);
|
return Some(action);
|
||||||
@@ -147,51 +180,76 @@ impl Config {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is bound to entering Edit mode (before cursor).
|
||||||
pub fn is_enter_edit_mode_before(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
pub fn is_enter_edit_mode_before(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
if let Some(bindings) = self.keybindings.get("enter_edit_mode_before") {
|
if let Some(bindings) = self.keybindings.read_only.get("enter_edit_mode_before") {
|
||||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is bound to entering Edit mode (after cursor).
|
||||||
pub fn is_enter_edit_mode_after(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
pub fn is_enter_edit_mode_after(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
if let Some(bindings) = self.keybindings.get("enter_edit_mode_after") {
|
if let Some(bindings) = self.keybindings.read_only.get("enter_edit_mode_after") {
|
||||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is bound to entering Edit mode.
|
||||||
pub fn is_enter_edit_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
pub fn is_enter_edit_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
self.is_enter_edit_mode_before(key, modifiers) ||
|
self.is_enter_edit_mode_before(key, modifiers) || self.is_enter_edit_mode_after(key, modifiers)
|
||||||
self.is_enter_edit_mode_after(key, modifiers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is bound to exiting Edit mode.
|
||||||
pub fn is_exit_edit_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
pub fn is_exit_edit_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
if let Some(bindings) = self.keybindings.get("exit_edit_mode") {
|
if let Some(bindings) = self.keybindings.edit.get("exit_edit_mode") {
|
||||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is bound to entering Command mode.
|
||||||
pub fn is_enter_command_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
pub fn is_enter_command_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
if let Some(bindings) = self.keybindings.get("enter_command_mode") {
|
if let Some(bindings) = self.keybindings.command.get("enter_command_mode") {
|
||||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is bound to exiting Command mode.
|
||||||
pub fn is_exit_command_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
pub fn is_exit_command_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
if let Some(bindings) = self.keybindings.get("exit_command_mode") {
|
if let Some(bindings) = self.keybindings.command.get("exit_command_mode") {
|
||||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is bound to executing a command.
|
||||||
|
pub fn is_command_execute(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
|
if let Some(bindings) = self.keybindings.command.get("command_execute") {
|
||||||
|
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||||
|
} else {
|
||||||
|
// Fall back to Enter key if no command_execute is defined.
|
||||||
|
key == KeyCode::Enter && modifiers.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is bound to backspacing in Command mode.
|
||||||
|
pub fn is_command_backspace(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
|
if let Some(bindings) = self.keybindings.command.get("command_backspace") {
|
||||||
|
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||||
|
} else {
|
||||||
|
// Fall back to Backspace key if no command_backspace is defined.
|
||||||
|
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 {
|
pub fn has_key_for_action(&self, action: &str, key_char: char) -> bool {
|
||||||
if let Some(bindings) = self.keybindings.get(action) {
|
if let Some(bindings) = self.keybindings.get(action) {
|
||||||
for binding in bindings {
|
for binding in bindings {
|
||||||
@@ -203,7 +261,7 @@ impl Config {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method handles all keybinding formats, both with and without +
|
/// This method handles all keybinding formats, both with and without +
|
||||||
pub fn matches_key_sequence_generalized(&self, sequence: &[KeyCode]) -> Option<&str> {
|
pub fn matches_key_sequence_generalized(&self, sequence: &[KeyCode]) -> Option<&str> {
|
||||||
if sequence.is_empty() {
|
if sequence.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
@@ -255,7 +313,7 @@ impl Config {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the current key sequence is a prefix of a longer binding
|
/// Check if the current key sequence is a prefix of a longer binding
|
||||||
pub fn is_key_sequence_prefix(&self, sequence: &[KeyCode]) -> bool {
|
pub fn is_key_sequence_prefix(&self, sequence: &[KeyCode]) -> bool {
|
||||||
if sequence.is_empty() {
|
if sequence.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
@@ -300,22 +358,4 @@ impl Config {
|
|||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_command_execute(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
|
||||||
if let Some(bindings) = self.keybindings.get("command_execute") {
|
|
||||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
|
||||||
} else {
|
|
||||||
// Fall back to Enter key if no command_execute is defined
|
|
||||||
key == KeyCode::Enter && modifiers.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_command_backspace(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
|
||||||
if let Some(bindings) = self.keybindings.get("command_backspace") {
|
|
||||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
|
||||||
} else {
|
|
||||||
// Fall back to Backspace key if no command_backspace is defined
|
|
||||||
key == KeyCode::Backspace && modifiers.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,52 +14,16 @@ pub async fn handle_edit_event_internal(
|
|||||||
ideal_cursor_column: &mut usize,
|
ideal_cursor_column: &mut usize,
|
||||||
command_message: &mut String,
|
command_message: &mut String,
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
// Try to match against configured action mappings first
|
// Try to match against configured Edit mode action mappings first
|
||||||
let mut key_sequence_tracker = KeySequenceTracker::new(800);
|
if let Some(action) = config.get_edit_action_for_key(key.code, key.modifiers) {
|
||||||
let mut handled_by_config = false;
|
return execute_edit_action(
|
||||||
|
action,
|
||||||
if key.modifiers.is_empty() {
|
form_state,
|
||||||
key_sequence_tracker.add_key(key.code);
|
ideal_cursor_column,
|
||||||
let sequence = key_sequence_tracker.get_sequence();
|
).await;
|
||||||
|
|
||||||
// Try to match the current sequence against all bindings
|
|
||||||
if let Some(action) = config.matches_key_sequence_generalized(&sequence) {
|
|
||||||
return execute_edit_action(
|
|
||||||
action,
|
|
||||||
form_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this might be a prefix of a longer sequence
|
|
||||||
if config.is_key_sequence_prefix(&sequence) {
|
|
||||||
// If it's a prefix, wait for more keys
|
|
||||||
return Ok(command_message.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since it's not part of a multi-key sequence, check for a direct action
|
|
||||||
if sequence.len() == 1 && !config.is_key_sequence_prefix(&sequence) {
|
|
||||||
// Try to handle it as a single key
|
|
||||||
if let Some(action) = config.get_action_for_key(key.code, key.modifiers) {
|
|
||||||
return execute_edit_action(
|
|
||||||
action,
|
|
||||||
form_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If modifiers are pressed, check for direct key bindings
|
|
||||||
if let Some(action) = config.get_action_for_key(key.code, key.modifiers) {
|
|
||||||
return execute_edit_action(
|
|
||||||
action,
|
|
||||||
form_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, no key mapping was found, so we handle fallback behavior
|
// If no Edit mode action is found, handle fallback behavior
|
||||||
handle_edit_specific_input(
|
handle_edit_specific_input(
|
||||||
key,
|
key,
|
||||||
form_state,
|
form_state,
|
||||||
@@ -69,39 +33,6 @@ pub async fn handle_edit_event_internal(
|
|||||||
Ok(command_message.clone())
|
Ok(command_message.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Original handle_edit_event for backward compatibility - not actually used anymore
|
|
||||||
// but kept for API compatibility
|
|
||||||
pub async fn handle_edit_event(
|
|
||||||
key: KeyEvent,
|
|
||||||
config: &Config,
|
|
||||||
form_state: &mut FormState,
|
|
||||||
_is_edit_mode: &mut bool,
|
|
||||||
edit_mode_cooldown: &mut bool,
|
|
||||||
ideal_cursor_column: &mut usize,
|
|
||||||
command_message: &mut String,
|
|
||||||
_command_mode: &mut bool,
|
|
||||||
_command_input: &mut String,
|
|
||||||
_app_terminal: &mut AppTerminal,
|
|
||||||
_is_saved: &mut bool,
|
|
||||||
_current_position: &mut u64,
|
|
||||||
_total_count: u64,
|
|
||||||
) -> Result<(bool, String), Box<dyn std::error::Error>> {
|
|
||||||
// This function should ideally not be called anymore - everything should go through event.rs
|
|
||||||
// Log a warning that this code path is deprecated
|
|
||||||
eprintln!("Warning: Deprecated code path in handle_edit_event - use event.rs instead");
|
|
||||||
|
|
||||||
let result = handle_edit_event_internal(
|
|
||||||
key,
|
|
||||||
config,
|
|
||||||
form_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
command_message,
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
*edit_mode_cooldown = false;
|
|
||||||
Ok((false, result))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle edit-specific key input as a fallback (character input, backspace, delete)
|
// Handle edit-specific key input as a fallback (character input, backspace, delete)
|
||||||
fn handle_edit_specific_input(
|
fn handle_edit_specific_input(
|
||||||
key: KeyEvent,
|
key: KeyEvent,
|
||||||
|
|||||||
@@ -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.is_exit_edit_mode(key.code, key.modifiers) {
|
if config.get_edit_action_for_key(key.code, key.modifiers) == Some("exit_edit_mode") {
|
||||||
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,8 +109,8 @@ 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.is_enter_edit_mode(key.code, key.modifiers) {
|
if config.get_read_only_action_for_key(key.code, key.modifiers) == Some("enter_edit_mode_before") {
|
||||||
if config.is_enter_edit_mode_after(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();
|
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;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// 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;
|
||||||
@@ -13,7 +11,6 @@ enum CharType {
|
|||||||
Punctuation,
|
Punctuation,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace your current handle_read_only_event with this generalized version
|
|
||||||
pub async fn handle_read_only_event(
|
pub async fn handle_read_only_event(
|
||||||
key: KeyEvent,
|
key: KeyEvent,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
@@ -26,13 +23,30 @@ pub async fn handle_read_only_event(
|
|||||||
edit_mode_cooldown: &mut bool,
|
edit_mode_cooldown: &mut bool,
|
||||||
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>> {
|
||||||
// Always add the key to the sequence tracker if no modifiers
|
// Check for entering Edit mode from Read-Only mode
|
||||||
// This tracks ALL keys, not just character keys
|
if config.get_read_only_action_for_key(key.code, key.modifiers) == Some("enter_edit_mode_before") {
|
||||||
|
*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") {
|
||||||
|
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;
|
||||||
|
*ideal_cursor_column = form_state.current_cursor_pos;
|
||||||
|
}
|
||||||
|
*edit_mode_cooldown = true;
|
||||||
|
*command_message = "Entering Edit mode (after cursor)".to_string();
|
||||||
|
return Ok((false, command_message.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Read-Only mode keybindings
|
||||||
if key.modifiers.is_empty() {
|
if key.modifiers.is_empty() {
|
||||||
key_sequence_tracker.add_key(key.code);
|
key_sequence_tracker.add_key(key.code);
|
||||||
let sequence = key_sequence_tracker.get_sequence();
|
let sequence = key_sequence_tracker.get_sequence();
|
||||||
|
|
||||||
// Try to match the current sequence against all bindings
|
// Try to match the current sequence against Read-Only mode bindings
|
||||||
if let Some(action) = config.matches_key_sequence_generalized(&sequence) {
|
if let Some(action) = config.matches_key_sequence_generalized(&sequence) {
|
||||||
let result = execute_action(
|
let result = execute_action(
|
||||||
action,
|
action,
|
||||||
@@ -50,14 +64,12 @@ pub async fn handle_read_only_event(
|
|||||||
|
|
||||||
// Check if this might be a prefix of a longer sequence
|
// Check if this might be a prefix of a longer sequence
|
||||||
if config.is_key_sequence_prefix(&sequence) {
|
if config.is_key_sequence_prefix(&sequence) {
|
||||||
// If it's a prefix, wait for more keys
|
|
||||||
return Ok((false, command_message.clone()));
|
return Ok((false, command_message.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since it's not part of a multi-key sequence, check for a direct action
|
// Since it's not part of a multi-key sequence, check for a direct action
|
||||||
if sequence.len() == 1 && !config.is_key_sequence_prefix(&sequence) {
|
if sequence.len() == 1 && !config.is_key_sequence_prefix(&sequence) {
|
||||||
// Try to handle it as a single key
|
if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers) {
|
||||||
if let Some(action) = config.get_action_for_key(key.code, key.modifiers) {
|
|
||||||
let result = execute_action(
|
let result = execute_action(
|
||||||
action,
|
action,
|
||||||
form_state,
|
form_state,
|
||||||
@@ -76,7 +88,7 @@ pub async fn handle_read_only_event(
|
|||||||
// If modifiers are pressed, check for direct key bindings
|
// If modifiers are pressed, check for direct key bindings
|
||||||
key_sequence_tracker.reset();
|
key_sequence_tracker.reset();
|
||||||
|
|
||||||
if let Some(action) = config.get_action_for_key(key.code, key.modifiers) {
|
if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers) {
|
||||||
let result = execute_action(
|
let result = execute_action(
|
||||||
action,
|
action,
|
||||||
form_state,
|
form_state,
|
||||||
@@ -94,7 +106,7 @@ pub async fn handle_read_only_event(
|
|||||||
// Show a helpful message when no binding was found
|
// Show a helpful message when no binding was found
|
||||||
if !*edit_mode_cooldown {
|
if !*edit_mode_cooldown {
|
||||||
let default_key = "i".to_string();
|
let default_key = "i".to_string();
|
||||||
let edit_key = config.keybindings.get("enter_edit_mode_before")
|
let edit_key = config.keybindings.read_only.get("enter_edit_mode_before")
|
||||||
.and_then(|keys| keys.first())
|
.and_then(|keys| keys.first())
|
||||||
.unwrap_or(&default_key);
|
.unwrap_or(&default_key);
|
||||||
*command_message = format!("Read-only mode - press {} to edit", edit_key);
|
*command_message = format!("Read-only mode - press {} to edit", edit_key);
|
||||||
|
|||||||
Reference in New Issue
Block a user