WIP [no ci]

This commit is contained in:
sxyazi 2023-11-02 19:02:12 +08:00
parent 530f8ca317
commit 50b7f1af2b
No known key found for this signature in database
5 changed files with 161 additions and 149 deletions

View file

@ -2,7 +2,7 @@ use anyhow::bail;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use serde::Deserialize; use serde::Deserialize;
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)]
#[serde(try_from = "String")] #[serde(try_from = "String")]
pub struct Key { pub struct Key {
pub code: KeyCode, pub code: KeyCode,

View file

@ -1,5 +1,4 @@
use tokio::fs; use tokio::fs;
use tracing::info;
use yazi_config::keymap::{Exec, KeymapLayer}; use yazi_config::keymap::{Exec, KeymapLayer};
use crate::{completion::Completion, emit}; use crate::{completion::Completion, emit};

View file

@ -70,27 +70,8 @@ impl Ctx {
None None
} }
#[inline]
pub fn layer(&self) -> KeymapLayer {
if self.which.visible {
KeymapLayer::Which
} else if self.completion.visible {
KeymapLayer::Completion
} else if self.help.visible {
KeymapLayer::Help
} else if self.input.visible {
KeymapLayer::Input
} else if self.select.visible {
KeymapLayer::Select
} else if self.tasks.visible {
KeymapLayer::Tasks
} else {
KeymapLayer::Manager
}
}
#[inline] #[inline]
pub fn image_layer(&self) -> bool { pub fn image_layer(&self) -> bool {
!matches!(self.layer(), KeymapLayer::Which | KeymapLayer::Help | KeymapLayer::Tasks) !self.which.visible && !self.help.visible && !self.tasks.visible
} }
} }

View file

