refactor: add Keymap with default bindings and wire into handle_key
Create keymap.rs with Keymap struct mapping (mode, key) to Cmd trait objects. Wire into App::handle_key — keymap dispatch is tried first, falling through to old handlers for unmigrated bindings. Normal mode navigation, cell ops, mode switches, and Help mode are keymap-driven. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -3,6 +3,8 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::command::cmd::CmdContext;
|
||||
use crate::command::keymap::Keymap;
|
||||
use crate::command::{self, Command};
|
||||
use crate::import::wizard::{ImportWizard, WizardStep};
|
||||
use crate::model::cell::{CellKey, CellValue};
|
||||
@ -66,6 +68,7 @@ pub struct App {
|
||||
pub pending_key: Option<char>,
|
||||
/// Yanked cell value for `p` paste
|
||||
pub yanked: Option<CellValue>,
|
||||
keymap: Keymap,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@ -88,6 +91,23 @@ impl App {
|
||||
dirty: false,
|
||||
pending_key: None,
|
||||
yanked: None,
|
||||
keymap: Keymap::default_keymap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmd_context(&self) -> CmdContext {
|
||||
let view = self.model.active_view();
|
||||
CmdContext {
|
||||
model: &self.model,
|
||||
mode: &self.mode,
|
||||
selected: view.selected,
|
||||
row_offset: view.row_offset,
|
||||
col_offset: view.col_offset,
|
||||
search_query: &self.search_query,
|
||||
yanked: &self.yanked,
|
||||
pending_key: self.pending_key,
|
||||
dirty: self.dirty,
|
||||
file_path_set: self.file_path.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,9 +123,19 @@ impl App {
|
||||
}
|
||||
|
||||
pub fn handle_key(&mut self, key: KeyEvent) -> Result<()> {
|
||||
// Try keymap first — if a binding matches, apply effects and return
|
||||
let ctx = self.cmd_context();
|
||||
if let Some(effects) = self.keymap.dispatch(&ctx, key.code, key.modifiers) {
|
||||
drop(ctx);
|
||||
self.apply_effects(effects);
|
||||
return Ok(());
|
||||
}
|
||||
drop(ctx);
|
||||
|
||||
match &self.mode.clone() {
|
||||
AppMode::Quit => {}
|
||||
AppMode::Help => {
|
||||
// Handled by keymap now, but keep as fallback
|
||||
self.mode = AppMode::Normal;
|
||||
}
|
||||
AppMode::ImportWizard => {
|
||||
|
||||
Reference in New Issue
Block a user