config now finally works
This commit is contained in:
@@ -226,50 +226,219 @@ impl CanvasConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn matches_keybinding(&self, binding: &str, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
fn matches_keybinding(&self, binding: &str, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||||
// Handle multi-character bindings (like "gg")
|
// Special handling for shift+character combinations
|
||||||
|
if binding.to_lowercase().starts_with("shift+") {
|
||||||
|
let parts: Vec<&str> = binding.split('+').collect();
|
||||||
|
if parts.len() == 2 && parts[1].len() == 1 {
|
||||||
|
let expected_lowercase = parts[1].chars().next().unwrap().to_lowercase().next().unwrap();
|
||||||
|
let expected_uppercase = expected_lowercase.to_uppercase().next().unwrap();
|
||||||
|
if let KeyCode::Char(actual_char) = key {
|
||||||
|
if actual_char == expected_uppercase && modifiers.contains(KeyModifiers::SHIFT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Shift+Tab -> BackTab
|
||||||
|
if binding.to_lowercase() == "shift+tab" && key == KeyCode::BackTab && modifiers.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle multi-character bindings (all standard keys without modifiers)
|
||||||
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() {
|
||||||
|
// Navigation keys
|
||||||
"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,
|
|
||||||
"enter" => key == KeyCode::Enter,
|
|
||||||
"delete" => key == KeyCode::Delete,
|
|
||||||
"backspace" => key == KeyCode::Backspace,
|
|
||||||
"tab" => key == KeyCode::Tab,
|
|
||||||
"home" => key == KeyCode::Home,
|
"home" => key == KeyCode::Home,
|
||||||
"end" => key == KeyCode::End,
|
"end" => key == KeyCode::End,
|
||||||
"gg" => false, // Multi-key sequences need special handling
|
"pageup" | "pgup" => key == KeyCode::PageUp,
|
||||||
_ => false,
|
"pagedown" | "pgdn" => key == KeyCode::PageDown,
|
||||||
|
|
||||||
|
// Editing keys
|
||||||
|
"insert" | "ins" => key == KeyCode::Insert,
|
||||||
|
"delete" | "del" => key == KeyCode::Delete,
|
||||||
|
"backspace" => key == KeyCode::Backspace,
|
||||||
|
|
||||||
|
// Tab keys
|
||||||
|
"tab" => key == KeyCode::Tab,
|
||||||
|
"backtab" => key == KeyCode::BackTab,
|
||||||
|
|
||||||
|
// Special keys
|
||||||
|
"enter" | "return" => key == KeyCode::Enter,
|
||||||
|
"escape" | "esc" => key == KeyCode::Esc,
|
||||||
|
"space" => key == KeyCode::Char(' '),
|
||||||
|
|
||||||
|
// Function keys F1-F24
|
||||||
|
"f1" => key == KeyCode::F(1),
|
||||||
|
"f2" => key == KeyCode::F(2),
|
||||||
|
"f3" => key == KeyCode::F(3),
|
||||||
|
"f4" => key == KeyCode::F(4),
|
||||||
|
"f5" => key == KeyCode::F(5),
|
||||||
|
"f6" => key == KeyCode::F(6),
|
||||||
|
"f7" => key == KeyCode::F(7),
|
||||||
|
"f8" => key == KeyCode::F(8),
|
||||||
|
"f9" => key == KeyCode::F(9),
|
||||||
|
"f10" => key == KeyCode::F(10),
|
||||||
|
"f11" => key == KeyCode::F(11),
|
||||||
|
"f12" => key == KeyCode::F(12),
|
||||||
|
"f13" => key == KeyCode::F(13),
|
||||||
|
"f14" => key == KeyCode::F(14),
|
||||||
|
"f15" => key == KeyCode::F(15),
|
||||||
|
"f16" => key == KeyCode::F(16),
|
||||||
|
"f17" => key == KeyCode::F(17),
|
||||||
|
"f18" => key == KeyCode::F(18),
|
||||||
|
"f19" => key == KeyCode::F(19),
|
||||||
|
"f20" => key == KeyCode::F(20),
|
||||||
|
"f21" => key == KeyCode::F(21),
|
||||||
|
"f22" => key == KeyCode::F(22),
|
||||||
|
"f23" => key == KeyCode::F(23),
|
||||||
|
"f24" => key == KeyCode::F(24),
|
||||||
|
|
||||||
|
// Lock keys (may not work reliably in all terminals)
|
||||||
|
"capslock" => key == KeyCode::CapsLock,
|
||||||
|
"scrolllock" => key == KeyCode::ScrollLock,
|
||||||
|
"numlock" => key == KeyCode::NumLock,
|
||||||
|
|
||||||
|
// System keys
|
||||||
|
"printscreen" => key == KeyCode::PrintScreen,
|
||||||
|
"pause" => key == KeyCode::Pause,
|
||||||
|
"menu" => key == KeyCode::Menu,
|
||||||
|
"keypadbegin" => key == KeyCode::KeypadBegin,
|
||||||
|
|
||||||
|
// Media keys (rarely supported but included for completeness)
|
||||||
|
"mediaplay" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Play),
|
||||||
|
"mediapause" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Pause),
|
||||||
|
"mediaplaypause" => key == KeyCode::Media(crossterm::event::MediaKeyCode::PlayPause),
|
||||||
|
"mediareverse" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Reverse),
|
||||||
|
"mediastop" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Stop),
|
||||||
|
"mediafastforward" => key == KeyCode::Media(crossterm::event::MediaKeyCode::FastForward),
|
||||||
|
"mediarewind" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Rewind),
|
||||||
|
"mediatracknext" => key == KeyCode::Media(crossterm::event::MediaKeyCode::TrackNext),
|
||||||
|
"mediatrackprevious" => key == KeyCode::Media(crossterm::event::MediaKeyCode::TrackPrevious),
|
||||||
|
"mediarecord" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Record),
|
||||||
|
"medialowervolume" => key == KeyCode::Media(crossterm::event::MediaKeyCode::LowerVolume),
|
||||||
|
"mediaraisevolume" => key == KeyCode::Media(crossterm::event::MediaKeyCode::RaiseVolume),
|
||||||
|
"mediamutevolume" => key == KeyCode::Media(crossterm::event::MediaKeyCode::MuteVolume),
|
||||||
|
|
||||||
|
// Modifier keys (these work better as part of combinations)
|
||||||
|
"leftshift" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::LeftShift),
|
||||||
|
"leftcontrol" | "leftctrl" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::LeftControl),
|
||||||
|
"leftalt" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::LeftAlt),
|
||||||
|
"leftsuper" | "leftwindows" | "leftcmd" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::LeftSuper),
|
||||||
|
"lefthyper" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::LeftHyper),
|
||||||
|
"leftmeta" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::LeftMeta),
|
||||||
|
"rightshift" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::RightShift),
|
||||||
|
"rightcontrol" | "rightctrl" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::RightControl),
|
||||||
|
"rightalt" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::RightAlt),
|
||||||
|
"rightsuper" | "rightwindows" | "rightcmd" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::RightSuper),
|
||||||
|
"righthyper" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::RightHyper),
|
||||||
|
"rightmeta" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::RightMeta),
|
||||||
|
"isolevel3shift" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::IsoLevel3Shift),
|
||||||
|
"isolevel5shift" => key == KeyCode::Modifier(crossterm::event::ModifierKeyCode::IsoLevel5Shift),
|
||||||
|
|
||||||
|
// Multi-key sequences need special handling
|
||||||
|
"gg" => false, // This needs sequence handling
|
||||||
|
_ => {
|
||||||
|
// Handle single characters and punctuation
|
||||||
|
if binding.len() == 1 {
|
||||||
|
if let Some(c) = binding.chars().next() {
|
||||||
|
key == KeyCode::Char(c)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle modifier combinations (like "Ctrl+p")
|
// Handle modifier combinations (like "Ctrl+F5", "Alt+Shift+A")
|
||||||
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;
|
||||||
|
|
||||||
for part in parts {
|
for part in parts {
|
||||||
match part.to_lowercase().as_str() {
|
match part.to_lowercase().as_str() {
|
||||||
"ctrl" => expected_modifiers |= KeyModifiers::CONTROL,
|
// Modifiers
|
||||||
|
"ctrl" | "control" => expected_modifiers |= KeyModifiers::CONTROL,
|
||||||
"shift" => expected_modifiers |= KeyModifiers::SHIFT,
|
"shift" => expected_modifiers |= KeyModifiers::SHIFT,
|
||||||
"alt" => expected_modifiers |= KeyModifiers::ALT,
|
"alt" => expected_modifiers |= KeyModifiers::ALT,
|
||||||
|
"super" | "windows" | "cmd" => expected_modifiers |= KeyModifiers::SUPER,
|
||||||
|
"hyper" => expected_modifiers |= KeyModifiers::HYPER,
|
||||||
|
"meta" => expected_modifiers |= KeyModifiers::META,
|
||||||
|
|
||||||
|
// Navigation keys
|
||||||
"left" => expected_key = Some(KeyCode::Left),
|
"left" => expected_key = Some(KeyCode::Left),
|
||||||
"right" => expected_key = Some(KeyCode::Right),
|
"right" => expected_key = Some(KeyCode::Right),
|
||||||
"up" => expected_key = Some(KeyCode::Up),
|
"up" => expected_key = Some(KeyCode::Up),
|
||||||
"down" => expected_key = Some(KeyCode::Down),
|
"down" => expected_key = Some(KeyCode::Down),
|
||||||
"esc" => expected_key = Some(KeyCode::Esc),
|
|
||||||
"enter" => expected_key = Some(KeyCode::Enter),
|
|
||||||
"delete" => expected_key = Some(KeyCode::Delete),
|
|
||||||
"backspace" => expected_key = Some(KeyCode::Backspace),
|
|
||||||
"tab" => expected_key = Some(KeyCode::Tab),
|
|
||||||
"home" => expected_key = Some(KeyCode::Home),
|
"home" => expected_key = Some(KeyCode::Home),
|
||||||
"end" => expected_key = Some(KeyCode::End),
|
"end" => expected_key = Some(KeyCode::End),
|
||||||
|
"pageup" | "pgup" => expected_key = Some(KeyCode::PageUp),
|
||||||
|
"pagedown" | "pgdn" => expected_key = Some(KeyCode::PageDown),
|
||||||
|
|
||||||
|
// Editing keys
|
||||||
|
"insert" | "ins" => expected_key = Some(KeyCode::Insert),
|
||||||
|
"delete" | "del" => expected_key = Some(KeyCode::Delete),
|
||||||
|
"backspace" => expected_key = Some(KeyCode::Backspace),
|
||||||
|
|
||||||
|
// Tab keys
|
||||||
|
"tab" => expected_key = Some(KeyCode::Tab),
|
||||||
|
"backtab" => expected_key = Some(KeyCode::BackTab),
|
||||||
|
|
||||||
|
// Special keys
|
||||||
|
"enter" | "return" => expected_key = Some(KeyCode::Enter),
|
||||||
|
"escape" | "esc" => expected_key = Some(KeyCode::Esc),
|
||||||
|
"space" => expected_key = Some(KeyCode::Char(' ')),
|
||||||
|
|
||||||
|
// Function keys
|
||||||
|
"f1" => expected_key = Some(KeyCode::F(1)),
|
||||||
|
"f2" => expected_key = Some(KeyCode::F(2)),
|
||||||
|
"f3" => expected_key = Some(KeyCode::F(3)),
|
||||||
|
"f4" => expected_key = Some(KeyCode::F(4)),
|
||||||
|
"f5" => expected_key = Some(KeyCode::F(5)),
|
||||||
|
"f6" => expected_key = Some(KeyCode::F(6)),
|
||||||
|
"f7" => expected_key = Some(KeyCode::F(7)),
|
||||||
|
"f8" => expected_key = Some(KeyCode::F(8)),
|
||||||
|
"f9" => expected_key = Some(KeyCode::F(9)),
|
||||||
|
"f10" => expected_key = Some(KeyCode::F(10)),
|
||||||
|
"f11" => expected_key = Some(KeyCode::F(11)),
|
||||||
|
"f12" => expected_key = Some(KeyCode::F(12)),
|
||||||
|
"f13" => expected_key = Some(KeyCode::F(13)),
|
||||||
|
"f14" => expected_key = Some(KeyCode::F(14)),
|
||||||
|
"f15" => expected_key = Some(KeyCode::F(15)),
|
||||||
|
"f16" => expected_key = Some(KeyCode::F(16)),
|
||||||
|
"f17" => expected_key = Some(KeyCode::F(17)),
|
||||||
|
"f18" => expected_key = Some(KeyCode::F(18)),
|
||||||
|
"f19" => expected_key = Some(KeyCode::F(19)),
|
||||||
|
"f20" => expected_key = Some(KeyCode::F(20)),
|
||||||
|
"f21" => expected_key = Some(KeyCode::F(21)),
|
||||||
|
"f22" => expected_key = Some(KeyCode::F(22)),
|
||||||
|
"f23" => expected_key = Some(KeyCode::F(23)),
|
||||||
|
"f24" => expected_key = Some(KeyCode::F(24)),
|
||||||
|
|
||||||
|
// Lock keys
|
||||||
|
"capslock" => expected_key = Some(KeyCode::CapsLock),
|
||||||
|
"scrolllock" => expected_key = Some(KeyCode::ScrollLock),
|
||||||
|
"numlock" => expected_key = Some(KeyCode::NumLock),
|
||||||
|
|
||||||
|
// System keys
|
||||||
|
"printscreen" => expected_key = Some(KeyCode::PrintScreen),
|
||||||
|
"pause" => expected_key = Some(KeyCode::Pause),
|
||||||
|
"menu" => expected_key = Some(KeyCode::Menu),
|
||||||
|
"keypadbegin" => expected_key = Some(KeyCode::KeypadBegin),
|
||||||
|
|
||||||
|
// Single character (letters, numbers, punctuation)
|
||||||
part => {
|
part => {
|
||||||
if part.len() == 1 {
|
if part.len() == 1 {
|
||||||
let c = part.chars().next().unwrap();
|
if let Some(c) = part.chars().next() {
|
||||||
expected_key = Some(KeyCode::Char(c));
|
expected_key = Some(KeyCode::Char(c));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ move_word_end_prev = ["ge"]
|
|||||||
move_line_start = ["0"]
|
move_line_start = ["0"]
|
||||||
move_line_end = ["$"]
|
move_line_end = ["$"]
|
||||||
move_first_line = ["gg"]
|
move_first_line = ["gg"]
|
||||||
move_last_line = ["G"]
|
move_last_line = ["CapsLock"]
|
||||||
next_field = ["Tab"]
|
next_field = ["Tab"]
|
||||||
prev_field = ["Shift+Tab"]
|
prev_field = ["Shift+Tab"]
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
[keybindings]
|
[keybindings]
|
||||||
|
|
||||||
enter_command_mode = [":", "ctrl+;"]
|
enter_command_mode = [":", "ctrl+;"]
|
||||||
next_buffer = ["ctrl+l"]
|
next_buffer = ["space+b+n"]
|
||||||
previous_buffer = ["ctrl+h"]
|
previous_buffer = ["space+b+p"]
|
||||||
close_buffer = ["ctrl+k"]
|
close_buffer = ["space+b+d"]
|
||||||
|
|
||||||
[keybindings.general]
|
[keybindings.general]
|
||||||
move_up = ["k", "Up"]
|
move_up = ["k", "Up"]
|
||||||
@@ -22,8 +22,6 @@ open_search = ["ctrl+f"]
|
|||||||
[keybindings.common]
|
[keybindings.common]
|
||||||
save = ["ctrl+s"]
|
save = ["ctrl+s"]
|
||||||
quit = ["ctrl+q"]
|
quit = ["ctrl+q"]
|
||||||
# !!!change to space b r in the future and from edit mode
|
|
||||||
revert = ["ctrl+r"]
|
|
||||||
|
|
||||||
force_quit = ["ctrl+shift+q"]
|
force_quit = ["ctrl+shift+q"]
|
||||||
save_and_quit = ["ctrl+shift+s"]
|
save_and_quit = ["ctrl+shift+s"]
|
||||||
@@ -39,6 +37,7 @@ enter_edit_mode_before = ["i"]
|
|||||||
enter_edit_mode_after = ["a"]
|
enter_edit_mode_after = ["a"]
|
||||||
previous_entry = ["left","q"]
|
previous_entry = ["left","q"]
|
||||||
next_entry = ["right","1"]
|
next_entry = ["right","1"]
|
||||||
|
revert = ["space+b+r"]
|
||||||
|
|
||||||
move_left = ["h"]
|
move_left = ["h"]
|
||||||
move_right = ["l"]
|
move_right = ["l"]
|
||||||
|
|||||||
@@ -251,47 +251,206 @@ impl Config {
|
|||||||
key: KeyCode,
|
key: KeyCode,
|
||||||
modifiers: KeyModifiers,
|
modifiers: KeyModifiers,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// For multi-character bindings without modifiers, handle them in matches_key_sequence.
|
// Special handling for shift+character combinations
|
||||||
|
if binding.to_lowercase().starts_with("shift+") {
|
||||||
|
let parts: Vec<&str> = binding.split('+').collect();
|
||||||
|
if parts.len() == 2 && parts[1].len() == 1 {
|
||||||
|
let expected_lowercase = parts[1].chars().next().unwrap().to_lowercase().next().unwrap();
|
||||||
|
let expected_uppercase = expected_lowercase.to_uppercase().next().unwrap();
|
||||||
|
if let KeyCode::Char(actual_char) = key {
|
||||||
|
if actual_char == expected_uppercase && modifiers.contains(KeyModifiers::SHIFT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Shift+Tab -> BackTab
|
||||||
|
if binding.to_lowercase() == "shift+tab" && key == KeyCode::BackTab && modifiers.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle multi-character bindings (all standard keys without modifiers)
|
||||||
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() {
|
||||||
|
// Navigation keys
|
||||||
"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,
|
"home" => key == KeyCode::Home,
|
||||||
"enter" => key == KeyCode::Enter,
|
"end" => key == KeyCode::End,
|
||||||
"delete" => key == KeyCode::Delete,
|
"pageup" | "pgup" => key == KeyCode::PageUp,
|
||||||
|
"pagedown" | "pgdn" => key == KeyCode::PageDown,
|
||||||
|
|
||||||
|
// Editing keys
|
||||||
|
"insert" | "ins" => key == KeyCode::Insert,
|
||||||
|
"delete" | "del" => key == KeyCode::Delete,
|
||||||
"backspace" => key == KeyCode::Backspace,
|
"backspace" => key == KeyCode::Backspace,
|
||||||
|
|
||||||
|
// Tab keys
|
||||||
"tab" => key == KeyCode::Tab,
|
"tab" => key == KeyCode::Tab,
|
||||||
"backtab" => key == KeyCode::BackTab,
|
"backtab" => key == KeyCode::BackTab,
|
||||||
_ => false,
|
|
||||||
|
// Special keys
|
||||||
|
"enter" | "return" => key == KeyCode::Enter,
|
||||||
|
"escape" | "esc" => key == KeyCode::Esc,
|
||||||
|
"space" => key == KeyCode::Char(' '),
|
||||||
|
|
||||||
|
// Function keys F1-F24
|
||||||
|
"f1" => key == KeyCode::F(1),
|
||||||
|
"f2" => key == KeyCode::F(2),
|
||||||
|
"f3" => key == KeyCode::F(3),
|
||||||
|
"f4" => key == KeyCode::F(4),
|
||||||
|
"f5" => key == KeyCode::F(5),
|
||||||
|
"f6" => key == KeyCode::F(6),
|
||||||
|
"f7" => key == KeyCode::F(7),
|
||||||
|
"f8" => key == KeyCode::F(8),
|
||||||
|
"f9" => key == KeyCode::F(9),
|
||||||
|
"f10" => key == KeyCode::F(10),
|
||||||
|
"f11" => key == KeyCode::F(11),
|
||||||
|
"f12" => key == KeyCode::F(12),
|
||||||
|
"f13" => key == KeyCode::F(13),
|
||||||
|
"f14" => key == KeyCode::F(14),
|
||||||
|
"f15" => key == KeyCode::F(15),
|
||||||
|
"f16" => key == KeyCode::F(16),
|
||||||
|
"f17" => key == KeyCode::F(17),
|
||||||
|
"f18" => key == KeyCode::F(18),
|
||||||
|
"f19" => key == KeyCode::F(19),
|
||||||
|
"f20" => key == KeyCode::F(20),
|
||||||
|
"f21" => key == KeyCode::F(21),
|
||||||
|
"f22" => key == KeyCode::F(22),
|
||||||
|
"f23" => key == KeyCode::F(23),
|
||||||
|
"f24" => key == KeyCode::F(24),
|
||||||
|
|
||||||
|
// Lock keys
|
||||||
|
"capslock" => key == KeyCode::CapsLock,
|
||||||
|
"scrolllock" => key == KeyCode::ScrollLock,
|
||||||
|
"numlock" => key == KeyCode::NumLock,
|
||||||
|
|
||||||
|
// System keys
|
||||||
|
"printscreen" => key == KeyCode::PrintScreen,
|
||||||
|
"pause" => key == KeyCode::Pause,
|
||||||
|
"menu" => key == KeyCode::Menu,
|
||||||
|
"keypadbegin" => key == KeyCode::KeypadBegin,
|
||||||
|
|
||||||
|
// Media keys
|
||||||
|
"mediaplay" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Play),
|
||||||
|
"mediapause" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Pause),
|
||||||
|
"mediaplaypause" => key == KeyCode::Media(crossterm::event::MediaKeyCode::PlayPause),
|
||||||
|
"mediareverse" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Reverse),
|
||||||
|
"mediastop" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Stop),
|
||||||
|
"mediafastforward" => key == KeyCode::Media(crossterm::event::MediaKeyCode::FastForward),
|
||||||
|
"mediarewind" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Rewind),
|
||||||
|
"mediatracknext" => key == KeyCode::Media(crossterm::event::MediaKeyCode::TrackNext),
|
||||||
|
"mediatrackprevious" => key == KeyCode::Media(crossterm::event::MediaKeyCode::TrackPrevious),
|
||||||
|
"mediarecord" => key == KeyCode::Media(crossterm::event::MediaKeyCode::Record),
|
||||||
|
"medialowervolume" => key == KeyCode::Media(crossterm::event::MediaKeyCode::LowerVolume),
|
||||||
|
"mediaraisevolume" => key == KeyCode::Media(crossterm::event::MediaKeyCode::RaiseVolume),
|
||||||
|
"mediamutevolume" => key == KeyCode::Media(crossterm::event::MediaKeyCode::MuteVolume),
|
||||||
|
|
||||||
|
// Multi-key sequences need special handling
|
||||||
|
"gg" => false, // This needs sequence handling
|
||||||
|
_ => {
|
||||||
|
// Handle single characters and punctuation
|
||||||
|
if binding.len() == 1 {
|
||||||
|
if let Some(c) = binding.chars().next() {
|
||||||
|
key == KeyCode::Char(c)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle modifier combinations (like "Ctrl+F5", "Alt+Shift+A")
|
||||||
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;
|
||||||
|
|
||||||
for part in parts {
|
for part in parts {
|
||||||
match part.to_lowercase().as_str() {
|
match part.to_lowercase().as_str() {
|
||||||
"ctrl" => expected_modifiers |= KeyModifiers::CONTROL,
|
// Modifiers
|
||||||
|
"ctrl" | "control" => expected_modifiers |= KeyModifiers::CONTROL,
|
||||||
"shift" => expected_modifiers |= KeyModifiers::SHIFT,
|
"shift" => expected_modifiers |= KeyModifiers::SHIFT,
|
||||||
"alt" => expected_modifiers |= KeyModifiers::ALT,
|
"alt" => expected_modifiers |= KeyModifiers::ALT,
|
||||||
|
"super" | "windows" | "cmd" => expected_modifiers |= KeyModifiers::SUPER,
|
||||||
|
"hyper" => expected_modifiers |= KeyModifiers::HYPER,
|
||||||
|
"meta" => expected_modifiers |= KeyModifiers::META,
|
||||||
|
|
||||||
|
// Navigation keys
|
||||||
"left" => expected_key = Some(KeyCode::Left),
|
"left" => expected_key = Some(KeyCode::Left),
|
||||||
"right" => expected_key = Some(KeyCode::Right),
|
"right" => expected_key = Some(KeyCode::Right),
|
||||||
"up" => expected_key = Some(KeyCode::Up),
|
"up" => expected_key = Some(KeyCode::Up),
|
||||||
"down" => expected_key = Some(KeyCode::Down),
|
"down" => expected_key = Some(KeyCode::Down),
|
||||||
"esc" => expected_key = Some(KeyCode::Esc),
|
"home" => expected_key = Some(KeyCode::Home),
|
||||||
"enter" => expected_key = Some(KeyCode::Enter),
|
"end" => expected_key = Some(KeyCode::End),
|
||||||
"delete" => expected_key = Some(KeyCode::Delete),
|
"pageup" | "pgup" => expected_key = Some(KeyCode::PageUp),
|
||||||
|
"pagedown" | "pgdn" => expected_key = Some(KeyCode::PageDown),
|
||||||
|
|
||||||
|
// Editing keys
|
||||||
|
"insert" | "ins" => expected_key = Some(KeyCode::Insert),
|
||||||
|
"delete" | "del" => expected_key = Some(KeyCode::Delete),
|
||||||
"backspace" => expected_key = Some(KeyCode::Backspace),
|
"backspace" => expected_key = Some(KeyCode::Backspace),
|
||||||
|
|
||||||
|
// Tab keys
|
||||||
"tab" => expected_key = Some(KeyCode::Tab),
|
"tab" => expected_key = Some(KeyCode::Tab),
|
||||||
"backtab" => expected_key = Some(KeyCode::BackTab),
|
"backtab" => expected_key = Some(KeyCode::BackTab),
|
||||||
|
|
||||||
|
// Special keys
|
||||||
|
"enter" | "return" => expected_key = Some(KeyCode::Enter),
|
||||||
|
"escape" | "esc" => expected_key = Some(KeyCode::Esc),
|
||||||
|
"space" => expected_key = Some(KeyCode::Char(' ')),
|
||||||
|
|
||||||
|
// Function keys
|
||||||
|
"f1" => expected_key = Some(KeyCode::F(1)),
|
||||||
|
"f2" => expected_key = Some(KeyCode::F(2)),
|
||||||
|
"f3" => expected_key = Some(KeyCode::F(3)),
|
||||||
|
"f4" => expected_key = Some(KeyCode::F(4)),
|
||||||
|
"f5" => expected_key = Some(KeyCode::F(5)),
|
||||||
|
"f6" => expected_key = Some(KeyCode::F(6)),
|
||||||
|
"f7" => expected_key = Some(KeyCode::F(7)),
|
||||||
|
"f8" => expected_key = Some(KeyCode::F(8)),
|
||||||
|
"f9" => expected_key = Some(KeyCode::F(9)),
|
||||||
|
"f10" => expected_key = Some(KeyCode::F(10)),
|
||||||
|
"f11" => expected_key = Some(KeyCode::F(11)),
|
||||||
|
"f12" => expected_key = Some(KeyCode::F(12)),
|
||||||
|
"f13" => expected_key = Some(KeyCode::F(13)),
|
||||||
|
"f14" => expected_key = Some(KeyCode::F(14)),
|
||||||
|
"f15" => expected_key = Some(KeyCode::F(15)),
|
||||||
|
"f16" => expected_key = Some(KeyCode::F(16)),
|
||||||
|
"f17" => expected_key = Some(KeyCode::F(17)),
|
||||||
|
"f18" => expected_key = Some(KeyCode::F(18)),
|
||||||
|
"f19" => expected_key = Some(KeyCode::F(19)),
|
||||||
|
"f20" => expected_key = Some(KeyCode::F(20)),
|
||||||
|
"f21" => expected_key = Some(KeyCode::F(21)),
|
||||||
|
"f22" => expected_key = Some(KeyCode::F(22)),
|
||||||
|
"f23" => expected_key = Some(KeyCode::F(23)),
|
||||||
|
"f24" => expected_key = Some(KeyCode::F(24)),
|
||||||
|
|
||||||
|
// Lock keys
|
||||||
|
"capslock" => expected_key = Some(KeyCode::CapsLock),
|
||||||
|
"scrolllock" => expected_key = Some(KeyCode::ScrollLock),
|
||||||
|
"numlock" => expected_key = Some(KeyCode::NumLock),
|
||||||
|
|
||||||
|
// System keys
|
||||||
|
"printscreen" => expected_key = Some(KeyCode::PrintScreen),
|
||||||
|
"pause" => expected_key = Some(KeyCode::Pause),
|
||||||
|
"menu" => expected_key = Some(KeyCode::Menu),
|
||||||
|
"keypadbegin" => expected_key = Some(KeyCode::KeypadBegin),
|
||||||
|
|
||||||
|
// Special characters and colon (legacy support)
|
||||||
":" => expected_key = Some(KeyCode::Char(':')),
|
":" => expected_key = Some(KeyCode::Char(':')),
|
||||||
|
|
||||||
|
// Single character (letters, numbers, punctuation)
|
||||||
part => {
|
part => {
|
||||||
if part.len() == 1 {
|
if part.len() == 1 {
|
||||||
let c = part.chars().next().unwrap();
|
if let Some(c) = part.chars().next() {
|
||||||
expected_key = Some(KeyCode::Char(c));
|
expected_key = Some(KeyCode::Char(c));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,14 +149,17 @@ fn parse_key_part(part: &str) -> Option<ParsedKey> {
|
|||||||
let mut code = None;
|
let mut code = None;
|
||||||
|
|
||||||
if part.contains('+') {
|
if part.contains('+') {
|
||||||
// This handles modifiers like "ctrl+s"
|
// This handles modifiers like "ctrl+s", "super+shift+f5"
|
||||||
let components: Vec<&str> = part.split('+').collect();
|
let components: Vec<&str> = part.split('+').collect();
|
||||||
|
|
||||||
for component in components {
|
for component in components {
|
||||||
match component.to_lowercase().as_str() {
|
match component.to_lowercase().as_str() {
|
||||||
"ctrl" => modifiers |= KeyModifiers::CONTROL,
|
"ctrl" | "control" => modifiers |= KeyModifiers::CONTROL,
|
||||||
"shift" => modifiers |= KeyModifiers::SHIFT,
|
"shift" => modifiers |= KeyModifiers::SHIFT,
|
||||||
"alt" => modifiers |= KeyModifiers::ALT,
|
"alt" => modifiers |= KeyModifiers::ALT,
|
||||||
|
"super" | "windows" | "cmd" => modifiers |= KeyModifiers::SUPER,
|
||||||
|
"hyper" => modifiers |= KeyModifiers::HYPER,
|
||||||
|
"meta" => modifiers |= KeyModifiers::META,
|
||||||
_ => {
|
_ => {
|
||||||
// Last component is the key
|
// Last component is the key
|
||||||
code = string_to_keycode(component);
|
code = string_to_keycode(component);
|
||||||
|
|||||||
@@ -1024,12 +1024,24 @@ impl EventHandler {
|
|||||||
async fn handle_form_canvas_action(
|
async fn handle_form_canvas_action(
|
||||||
&mut self,
|
&mut self,
|
||||||
key_event: KeyEvent,
|
key_event: KeyEvent,
|
||||||
config: &Config,
|
_config: &Config, // Not used anymore - canvas has its own config
|
||||||
form_state: &mut FormState,
|
form_state: &mut FormState,
|
||||||
is_edit_mode: bool,
|
is_edit_mode: bool,
|
||||||
) -> Result<Option<String>> {
|
) -> Result<Option<String>> {
|
||||||
|
// Load canvas config (canvas_config.toml or vim defaults)
|
||||||
|
let canvas_config = canvas::config::CanvasConfig::load();
|
||||||
|
|
||||||
// Handle suggestion actions first if suggestions are active
|
// Handle suggestion actions first if suggestions are active
|
||||||
if form_state.autocomplete_active {
|
if form_state.autocomplete_active {
|
||||||
|
if let Some(action_str) = canvas_config.get_suggestion_action(key_event.code, key_event.modifiers) {
|
||||||
|
let canvas_action = CanvasAction::from_string(&action_str);
|
||||||
|
match ActionDispatcher::dispatch(canvas_action, form_state, &mut self.ideal_cursor_column).await {
|
||||||
|
Ok(result) => return Ok(Some(result.message().unwrap_or("").to_string())),
|
||||||
|
Err(_) => return Ok(Some("Suggestion action failed".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback hardcoded suggestion handling
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
KeyCode::Up => {
|
KeyCode::Up => {
|
||||||
if let Ok(result) = ActionDispatcher::dispatch(
|
if let Ok(result) = ActionDispatcher::dispatch(
|
||||||
@@ -1071,15 +1083,16 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check config mappings FIRST, before CanvasAction::from_key
|
// FIXED: Use canvas config instead of client config
|
||||||
let action_str = if is_edit_mode {
|
let action_str = canvas_config.get_action_for_key(
|
||||||
config.get_edit_action_for_key(key_event.code, key_event.modifiers)
|
key_event.code,
|
||||||
} else {
|
key_event.modifiers,
|
||||||
config.get_read_only_action_for_key(key_event.code, key_event.modifiers)
|
is_edit_mode,
|
||||||
};
|
form_state.autocomplete_active
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(action_str) = action_str {
|
if let Some(action_str) = action_str {
|
||||||
// FIXED: Filter out mode transition actions - let legacy handlers deal with these
|
// Filter out mode transition actions - let legacy handlers deal with these
|
||||||
if Self::is_mode_transition_action(action_str) {
|
if Self::is_mode_transition_action(action_str) {
|
||||||
return Ok(None); // Let legacy handler handle mode transitions
|
return Ok(None); // Let legacy handler handle mode transitions
|
||||||
}
|
}
|
||||||
@@ -1094,14 +1107,13 @@ impl EventHandler {
|
|||||||
return Ok(Some(result.message().unwrap_or("").to_string()));
|
return Ok(Some(result.message().unwrap_or("").to_string()));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Ok(Some("Action failed".to_string()));
|
return Ok(Some("Canvas action failed".to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only try CanvasAction::from_key as a fallback, and be more selective
|
// Fallback to automatic key handling for edit mode
|
||||||
if is_edit_mode {
|
if is_edit_mode {
|
||||||
// In edit mode, allow character insertion for unmapped keys
|
|
||||||
if let Some(canvas_action) = CanvasAction::from_key(key_event.code) {
|
if let Some(canvas_action) = CanvasAction::from_key(key_event.code) {
|
||||||
match ActionDispatcher::dispatch(
|
match ActionDispatcher::dispatch(
|
||||||
canvas_action,
|
canvas_action,
|
||||||
@@ -1112,7 +1124,7 @@ impl EventHandler {
|
|||||||
return Ok(Some(result.message().unwrap_or("").to_string()));
|
return Ok(Some(result.message().unwrap_or("").to_string()));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Ok(Some("Action failed".to_string()));
|
return Ok(Some("Auto action failed".to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1130,7 +1142,6 @@ impl EventHandler {
|
|||||||
KeyCode::BackTab => Some(CanvasAction::PrevField),
|
KeyCode::BackTab => Some(CanvasAction::PrevField),
|
||||||
KeyCode::Delete => Some(CanvasAction::DeleteForward),
|
KeyCode::Delete => Some(CanvasAction::DeleteForward),
|
||||||
KeyCode::Backspace => Some(CanvasAction::DeleteBackward),
|
KeyCode::Backspace => Some(CanvasAction::DeleteBackward),
|
||||||
// DON'T handle Char(c) in read-only mode - let config handle it
|
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user