yazi/src/ui/dispatcher.rs
2023-07-08 19:46:43 +08:00

186 lines
4.9 KiB
Rust

use crossterm::event::KeyCode;
use super::Ctx;
use crate::{config::{keymap::{Exec, Key, Single}, KEYMAP}, core::InputMode, emit, misc::optinal_bool};
pub struct Executor;
impl Executor {
pub fn handle(cx: &mut Ctx, key: Key) -> bool {
let layer = if cx.input.visible {
2
} else if cx.tasks.visible {
1
} else {
0
};
let mut render = false;
let mut matched = false;
let keymap = [&KEYMAP.manager, &KEYMAP.tasks, &KEYMAP.input][layer];
for Single { on, exec } in keymap {
if on.len() < 1 || on[0] != key {
continue;
}
matched = true;
for e in exec {
if layer == 0 {
render = Self::manager(cx, e) || render;
} else if layer == 1 {
render = Self::tasks(cx, e) || render;
} else if layer == 2 {
render = Self::input(cx, Some(e), key.code) || render;
}
}
}
if layer == 2 && !matched {
render = Self::input(cx, None, key.code);
}
render
}
fn manager(cx: &mut Ctx, exec: &Exec) -> bool {
match exec.cmd.as_str() {
"escape" => cx.manager.active_mut().escape(),
"quit" => {
emit!(Quit);
false
}
"close" => cx.manager.close(),
// Navigation
"arrow" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
cx.manager.active_mut().arrow(step)
}
"leave" => cx.manager.active_mut().leave(),
"enter" => cx.manager.active_mut().enter(),
"back" => cx.manager.active_mut().back(),
"forward" => cx.manager.active_mut().forward(),
// Selection
"select" => {
let state = exec.named.get("state").cloned().unwrap_or("none".to_string());
cx.manager.active_mut().select(optinal_bool(&state))
}
"visual_mode" => cx.manager.active_mut().visual_mode(exec.named.contains_key("unselect")),
"select_all" => {
let state = exec.named.get("state").cloned().unwrap_or("none".to_string());
cx.manager.active_mut().select_all(optinal_bool(&state))
}
// Operation
"yank" => cx.manager.yank(exec.named.contains_key("cut")),
"paste" => {
let dest = cx.manager.current().cwd.clone();
let (cut, src) = cx.manager.yanked();
let force = exec.named.contains_key("force");
if *cut {
cx.tasks.file_cut(src, dest, force)
} else {
cx.tasks.file_copy(src, dest, force, exec.named.contains_key("follow"))
}
}
"remove" => {
cx.tasks.file_remove(cx.manager.selected(), exec.named.contains_key("permanently"))
}
"create" => cx.manager.create(),
"rename" => cx.manager.rename(),
"hidden" => cx.manager.current_mut().hidden(match exec.args.get(0).map(|s| s.as_str()) {
Some("show") => Some(true),
Some("hide") => Some(false),
_ => None,
}),
// Tabs
"tab_create" => {
let path = if exec.named.contains_key("current") {
cx.manager.current().cwd.clone()
} else {
exec.args.get(0).map(|p| p.into()).unwrap_or("/".into())
};
cx.manager.tabs_mut().create(&path)
}
"tab_close" => {
let idx = exec.args.get(0).and_then(|i| i.parse().ok()).unwrap_or(0);
cx.manager.tabs_mut().close(idx)
}
"tab_switch" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
let rel = exec.named.contains_key("relative");
cx.manager.tabs_mut().switch(step, rel)
}
"tab_swap" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
cx.manager.tabs_mut().swap(step)
}
// Tasks
"tasks_show" => cx.tasks.toggle(),
_ => false,
}
}
fn tasks(cx: &mut Ctx, exec: &Exec) -> bool {
match exec.cmd.as_str() {
"close" => cx.tasks.toggle(),
"arrow" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
if step > 0 { cx.tasks.next() } else { cx.tasks.prev() }
}
"cancel" => cx.tasks.cancel(),
_ => false,
}
}
fn input(cx: &mut Ctx, exec: Option<&Exec>, code: KeyCode) -> bool {
let exec = if let Some(e) = exec {
e
} else {
if cx.input.mode() == InputMode::Insert {
if let KeyCode::Char(c) = code {
return cx.input.type_(c);
}
}
return false;
};
match cx.input.mode() {
InputMode::Normal => match exec.cmd.as_str() {
"close" => cx.input.close(exec.named.contains_key("submit")),
"escape" => cx.input.escape(),
"insert" => cx.input.insert(exec.named.contains_key("append")),
"visual" => cx.input.visual(),
"move" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
cx.input.move_(step)
}
"backward" => cx.input.backward(),
"forward" => cx.input.forward(exec.named.contains_key("end-of-word")),
"delete" => cx.input.delete(exec.named.contains_key("insert")),
_ => false,
},
InputMode::Insert => match exec.cmd.as_str() {
"close" => cx.input.close(exec.named.contains_key("submit")),
"escape" => cx.input.escape(),
"backspace" => cx.input.backspace(),
_ => {
if let KeyCode::Char(c) = code {
return cx.input.type_(c);
}
false
}
},
}
}
}