spliting the config file completely

This commit is contained in:
filipriec
2025-02-28 20:16:31 +01:00
parent 8f7350ea93
commit 9b290e1ad9
5 changed files with 139 additions and 155 deletions

View File

@@ -17,16 +17,25 @@ fn default_theme() -> String {
#[derive(Debug, Deserialize)]
pub struct Config {
pub keybindings: HashMap<String, Vec<String>>,
#[serde(rename = "keybindings")]
pub keybindings: ModeKeybindings,
#[serde(default)]
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 {
/// Loads the configuration from "config.toml" in the client crate directory.
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 config_path = Path::new(manifest_dir).join("config.toml");
let config_str = std::fs::read_to_string(&config_path)
@@ -35,32 +44,45 @@ impl 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,
mode_bindings: &HashMap<String, Vec<String>>,
key: KeyCode,
modifiers: KeyModifiers,
) -> Option<&str> {
for (action, bindings) in &self.keybindings {
// Skip mode toggle actions
if action == "enter_edit_mode" || action == "exit_edit_mode" {
continue;
}
for (action, bindings) in mode_bindings {
for binding in bindings {
if Self::matches_keybinding(binding, key, modifiers) {
return Some(action);
return Some(action.as_str());
}
}
}
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> {
if sequence.is_empty() {
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| {
if let KeyCode::Char(c) = key {
Some(*c)
@@ -68,46 +90,56 @@ impl Config {
None
}
}).collect();
if sequence_str.is_empty() {
return None;
}
// Check if this sequence matches any binding
for (action, bindings) in &self.keybindings {
// Check if this sequence matches any binding.
for (action, bindings) in &self.keybindings.read_only {
for binding in bindings {
// Skip bindings with modifiers (those contain '+')
if binding.contains('+') {
continue;
}
if binding == &sequence_str {
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
}
/// Checks if a keybinding matches a key and modifiers.
fn matches_keybinding(
binding: &str,
key: KeyCode,
modifiers: KeyModifiers,
) -> 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('+') {
return match binding.to_lowercase().as_str() {
"left" => key == KeyCode::Left,
"right" => key == KeyCode::Right,
"up" => key == KeyCode::Up,
"down" => key == KeyCode::Down,
"esc" => key == KeyCode::Esc,
"enter" => key == KeyCode::Enter,
_ => false,
};
return match binding.to_lowercase().as_str() {
"left" => key == KeyCode::Left,
"right" => key == KeyCode::Right,
"up" => key == KeyCode::Up,
"down" => key == KeyCode::Down,
"esc" => key == KeyCode::Esc,
"enter" => key == KeyCode::Enter,
_ => false,
};
}
let parts: Vec<&str> = binding.split('+').collect();
let mut expected_modifiers = KeyModifiers::empty();
let mut expected_key = None;
@@ -136,8 +168,9 @@ impl Config {
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> {
for (action, bindings) in &self.keybindings {
for (action, bindings) in &self.keybindings.command {
for binding in bindings {
if binding.starts_with(':') && binding.trim_start_matches(':') == command {
return Some(action);
@@ -147,51 +180,76 @@ impl Config {
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 {
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))
} else {
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 {
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))
} else {
false
}
}
/// Checks if a key is bound to entering Edit mode.
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_after(key, modifiers)
self.is_enter_edit_mode_before(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 {
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))
} else {
false
}
}
/// Checks if a key is bound to entering Command mode.
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))
} else {
false
}
}
/// Checks if a key is bound to exiting Command mode.
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))
} else {
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 {
if let Some(bindings) = self.keybindings.get(action) {
for binding in bindings {
@@ -203,7 +261,7 @@ impl Config {
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> {
if sequence.is_empty() {
return None;
@@ -255,7 +313,7 @@ impl Config {
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 {
if sequence.is_empty() {
return false;
@@ -300,22 +358,4 @@ impl Config {
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()
}
}
}