refactor: using definite patterns to simplify keybindings (#690)

This commit is contained in:
三咲雅 · Misaki Masa 2024-02-17 19:03:26 +08:00 committed by GitHub
parent 53525b511e
commit 1ec9943af2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 167 additions and 179 deletions

View file

@ -31,8 +31,8 @@ keymap = [
{ on = [ "<PageUp>" ], exec = "arrow -100%", desc = "Move cursor up one page" }, { on = [ "<PageUp>" ], exec = "arrow -100%", desc = "Move cursor up one page" },
{ on = [ "<PageDown>" ], exec = "arrow 100%", desc = "Move cursor down one page" }, { on = [ "<PageDown>" ], exec = "arrow 100%", desc = "Move cursor down one page" },
{ on = [ "h" ], exec = [ "escape --visual", "leave" ], desc = "Go back to the parent directory" }, { on = [ "h" ], exec = "leave", desc = "Go back to the parent directory" },
{ on = [ "l" ], exec = [ "escape --visual", "enter" ], desc = "Enter the child directory" }, { on = [ "l" ], exec = "enter", desc = "Enter the child directory" },
{ on = [ "H" ], exec = "back", desc = "Go back to the previous directory" }, { on = [ "H" ], exec = "back", desc = "Go back to the previous directory" },
{ on = [ "L" ], exec = "forward", desc = "Go forward to the next directory" }, { on = [ "L" ], exec = "forward", desc = "Go forward to the next directory" },
@ -58,29 +58,29 @@ keymap = [
{ on = [ "<C-r>" ], exec = "select_all --state=none", desc = "Inverse selection of all files" }, { on = [ "<C-r>" ], exec = "select_all --state=none", desc = "Inverse selection of all files" },
# Operation # Operation
{ on = [ "o" ], exec = [ "escape --visual", "open" ], desc = "Open the selected files" }, { on = [ "o" ], exec = "open", desc = "Open the selected files" },
{ on = [ "O" ], exec = [ "escape --visual", "open --interactive" ], desc = "Open the selected files interactively" }, { on = [ "O" ], exec = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "<Enter>" ], exec = [ "escape --visual", "open" ], desc = "Open the selected files" }, { on = [ "<Enter>" ], exec = "open", desc = "Open the selected files" },
{ on = [ "<C-Enter>" ], exec = [ "escape --visual", "open --interactive" ], desc = "Open the selected files interactively" }, { on = [ "<C-Enter>" ], exec = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "y" ], exec = [ "escape --visual", "yank" ], desc = "Copy the selected files" }, { on = [ "y" ], exec = "yank", desc = "Copy the selected files" },
{ on = [ "Y" ], exec = "unyank", desc = "Cancel the yank status of files" }, { on = [ "Y" ], exec = "unyank", desc = "Cancel the yank status of files" },
{ on = [ "x" ], exec = [ "escape --visual", "yank --cut" ], desc = "Cut the selected files" }, { on = [ "x" ], exec = "yank --cut", desc = "Cut the selected files" },
{ on = [ "p" ], exec = "paste", desc = "Paste the files" }, { on = [ "p" ], exec = "paste", desc = "Paste the files" },
{ on = [ "P" ], exec = "paste --force", desc = "Paste the files (overwrite if the destination exists)" }, { on = [ "P" ], exec = "paste --force", desc = "Paste the files (overwrite if the destination exists)" },
{ on = [ "-" ], exec = "link", desc = "Symlink the absolute path of files" }, { on = [ "-" ], exec = "link", desc = "Symlink the absolute path of files" },
{ on = [ "_" ], exec = "link --relative", desc = "Symlink the relative path of files" }, { on = [ "_" ], exec = "link --relative", desc = "Symlink the relative path of files" },
{ on = [ "d" ], exec = [ "escape --visual", "remove" ], desc = "Move the files to the trash" }, { on = [ "d" ], exec = "remove", desc = "Move the files to the trash" },
{ on = [ "D" ], exec = [ "escape --visual", "remove --permanently" ], desc = "Permanently delete the files" }, { on = [ "D" ], exec = "remove --permanently", desc = "Permanently delete the files" },
{ on = [ "a" ], exec = "create", desc = "Create a file or directory (ends with / for directories)" }, { on = [ "a" ], exec = "create", desc = "Create a file or directory (ends with / for directories)" },
{ on = [ "r" ], exec = [ "escape --visual", "rename --cursor=before_ext" ], desc = "Rename a file or directory" }, { on = [ "r" ], exec = "rename --cursor=before_ext", desc = "Rename a file or directory" },
{ on = [ ";" ], exec = [ "escape --visual", "shell" ], desc = "Run a shell command" }, { on = [ ";" ], exec = "shell", desc = "Run a shell command" },
{ on = [ ":" ], exec = [ "escape --visual", "shell --block" ], desc = "Run a shell command (block the UI until the command finishes)" }, { on = [ ":" ], exec = "shell --block", desc = "Run a shell command (block the UI until the command finishes)" },
{ on = [ "." ], exec = "hidden toggle", desc = "Toggle the visibility of hidden files" }, { on = [ "." ], exec = "hidden toggle", desc = "Toggle the visibility of hidden files" },
{ on = [ "s" ], exec = "search fd", desc = "Search files by name using fd" }, { on = [ "s" ], exec = "search fd", desc = "Search files by name using fd" },
{ on = [ "S" ], exec = "search rg", desc = "Search files by content using ripgrep" }, { on = [ "S" ], exec = "search rg", desc = "Search files by content using ripgrep" },
{ on = [ "<C-s>" ], exec = "search none", desc = "Cancel the ongoing search" }, { on = [ "<C-s>" ], exec = "search none", desc = "Cancel the ongoing search" },
{ on = [ "z" ], exec = "jump zoxide", desc = "Jump to a directory using zoxide" }, { on = [ "z" ], exec = "jump zoxide", desc = "Jump to a directory using zoxide" },
{ on = [ "Z" ], exec = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" }, { on = [ "Z" ], exec = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" },
# Linemode # Linemode
{ on = [ "m", "s" ], exec = "linemode size", desc = "Set linemode to size" }, { on = [ "m", "s" ], exec = "linemode size", desc = "Set linemode to size" },
@ -89,10 +89,10 @@ keymap = [
{ on = [ "m", "n" ], exec = "linemode none", desc = "Set linemode to none" }, { on = [ "m", "n" ], exec = "linemode none", desc = "Set linemode to none" },
# Copy # Copy
{ on = [ "c", "c" ], exec = [ "escape --visual", "copy path" ], desc = "Copy the absolute path" }, { on = [ "c", "c" ], exec = "copy path", desc = "Copy the absolute path" },
{ on = [ "c", "d" ], exec = [ "escape --visual", "copy dirname" ], desc = "Copy the path of the parent directory" }, { on = [ "c", "d" ], exec = "copy dirname", desc = "Copy the path of the parent directory" },
{ on = [ "c", "f" ], exec = [ "escape --visual", "copy filename" ], desc = "Copy the name of the file" }, { on = [ "c", "f" ], exec = "copy filename", desc = "Copy the name of the file" },
{ on = [ "c", "n" ], exec = [ "escape --visual", "copy name_without_ext" ], desc = "Copy the name of the file without the extension" }, { on = [ "c", "n" ], exec = "copy name_without_ext", desc = "Copy the name of the file without the extension" },
# Filter # Filter
{ on = [ "f" ], exec = "filter --smart", desc = "Filter the files" }, { on = [ "f" ], exec = "filter --smart", desc = "Filter the files" },

View file

@ -31,10 +31,10 @@ pub struct Manager {
#[validate(range(min = 1, message = "Must be greater than 0"))] #[validate(range(min = 1, message = "Must be greater than 0"))]
tab_width: u8, tab_width: u8,
// Selected counter // Count
count_selected: Style,
count_copied: Style, count_copied: Style,
count_cut: Style, count_cut: Style,
count_selected: Style,
// Border // Border
pub border_symbol: String, pub border_symbol: String,

View file

@ -1,7 +1,7 @@
use crossterm::event::KeyCode; use crossterm::event::KeyCode;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use yazi_config::{keymap::{Control, Key}, KEYMAP}; use yazi_config::{keymap::{Control, Key}, KEYMAP};
use yazi_shared::{render, term::Term, Layer}; use yazi_shared::{render, render_and, term::Term, Layer};
use super::HELP_MARGIN; use super::HELP_MARGIN;
use crate::input::Input; use crate::input::Input;
@ -49,8 +49,7 @@ impl Help {
} }
Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false } => { Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false } => {
self.in_filter = None; self.in_filter = None;
render!(); return render_and!(true); // Don't do the `filter_apply` below, since we already have the filtered results.
return true; // Don't do the `filter_apply` below, since we already have the filtered results.
} }
Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => { Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => {
input.backspace(false); input.backspace(false);

View file

@ -25,8 +25,11 @@ impl From<Cmd> for Opt {
impl Manager { impl Manager {
pub fn open(&mut self, opt: impl Into<Opt>, tasks: &Tasks) { pub fn open(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
let mut opt = opt.into() as Opt; if !self.active_mut().try_escape_visual() {
return;
}
let mut opt = opt.into() as Opt;
let selected = if opt.hovered { let selected = if opt.hovered {
self.hovered().map(|h| vec![&h.url]).unwrap_or_default() self.hovered().map(|h| vec![&h.url]).unwrap_or_default()
} else { } else {

View file

@ -18,10 +18,11 @@ impl Manager {
let opt = opt.into() as Opt; let opt = opt.into() as Opt;
let dest = self.cwd(); let dest = self.cwd();
if self.yanked.cut { if !self.yanked.cut {
tasks.file_cut(&self.yanked, dest, opt.force);
} else {
tasks.file_copy(&self.yanked, dest, opt.force, opt.follow); tasks.file_copy(&self.yanked, dest, opt.force, opt.follow);
} }
tasks.file_cut(&self.yanked, dest, opt.force);
self.unyank(());
} }
} }

View file

@ -18,6 +18,10 @@ impl From<Cmd> for Opt {
impl Manager { impl Manager {
pub fn remove(&mut self, opt: impl Into<Opt>, tasks: &Tasks) { pub fn remove(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
if !self.active_mut().try_escape_visual() {
return;
}
let opt = opt.into() as Opt; let opt = opt.into() as Opt;
let targets = self.selected_or_hovered().into_iter().cloned().collect(); let targets = self.selected_or_hovered().into_iter().cloned().collect();
tasks.file_remove(targets, opt.force, opt.permanently); tasks.file_remove(targets, opt.force, opt.permanently);

View file

@ -52,8 +52,10 @@ impl Manager {
Ok(Self::_hover(Some(new))) Ok(Self::_hover(Some(new)))
} }
pub fn rename(&self, opt: impl Into<Opt>) { pub fn rename(&mut self, opt: impl Into<Opt>) {
if !self.active().selected.is_empty() { if !self.active_mut().try_escape_visual() {
return;
} else if !self.active().selected.is_empty() {
return self.bulk_rename(); return self.bulk_rename();
} }

View file

@ -2,8 +2,17 @@ use yazi_shared::{event::Cmd, render};
use crate::manager::Manager; use crate::manager::Manager;
pub struct Opt;
impl From<Cmd> for Opt {
fn from(_: Cmd) -> Self { Self }
}
impl From<()> for Opt {
fn from(_: ()) -> Self { Self }
}
impl Manager { impl Manager {
pub fn unyank(&mut self, _: Cmd) { pub fn unyank(&mut self, _: impl Into<Opt>) {
render!(!self.yanked.is_empty()); render!(!self.yanked.is_empty());
self.yanked = Default::default(); self.yanked = Default::default();

View file

@ -14,6 +14,10 @@ impl From<Cmd> for Opt {
impl Manager { impl Manager {
pub fn yank(&mut self, opt: impl Into<Opt>) { pub fn yank(&mut self, opt: impl Into<Opt>) {
if !self.active_mut().try_escape_visual() {
return;
}
let selected: HashSet<_> = self.selected_or_hovered().into_iter().cloned().collect(); let selected: HashSet<_> = self.selected_or_hovered().into_iter().cloned().collect();
if selected.is_empty() { if selected.is_empty() {
return; return;

View file

@ -0,0 +1,7 @@
use yazi_shared::event::Cmd;
use crate::tab::Tab;
impl Tab {
pub fn back(&mut self, _: Cmd) { self.backstack.shift_backward().cloned().map(|u| self.cd(u)); }
}

View file

@ -1,25 +0,0 @@
use yazi_shared::event::Cmd;
use crate::tab::Tab;
pub struct Opt;
impl From<()> for Opt {
fn from(_: ()) -> Self { Self }
}
impl From<Cmd> for Opt {
fn from(_: Cmd) -> Self { Self }
}
impl Tab {
pub fn back(&mut self, _: impl Into<Opt>) {
if let Some(url) = self.backstack.shift_backward().cloned() {
self.cd(url);
}
}
pub fn forward(&mut self, _: impl Into<Opt>) {
if let Some(url) = self.backstack.shift_forward().cloned() {
self.cd(url);
}
}
}

View file

@ -33,6 +33,10 @@ impl Tab {
} }
pub fn cd(&mut self, opt: impl Into<Opt>) { pub fn cd(&mut self, opt: impl Into<Opt>) {
if !self.try_escape_visual() {
return;
}
let opt = opt.into() as Opt; let opt = opt.into() as Opt;
if opt.interactive { if opt.interactive {
return self.cd_interactive(); return self.cd_interactive();

View file

@ -13,8 +13,11 @@ impl From<Cmd> for Opt {
} }
impl Tab { impl Tab {
pub fn copy(&self, opt: impl Into<Opt>) { pub fn copy(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt; let opt = opt.into() as Opt;
if !self.try_escape_visual() {
return;
}
let mut s = OsString::new(); let mut s = OsString::new();
let mut it = self.selected_or_hovered().into_iter().peekable(); let mut it = self.selected_or_hovered().into_iter().peekable();

View file

@ -1,40 +1,9 @@
use std::mem; use yazi_shared::event::Cmd;
use yazi_shared::{event::Cmd, render}; use crate::tab::Tab;
use crate::{manager::Manager, tab::Tab};
pub struct Opt;
impl From<()> for Opt {
fn from(_: ()) -> Self { Self }
}
impl From<Cmd> for Opt {
fn from(_: Cmd) -> Self { Self }
}
impl Tab { impl Tab {
pub fn enter(&mut self, _: impl Into<Opt>) { pub fn enter(&mut self, _: Cmd) {
let Some(hovered) = self.current.hovered().filter(|h| h.is_dir()).map(|h| h.url()) else { self.current.hovered().filter(|h| h.is_dir()).map(|h| h.url()).map(|u| self.cd(u));
return;
};
// Current
let rep = self.history_new(&hovered);
let rep = mem::replace(&mut self.current, rep);
if rep.cwd.is_regular() {
self.history.insert(rep.cwd.clone(), rep);
}
// Parent
if let Some(rep) = self.parent.take() {
self.history.insert(rep.cwd.clone(), rep);
}
self.parent = Some(self.history_new(&hovered.parent_url().unwrap()));
// Backstack
self.backstack.push(hovered);
Manager::_refresh();
render!();
} }
} }

View file

@ -1,5 +1,5 @@
use bitflags::bitflags; use bitflags::bitflags;
use yazi_shared::{event::Cmd, render}; use yazi_shared::{event::Cmd, render, render_and};
use crate::{manager::Manager, tab::{Mode, Tab}}; use crate::{manager::Manager, tab::{Mode, Tab}};
@ -28,8 +28,36 @@ impl From<Cmd> for Opt {
} }
impl Tab { impl Tab {
pub fn escape(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if opt.is_empty() {
_ = self.escape_find()
|| self.escape_visual()
|| self.escape_select()
|| self.escape_filter()
|| self.escape_search();
return;
}
if opt.contains(Opt::FIND) {
self.escape_find();
}
if opt.contains(Opt::VISUAL) {
self.escape_visual();
}
if opt.contains(Opt::SELECT) {
self.escape_select();
}
if opt.contains(Opt::FILTER) {
self.escape_filter();
}
if opt.contains(Opt::SEARCH) {
self.escape_search();
}
}
#[inline] #[inline]
pub fn escape_find(&mut self) -> bool { self.finder.take().is_some() } pub fn escape_find(&mut self) -> bool { render_and!(self.finder.take().is_some()) }
#[inline] #[inline]
pub fn escape_visual(&mut self) -> bool { pub fn escape_visual(&mut self) -> bool {
@ -47,8 +75,7 @@ impl Tab {
} }
self.mode = Mode::Normal; self.mode = Mode::Normal;
render!(); render_and!(true)
true
} }
#[inline] #[inline]
@ -61,49 +88,32 @@ impl Tab {
if self.current.hovered().is_some_and(|h| h.is_dir()) { if self.current.hovered().is_some_and(|h| h.is_dir()) {
Manager::_peek(true); Manager::_peek(true);
} }
true render_and!(true)
} }
#[inline] #[inline]
pub fn escape_filter(&mut self) -> bool { pub fn escape_filter(&mut self) -> bool {
let b = self.current.files.filter().is_some(); if self.current.files.filter().is_none() {
return false;
}
self.filter_do(super::filter::Opt::default()); self.filter_do(super::filter::Opt::default());
b render_and!(true)
} }
#[inline] #[inline]
pub fn escape_search(&mut self) -> bool { pub fn escape_search(&mut self) -> bool {
let b = self.current.cwd.is_search(); if !self.current.cwd.is_search() {
return false;
}
self.search_stop(); self.search_stop();
b render_and!(true)
} }
pub fn escape(&mut self, opt: impl Into<Opt>) { #[inline]
let opt = opt.into() as Opt; pub fn try_escape_visual(&mut self) -> bool {
if opt.is_empty() { self.escape_visual();
return render!( true
self.escape_find()
|| self.escape_visual()
|| self.escape_select()
|| self.escape_filter()
|| self.escape_search()
);
}
if opt.contains(Opt::FIND) {
render!(self.escape_find());
}
if opt.contains(Opt::VISUAL) {
render!(self.escape_visual());
}
if opt.contains(Opt::SELECT) {
render!(self.escape_select());
}
if opt.contains(Opt::FILTER) {
render!(self.escape_filter());
}
if opt.contains(Opt::SEARCH) {
render!(self.escape_search());
}
} }
} }

View file

@ -58,7 +58,8 @@ impl Tab {
return; return;
}; };
if query.is_empty() { if query.is_empty() {
return self.escape(super::escape::Opt::FIND); self.escape_find();
return;
} }
let Ok(finder) = Finder::new(&query, opt.case) else { let Ok(finder) = Finder::new(&query, opt.case) else {

View file

@ -0,0 +1,7 @@
use yazi_shared::event::Cmd;
use crate::tab::Tab;
impl Tab {
pub fn forward(&mut self, _: Cmd) { self.backstack.shift_forward().cloned().map(|u| self.cd(u)); }
}

View file

@ -1,8 +1,6 @@
use std::mem; use yazi_shared::event::Cmd;
use yazi_shared::{event::Cmd, render}; use crate::tab::Tab;
use crate::{manager::Manager, tab::Tab};
pub struct Opt; pub struct Opt;
impl From<()> for Opt { impl From<()> for Opt {
@ -14,36 +12,12 @@ impl From<Cmd> for Opt {
impl Tab { impl Tab {
pub fn leave(&mut self, _: impl Into<Opt>) { pub fn leave(&mut self, _: impl Into<Opt>) {
let current = self self
.current .current
.hovered() .hovered()
.and_then(|h| h.parent()) .and_then(|h| h.parent())
.filter(|p| *p != self.current.cwd) .filter(|p| *p != self.current.cwd)
.or_else(|| self.current.cwd.parent_url()); .or_else(|| self.current.cwd.parent_url())
.map(|u| self.cd(u));
let Some(current) = current else {
return;
};
// Parent
if let Some(rep) = self.parent.take() {
self.history.insert(rep.cwd.clone(), rep);
}
if let Some(parent) = current.parent_url() {
self.parent = Some(self.history_new(&parent));
}
// Current
let rep = self.history_new(&current);
let rep = mem::replace(&mut self.current, rep);
if rep.cwd.is_regular() {
self.history.insert(rep.cwd.clone(), rep);
}
// Backstack
self.backstack.push(current);
Manager::_refresh();
render!();
} }
} }

View file

@ -1,11 +1,12 @@
mod arrow; mod arrow;
mod backstack; mod back;
mod cd; mod cd;
mod copy; mod copy;
mod enter; mod enter;
mod escape; mod escape;
mod filter; mod filter;
mod find; mod find;
mod forward;
mod hidden; mod hidden;
mod jump; mod jump;
mod leave; mod leave;

View file

@ -20,7 +20,11 @@ impl From<Cmd> for Opt {
} }
impl Tab { impl Tab {
pub fn shell(&self, opt: impl Into<Opt>) { pub fn shell(&mut self, opt: impl Into<Opt>) {
if !self.try_escape_visual() {
return;
}
let mut opt = opt.into() as Opt; let mut opt = opt.into() as Opt;
let selected: Vec<_> = self.selected_or_hovered().into_iter().cloned().collect(); let selected: Vec<_> = self.selected_or_hovered().into_iter().cloned().collect();

View file

@ -1,5 +1,5 @@
use yazi_config::keymap::{ControlCow, Key}; use yazi_config::keymap::{ControlCow, Key};
use yazi_shared::{emit, render, Layer}; use yazi_shared::{emit, render, render_and, Layer};
#[derive(Default)] #[derive(Default)]
pub struct Which { pub struct Which {
@ -27,8 +27,7 @@ impl Which {
self.reset(); self.reset();
} }
render!(); render_and!(true)
true
} }
fn reset(&mut self) { fn reset(&mut self) {

View file

@ -14,7 +14,7 @@ function Header:cwd()
return span:style(THEME.manager.cwd) return span:style(THEME.manager.cwd)
end end
function Header:counter() function Header:count()
local yanked = #cx.yanked local yanked = #cx.yanked
local count, style local count, style
@ -73,7 +73,7 @@ function Header:render(area)
local chunks = self:layout(area) local chunks = self:layout(area)
local left = ui.Line { self:cwd() } local left = ui.Line { self:cwd() }
local right = ui.Line { self:counter(), self:tabs() } local right = ui.Line { self:count(), self:tabs() }
return { return {
ui.Paragraph(chunks[1], { left }), ui.Paragraph(chunks[1], { left }),
ui.Paragraph(chunks[2], { right }):align(ui.Paragraph.RIGHT), ui.Paragraph(chunks[2], { right }):align(ui.Paragraph.RIGHT),

View file

@ -7,9 +7,21 @@ macro_rules! render {
() => { () => {
$crate::event::NEED_RENDER.store(true, std::sync::atomic::Ordering::Relaxed); $crate::event::NEED_RENDER.store(true, std::sync::atomic::Ordering::Relaxed);
}; };
($expr:expr) => { ($cond:expr) => {
if $expr { if $cond {
render!(); render!();
} }
}; };
} }
#[macro_export]
macro_rules! render_and {
($cond:expr) => {
if $cond {
render!();
true
} else {
false
}
};
}