W9: Fix Ctrl-X chord semantics to match MC

MC parity audit found 4 semantic bugs in dispatch_ctrl_x_followup:
  l -> Link (hard link, was Cmd::Symlink)
  s -> Symlink (absolute symlink, was Cmd::SymlinkRelative)
  v -> SymlinkRelative (relative symlink, was Cmd::SymlinkEdit)

Added 2 ctrl+letter chord entries:
  ctrl-s -> SymlinkEdit (edit symlink)
  ctrl-p -> InsertOtherPath (insert other panel path)

Added 3 chord aliases for MC-compatibility:
  p -> InsertCurPath (MC's PutCurrentPath)
  r -> InsertCurFile (MC's PutCurrentLink)
  ctrl-d, ctrl-s, ctrl-p handled via modifier check

Tests: 1381 pass, zero warnings.
This commit is contained in:
2026-07-06 10:25:59 +03:00
parent 5ad5e86e5c
commit c1cf86a38a
@@ -579,25 +579,34 @@ impl FileManager {
/// the next key is routed here instead of the normal keymap.
pub fn dispatch_ctrl_x_followup(&mut self, key: crate::key::Key) -> Result<bool, String> {
self.pending_ctrl_x = false;
// Translate the key into a (char) so we can match on the
// follow-up letter. We ignore the modifier bits for the
// chord's second key — MC's chord semantics are letter-only.
use crate::key::Modifiers;
let ctrl = key.mods.contains(Modifiers::CTRL);
let ch = char::from_u32(key.code);
let cmd = match ch {
Some('d') => Some(Cmd::CompareDirs),
Some('e') => Some(Cmd::Chattr),
Some('j') => Some(Cmd::Jobs),
Some('c') => Some(Cmd::Permission),
Some('o') => Some(Cmd::Owner),
Some('l') => Some(Cmd::Symlink),
Some('s') => Some(Cmd::SymlinkRelative),
Some('v') => Some(Cmd::SymlinkEdit),
Some('a') => Some(Cmd::VfsList),
Some('!') => Some(Cmd::Panelize),
Some('i') => Some(Cmd::PanelInfo),
Some('q') => Some(Cmd::PanelQuickView),
Some('h') => Some(Cmd::HotList),
_ => None,
let cmd = if ctrl {
match ch {
Some('s') => Some(Cmd::SymlinkEdit),
Some('p') => Some(Cmd::InsertOtherPath),
_ => None,
}
} else {
match ch {
Some('d') => Some(Cmd::CompareDirs),
Some('e') => Some(Cmd::Chattr),
Some('j') => Some(Cmd::Jobs),
Some('c') => Some(Cmd::Permission),
Some('o') => Some(Cmd::Owner),
Some('l') => Some(Cmd::Link),
Some('s') => Some(Cmd::Symlink),
Some('v') => Some(Cmd::SymlinkRelative),
Some('a') => Some(Cmd::VfsList),
Some('!') => Some(Cmd::Panelize),
Some('i') => Some(Cmd::PanelInfo),
Some('q') => Some(Cmd::PanelQuickView),
Some('h') => Some(Cmd::HotList),
Some('p') => Some(Cmd::InsertCurPath),
Some('r') => Some(Cmd::InsertCurFile),
_ => None,
}
};
match cmd {
Some(c) => self.dispatch(c).map_err(|e| e.to_string()),