mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-24 16:21:04 +00:00
186 lines
4.9 KiB
Rust
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
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|