@ -59,13 +59,13 @@ impl App {
fn dispatch_key(&mut self, key: KeyEvent) { fn dispatch_key(&mut self, key: KeyEvent) {
let key = Key::from(key); let key = Key::from(key);
if Executor::handle(&mut self.cx, key) { if Executor::new(&mut self.cx).handle(key) {
emit!(Render); emit!(Render);
} }
} }
fn dispatch_paste(&mut self, str: String) { fn dispatch_paste(&mut self, str: String) {
if self.cx.layer() == KeymapLayer::Input { if self.cx.input.visible {
let input = &mut self.cx.input; let input = &mut self.cx.input;
if input.mode() == InputMode::Insert && input.type_str(&str) { if input.mode() == InputMode::Insert && input.type_str(&str) {
emit!(Render); emit!(Render);
@ -112,7 +112,7 @@ impl App {
#[inline] #[inline]
fn dispatch_call(&mut self, exec: Vec<Exec>, layer: KeymapLayer) { fn dispatch_call(&mut self, exec: Vec<Exec>, layer: KeymapLayer) {
if Executor::dispatch(&mut self.cx, &exec, layer) { if Executor::new(&mut self.cx).dispatch(&exec, layer) {
emit!(Render); emit!(Render);
} }
} }

View file

@ -2,79 +2,99 @@ use yazi_config::{keymap::{Control, Exec, Key, KeymapLayer}, KEYMAP};
use yazi_core::{emit, input::InputMode, tab::FinderCase, Ctx}; use yazi_core::{emit, input::InputMode, tab::FinderCase, Ctx};
use yazi_shared::{optional_bool, Url}; use yazi_shared::{optional_bool, Url};
pub(super) struct Executor; pub(super) struct Executor<'a> {
cx: &'a mut Ctx,
}
impl Executor { impl<'a> Executor<'a> {
pub(super) fn handle(cx: &mut Ctx, key: Key) -> bool { #[inline]
let layer = cx.layer(); pub(super) fn new(cx: &'a mut Ctx) -> Self { Self { cx } }
if layer == KeymapLayer::Which {
return cx.which.press(key); pub(super) fn handle(&mut self, key: Key) -> bool {
if self.cx.which.visible {
return self.cx.which.press(key);
} }
if self.cx.input.visible && self.cx.input.type_(&key) {
if layer == KeymapLayer::Input && cx.input.type_(&key) { return true;
}
if self.cx.help.visible && self.cx.help.type_(&key) {
return true; return true;
} }
if layer == KeymapLayer::Help && cx.help.type_(&key) { let b = if self.cx.completion.visible {
return true; self.matches(KeymapLayer::Completion, key).or_else(|| self.matches(KeymapLayer::Input, key))
} } else if self.cx.help.visible {
self.matches(KeymapLayer::Help, key)
} else if self.cx.input.visible {
self.matches(KeymapLayer::Input, key)
} else if self.cx.select.visible {
self.matches(KeymapLayer::Select, key)
} else if self.cx.tasks.visible {
self.matches(KeymapLayer::Tasks, key)
} else {
self.matches(KeymapLayer::Manager, key)
};
b == Some(true)
}
#[inline]
fn matches(&mut self, layer: KeymapLayer, key: Key) -> Option<bool> {
for Control { on, exec, .. } in KEYMAP.get(layer) { for Control { on, exec, .. } in KEYMAP.get(layer) {
if on.is_empty() || on[0] != key { if on.is_empty() || on[0] != key {
continue; continue;
} }
return if on.len() > 1 { return Some(if on.len() > 1 {
cx.which.show(&key, layer) self.cx.which.show(&key, layer)
} else { } else {
Self::dispatch(cx, exec, layer) self.dispatch(exec, layer)
}; });
} }
false None
} }
#[inline] #[inline]
pub(super) fn dispatch(cx: &mut Ctx, exec: &[Exec], layer: KeymapLayer) -> bool { pub(super) fn dispatch(&mut self, exec: &[Exec], layer: KeymapLayer) -> bool {
let mut render = false; let mut render = false;
for e in exec { for e in exec {
render |= match layer { render |= match layer {
KeymapLayer::Manager => Self::manager(cx, e), KeymapLayer::Manager => self.manager(e),
KeymapLayer::Tasks => Self::tasks(cx, e), KeymapLayer::Tasks => self.tasks(e),
KeymapLayer::Select => Self::select(cx, e), KeymapLayer::Select => self.select(e),
KeymapLayer::Input => Self::input(cx, e), KeymapLayer::Input => self.input(e),
KeymapLayer::Help => Self::help(cx, e), KeymapLayer::Help => self.help(e),
KeymapLayer::Completion => Self::completion(cx, e), KeymapLayer::Completion => self.completion(e),
KeymapLayer::Which => unreachable!(), KeymapLayer::Which => unreachable!(),
}; };
} }
render render
} }
fn manager(cx: &mut Ctx, exec: &Exec) -> bool { fn manager(&mut self, exec: &Exec) -> bool {
match exec.cmd.as_str() { match exec.cmd.as_str() {
"escape" => cx.manager.active_mut().escape(exec), "escape" => self.cx.manager.active_mut().escape(exec),
"quit" => cx.manager.quit(&cx.tasks, exec.named.contains_key("no-cwd-file")), "quit" => self.cx.manager.quit(&self.cx.tasks, exec.named.contains_key("no-cwd-file")),
"close" => cx.manager.close(&cx.tasks), "close" => self.cx.manager.close(&self.cx.tasks),
"suspend" => cx.manager.suspend(), "suspend" => self.cx.manager.suspend(),
// Navigation // Navigation
"arrow" => { "arrow" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or_default(); let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or_default();
cx.manager.active_mut().arrow(step) self.cx.manager.active_mut().arrow(step)
} }
"peek" => { "peek" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0);
cx.manager.active_mut().preview.arrow(step); self.cx.manager.active_mut().preview.arrow(step);
cx.manager.peek(true, cx.image_layer()) self.cx.manager.peek(true, self.cx.image_layer())
} }
"leave" => cx.manager.active_mut().leave(), "leave" => self.cx.manager.active_mut().leave(),
"enter" => cx.manager.active_mut().enter(), "enter" => self.cx.manager.active_mut().enter(),
"back" => cx.manager.active_mut().back(), "back" => self.cx.manager.active_mut().back(),
"forward" => cx.manager.active_mut().forward(), "forward" => self.cx.manager.active_mut().forward(),
"cd" => { "cd" => {
let url = exec.args.get(0).map(Url::from).unwrap_or_default(); let url = exec.args.first().map(Url::from).unwrap_or_default();
if exec.named.contains_key("interactive") { if exec.named.contains_key("interactive") {
cx.manager.active_mut().cd_interactive(url) self.cx.manager.active_mut().cd_interactive(url)
} else { } else {
emit!(Cd(url)); emit!(Cd(url));
false false
@ -84,64 +104,68 @@ impl Executor {
// Selection // Selection
"select" => { "select" => {
let state = exec.named.get("state").cloned().unwrap_or("none".to_string()); let state = exec.named.get("state").cloned().unwrap_or("none".to_string());
cx.manager.active_mut().select(optional_bool(&state)) self.cx.manager.active_mut().select(optional_bool(&state))
} }
"select_all" => { "select_all" => {
let state = exec.named.get("state").cloned().unwrap_or("none".to_string()); let state = exec.named.get("state").cloned().unwrap_or("none".to_string());
cx.manager.active_mut().select_all(optional_bool(&state)) self.cx.manager.active_mut().select_all(optional_bool(&state))
} }
"visual_mode" => cx.manager.active_mut().visual_mode(exec.named.contains_key("unset")), "visual_mode" => self.cx.manager.active_mut().visual_mode(exec.named.contains_key("unset")),
// Operation // Operation
"open" => cx.manager.open(exec.named.contains_key("interactive")), "open" => self.cx.manager.open(exec.named.contains_key("interactive")),
"yank" => cx.manager.yank(exec.named.contains_key("cut")), "yank" => self.cx.manager.yank(exec.named.contains_key("cut")),
"paste" => { "paste" => {
let dest = cx.manager.cwd(); let dest = self.cx.manager.cwd();
let (cut, ref src) = cx.manager.yanked; let (cut, ref src) = self.cx.manager.yanked;
let force = exec.named.contains_key("force"); let force = exec.named.contains_key("force");
if cut { cx.tasks.file_cut(src, dest, force) } else { cx.tasks.file_copy(src, dest, force) } if cut {
self.cx.tasks.file_cut(src, dest, force)
} else {
self.cx.tasks.file_copy(src, dest, force)
}
} }
"link" => { "link" => {
let (cut, ref src) = cx.manager.yanked; let (cut, ref src) = self.cx.manager.yanked;
!cut !cut
&& cx.tasks.file_link( && self.cx.tasks.file_link(
src, src,
cx.manager.cwd(), self.cx.manager.cwd(),
exec.named.contains_key("relative"), exec.named.contains_key("relative"),
exec.named.contains_key("force"), exec.named.contains_key("force"),
) )
} }
"remove" => { "remove" => {
let targets = cx.manager.selected().into_iter().map(|f| f.url()).collect(); let targets = self.cx.manager.selected().into_iter().map(|f| f.url()).collect();
let force = exec.named.contains_key("force"); let force = exec.named.contains_key("force");
let permanently = exec.named.contains_key("permanently"); let permanently = exec.named.contains_key("permanently");
cx.tasks.file_remove(targets, force, permanently) self.cx.tasks.file_remove(targets, force, permanently)
} }
"create" => cx.manager.create(exec.named.contains_key("force")), "create" => self.cx.manager.create(exec.named.contains_key("force")),
"rename" => cx.manager.rename(exec.named.contains_key("force")), "rename" => self.cx.manager.rename(exec.named.contains_key("force")),
"copy" => cx.manager.active().copy(exec.args.get(0).map(|s| s.as_str()).unwrap_or("")), "copy" => self.cx.manager.active().copy(exec.args.first().map(|s| s.as_str()).unwrap_or("")),
"shell" => cx.manager.active().shell( "shell" => self.cx.manager.active().shell(
exec.args.get(0).map(|e| e.as_str()).unwrap_or(""), exec.args.first().map(|e| e.as_str()).unwrap_or(""),
exec.named.contains_key("block"), exec.named.contains_key("block"),
exec.named.contains_key("confirm"), exec.named.contains_key("confirm"),
), ),
"hidden" => cx.manager.active_mut().hidden(exec), "hidden" => self.cx.manager.active_mut().hidden(exec),
"linemode" => cx.manager.active_mut().linemode(exec), "linemode" => self.cx.manager.active_mut().linemode(exec),
"search" => match exec.args.get(0).map(|s| s.as_str()).unwrap_or("") { "search" => match exec.args.first().map(|s| s.as_str()).unwrap_or("") {
"rg" => cx.manager.active_mut().search(true), "rg" => self.cx.manager.active_mut().search(true),
"fd" => cx.manager.active_mut().search(false), "fd" => self.cx.manager.active_mut().search(false),
_ => cx.manager.active_mut().search_stop(), _ => self.cx.manager.active_mut().search_stop(),
}, },
"jump" => match exec.args.get(0).map(|s| s.as_str()).unwrap_or("") { "jump" => match exec.args.first().map(|s| s.as_str()).unwrap_or("") {
"fzf" => cx.manager.active_mut().jump(true), "fzf" => self.cx.manager.active_mut().jump(true),
"zoxide" => cx.manager.active_mut().jump(false), "zoxide" => self.cx.manager.active_mut().jump(false),
_ => false, _ => false,
}, },
// Find // Find
"find" => { "find" => {
let query = exec.args.get(0).map(|s| s.as_str()); let query = exec.args.first().map(|s| s.as_str());
let prev = exec.named.contains_key("previous"); let prev = exec.named.contains_key("previous");
let case = match (exec.named.contains_key("smart"), exec.named.contains_key("insensitive")) let case = match (exec.named.contains_key("smart"), exec.named.contains_key("insensitive"))
{ {
@ -149,152 +173,160 @@ impl Executor {
(_, false) => FinderCase::Sensitive, (_, false) => FinderCase::Sensitive,
(_, true) => FinderCase::Insensitive, (_, true) => FinderCase::Insensitive,
}; };
cx.manager.active_mut().find(query, prev, case) self.cx.manager.active_mut().find(query, prev, case)
} }
"find_arrow" => cx.manager.active_mut().find_arrow(exec.named.contains_key("previous")), "find_arrow" => self.cx.manager.active_mut().find_arrow(exec.named.contains_key("previous")),
// Sorting // Sorting
"sort" => { "sort" => {
let b = cx.manager.active_mut().sort(exec); let b = self.cx.manager.active_mut().sort(exec);
cx.tasks.precache_size(&cx.manager.current().files); self.cx.tasks.precache_size(&self.cx.manager.current().files);
b b
} }
// Tabs // Tabs
"tab_create" => { "tab_create" => {
let path = if exec.named.contains_key("current") { let path = if exec.named.contains_key("current") {
cx.manager.cwd().to_owned() self.cx.manager.cwd().to_owned()
} else { } else {
exec.args.get(0).map(Url::from).unwrap_or_else(|| Url::from("/")) exec.args.first().map(Url::from).unwrap_or_else(|| Url::from("/"))
}; };
cx.manager.tabs.create(&path) self.cx.manager.tabs.create(&path)
} }
"tab_close" => { "tab_close" => {
let idx = exec.args.get(0).and_then(|i| i.parse().ok()).unwrap_or(0); let idx = exec.args.first().and_then(|i| i.parse().ok()).unwrap_or(0);
cx.manager.tabs.close(idx) self.cx.manager.tabs.close(idx)
} }
"tab_switch" => { "tab_switch" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0);
let rel = exec.named.contains_key("relative"); let rel = exec.named.contains_key("relative");
cx.manager.tabs.switch(step, rel) self.cx.manager.tabs.switch(step, rel)
} }
"tab_swap" => { "tab_swap" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0);
cx.manager.tabs.swap(step) self.cx.manager.tabs.swap(step)
} }
// Tasks // Tasks
"tasks_show" => cx.tasks.toggle(), "tasks_show" => self.cx.tasks.toggle(),
// Help // Help
"help" => cx.help.toggle(cx.layer()), "help" => self.cx.help.toggle(KeymapLayer::Manager),
_ => false, _ => false,
} }
} }
fn tasks(cx: &mut Ctx, exec: &Exec) -> bool { fn tasks(&mut self, exec: &Exec) -> bool {
match exec.cmd.as_str() { match exec.cmd.as_str() {
"close" => cx.tasks.toggle(), "close" => self.cx.tasks.toggle(),
"arrow" => { "arrow" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0);
if step > 0 { cx.tasks.next() } else { cx.tasks.prev() } if step > 0 { self.cx.tasks.next() } else { self.cx.tasks.prev() }
} }
"inspect" => cx.tasks.inspect(), "inspect" => self.cx.tasks.inspect(),
"cancel" => cx.tasks.cancel(), "cancel" => self.cx.tasks.cancel(),
"help" => cx.help.toggle(cx.layer()), "help" => self.cx.help.toggle(KeymapLayer::Tasks),
_ => false, _ => false,
} }
} }
fn select(cx: &mut Ctx, exec: &Exec) -> bool { fn select(&mut self, exec: &Exec) -> bool {
match exec.cmd.as_str() { match exec.cmd.as_str() {
"close" => cx.select.close(exec.named.contains_key("submit")), "close" => self.cx.select.close(exec.named.contains_key("submit")),
"arrow" => { "arrow" => {
let step: isize = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); let step: isize = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0);
if step > 0 { cx.select.next(step as usize) } else { cx.select.prev(step.unsigned_abs()) } if step > 0 {
self.cx.select.next(step as usize)
} else {
self.cx.select.prev(step.unsigned_abs())
}
} }
"help" => cx.help.toggle(cx.layer()), "help" => self.cx.help.toggle(KeymapLayer::Select),
_ => false, _ => false,
} }
} }
fn input(cx: &mut Ctx, exec: &Exec) -> bool { fn input(&mut self, exec: &Exec) -> bool {
match exec.cmd.as_str() { match exec.cmd.as_str() {
"close" => return cx.input.close(exec.named.contains_key("submit")), "close" => return self.cx.input.close(exec.named.contains_key("submit")),
"escape" => return cx.input.escape(), "escape" => return self.cx.input.escape(),
"move" => { "move" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0);
let in_operating = exec.named.contains_key("in-operating"); let in_operating = exec.named.contains_key("in-operating");
return if in_operating { cx.input.move_in_operating(step) } else { cx.input.move_(step) }; return if in_operating {
self.cx.input.move_in_operating(step)
} else {
self.cx.input.move_(step)
};
} }
"complete" => return cx.completion.trigger(exec), "complete" => return self.cx.completion.trigger(exec),
_ => {} _ => {}
} }
match cx.input.mode() { match self.cx.input.mode() {
InputMode::Normal => match exec.cmd.as_str() { InputMode::Normal => match exec.cmd.as_str() {
"insert" => cx.input.insert(exec.named.contains_key("append")), "insert" => self.cx.input.insert(exec.named.contains_key("append")),
"visual" => cx.input.visual(), "visual" => self.cx.input.visual(),
"backward" => cx.input.backward(), "backward" => self.cx.input.backward(),
"forward" => cx.input.forward(exec.named.contains_key("end-of-word")), "forward" => self.cx.input.forward(exec.named.contains_key("end-of-word")),
"delete" => { "delete" => {
cx.input.delete(exec.named.contains_key("cut"), exec.named.contains_key("insert")) self.cx.input.delete(exec.named.contains_key("cut"), exec.named.contains_key("insert"))
} }
"yank" => cx.input.yank(), "yank" => self.cx.input.yank(),
"paste" => cx.input.paste(exec.named.contains_key("before")), "paste" => self.cx.input.paste(exec.named.contains_key("before")),
"undo" => cx.input.undo(), "undo" => self.cx.input.undo(),
"redo" => cx.input.redo(), "redo" => self.cx.input.redo(),
"help" => cx.help.toggle(cx.layer()), "help" => self.cx.help.toggle(KeymapLayer::Input),
_ => false, _ => false,
}, },
InputMode::Insert => false, InputMode::Insert => false,
} }
} }
fn help(cx: &mut Ctx, exec: &Exec) -> bool { fn help(&mut self, exec: &Exec) -> bool {
match exec.cmd.as_str() { match exec.cmd.as_str() {
"close" => cx.help.toggle(cx.layer()), "close" => self.cx.help.toggle(KeymapLayer::Help),
"escape" => cx.help.escape(), "escape" => self.cx.help.escape(),
"arrow" => { "arrow" => {
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0);
cx.help.arrow(step) self.cx.help.arrow(step)
} }
"filter" => cx.help.filter(), "filter" => self.cx.help.filter(),
_ => false, _ => false,
} }
} }
fn completion(cx: &mut Ctx, exec: &Exec) -> bool { fn completion(&mut self, exec: &Exec) -> bool {
match exec.cmd.as_str() { match exec.cmd.as_str() {
"trigger" => cx.completion.trigger(exec), "trigger" => self.cx.completion.trigger(exec),
"show" => cx.completion.show(exec), "show" => self.cx.completion.show(exec),
"close" => cx.completion.close(exec.named.contains_key("submit")), "close" => self.cx.completion.close(exec.named.contains_key("submit")),
"arrow" => { "arrow" => {
let step: isize = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0); let step: isize = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0);
if step > 0 { if step > 0 {
cx.completion.next(step as usize) self.cx.completion.next(step as usize)
} else { } else {
cx.completion.prev(step.unsigned_abs()) self.cx.completion.prev(step.unsigned_abs())
} }
} }
"help" => cx.help.toggle(cx.layer()), "help" => self.cx.help.toggle(KeymapLayer::Completion),
_ => false, _ => false,
} }
} }