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 = [ "<PageDown>" ], exec = "arrow 100%", desc = "Move cursor down one page" },
{ on = [ "h" ], exec = [ "escape --visual", "leave" ], desc = "Go back to the parent directory" },
{ on = [ "l" ], exec = [ "escape --visual", "enter" ], desc = "Enter the child directory" },
{ on = [ "h" ], exec = "leave", desc = "Go back to the parent directory" },
{ on = [ "l" ], exec = "enter", desc = "Enter the child directory" },
{ on = [ "H" ], exec = "back", desc = "Go back to the previous 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" },
# Operation
{ on = [ "o" ], exec = [ "escape --visual", "open" ], desc = "Open the selected files" },
{ on = [ "O" ], exec = [ "escape --visual", "open --interactive" ], desc = "Open the selected files interactively" },
{ on = [ "<Enter>" ], exec = [ "escape --visual", "open" ], desc = "Open the selected files" },
{ on = [ "<C-Enter>" ], exec = [ "escape --visual", "open --interactive" ], desc = "Open the selected files interactively" },
{ on = [ "y" ], exec = [ "escape --visual", "yank" ], desc = "Copy the selected 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 = [ "p" ], exec = "paste", desc = "Paste the files" },
{ 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 --relative", desc = "Symlink the relative path of files" },
{ on = [ "d" ], exec = [ "escape --visual", "remove" ], desc = "Move the files to the trash" },
{ on = [ "D" ], exec = [ "escape --visual", "remove --permanently" ], desc = "Permanently delete the files" },
{ 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 = [ ";" ], exec = [ "escape --visual", "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 = "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 rg", desc = "Search files by content using ripgrep" },
{ 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 fzf", desc = "Jump to a directory, or reveal a file using fzf" },
{ on = [ "o" ], exec = "open", desc = "Open the selected files" },
{ on = [ "O" ], exec = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "<Enter>" ], exec = "open", desc = "Open the selected files" },
{ on = [ "<C-Enter>" ], exec = "open --interactive", desc = "Open the selected files interactively" },
{ on = [ "y" ], exec = "yank", desc = "Copy the selected files" },
{ on = [ "Y" ], exec = "unyank", desc = "Cancel the yank status of files" },
{ on = [ "x" ], exec = "yank --cut", desc = "Cut the selected files" },
{ on = [ "p" ], exec = "paste", desc = "Paste the files" },
{ 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 --relative", desc = "Symlink the relative path of files" },
{ on = [ "d" ], exec = "remove", desc = "Move the files to the trash" },
{ 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 = [ "r" ], exec = "rename --cursor=before_ext", desc = "Rename a file or directory" },
{ on = [ ";" ], exec = "shell", desc = "Run a shell command" },
{ 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 = [ "s" ], exec = "search fd", desc = "Search files by name using fd" },
{ on = [ "S" ], exec = "search rg", desc = "Search files by content using ripgrep" },
{ 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 fzf", desc = "Jump to a directory, or reveal a file using fzf" },
# Linemode
{ 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" },
# Copy
{ on = [ "c", "c" ], exec = [ "escape --visual", "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", "f" ], exec = [ "escape --visual", "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", "c" ], exec = "copy path", desc = "Copy the absolute path" },
{ on = [ "c", "d" ], exec = "copy dirname", desc = "Copy the path of the parent directory" },
{ on = [ "c", "f" ], exec = "copy filename", desc = "Copy the name of the file" },
{ on = [ "c", "n" ], exec = "copy name_without_ext", desc = "Copy the name of the file without the extension" },
# Filter
{ 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"))]
tab_width: u8,
// Selected counter
count_selected: Style,
// Count
count_copied: Style,
count_cut: Style,
count_selected: Style,
// Border
pub border_symbol: String,

View File

@ -1,7 +1,7 @@
use crossterm::event::KeyCode;
use unicode_width::UnicodeWidthStr;
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 crate::input::Input;
@ -49,8 +49,7 @@ impl Help {
}
Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false } => {
self.in_filter = None;
render!();
return true; // Don't do the `filter_apply` below, since we already have the filtered results.
return render_and!(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 } => {
input.backspace(false);

View File

@ -25,8 +25,11 @@ impl From<Cmd> for Opt {
impl Manager {
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 {
self.hovered().map(|h| vec![&h.url]).unwrap_or_default()
} else {

View File

@ -18,10 +18,11 @@ impl Manager {
let opt = opt.into() as Opt;
let dest = self.cwd();
if self.yanked.cut {
tasks.file_cut(&self.yanked, dest, opt.force);
} else {
if !self.yanked.cut {
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 {
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 targets = self.selected_or_hovered().into_iter().cloned().collect();
tasks.file_remove(targets, opt.force, opt.permanently);

View File

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

View File

@ -2,8 +2,17 @@ use yazi_shared::{event::Cmd, render};
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 {
pub fn unyank(&mut self, _: Cmd) {
pub fn unyank(&mut self, _: impl Into<Opt>) {
render!(!self.yanked.is_empty());
self.yanked = Default::default();

View File

@ -14,6 +14,10 @@ impl From<Cmd> for Opt {
impl Manager {
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();
if selected.is_empty() {
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>) {
if !self.try_escape_visual() {
return;
}
let opt = opt.into() as Opt;
if opt.interactive {
return self.cd_interactive();

View File

@ -13,8 +13,11 @@ impl From<Cmd> for Opt {
}
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;
if !self.try_escape_visual() {
return;
}
let mut s = OsString::new();
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::{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 }
}
use crate::tab::Tab;
impl Tab {
pub fn enter(&mut self, _: impl Into<Opt>) {
let Some(hovered) = self.current.hovered().filter(|h| h.is_dir()).map(|h| h.url()) else {
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!();
pub fn enter(&mut self, _: Cmd) {
self.current.hovered().filter(|h| h.is_dir()).map(|h| h.url()).map(|u| self.cd(u));
}
}

View File

@ -1,5 +1,5 @@
use bitflags::bitflags;
use yazi_shared::{event::Cmd, render};
use yazi_shared::{event::Cmd, render, render_and};
use crate::{manager::Manager, tab::{Mode, Tab}};
@ -28,8 +28,36 @@ impl From<Cmd> for Opt {
}
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]
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]
pub fn escape_visual(&mut self) -> bool {
@ -47,8 +75,7 @@ impl Tab {
}
self.mode = Mode::Normal;
render!();
true
render_and!(true)
}
#[inline]
@ -61,49 +88,32 @@ impl Tab {
if self.current.hovered().is_some_and(|h| h.is_dir()) {
Manager::_peek(true);
}
true
render_and!(true)
}
#[inline]
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());
b
render_and!(true)
}
#[inline]
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();
b
render_and!(true)
}
pub fn escape(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if opt.is_empty() {
return render!(
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());
}
#[inline]
pub fn try_escape_visual(&mut self) -> bool {
self.escape_visual();
true
}
}

View File

@ -58,7 +58,8 @@ impl Tab {
return;
};
if query.is_empty() {
return self.escape(super::escape::Opt::FIND);
self.escape_find();
return;
}
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::{manager::Manager, tab::Tab};
use crate::tab::Tab;
pub struct Opt;
impl From<()> for Opt {
@ -14,36 +12,12 @@ impl From<Cmd> for Opt {
impl Tab {
pub fn leave(&mut self, _: impl Into<Opt>) {
let current = self
self
.current
.hovered()
.and_then(|h| h.parent())
.filter(|p| *p != self.current.cwd)
.or_else(|| self.current.cwd.parent_url());
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!();
.or_else(|| self.current.cwd.parent_url())
.map(|u| self.cd(u));
}
}

View File

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

View File

@ -20,7 +20,11 @@ impl From<Cmd> for Opt {
}
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 selected: Vec<_> = self.selected_or_hovered().into_iter().cloned().collect();

View File

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

View File

@ -14,7 +14,7 @@ function Header:cwd()
return span:style(THEME.manager.cwd)
end
function Header:counter()
function Header:count()
local yanked = #cx.yanked
local count, style
@ -73,7 +73,7 @@ function Header:render(area)
local chunks = self:layout(area)
local left = ui.Line { self:cwd() }
local right = ui.Line { self:counter(), self:tabs() }
local right = ui.Line { self:count(), self:tabs() }
return {
ui.Paragraph(chunks[1], { left }),
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);
};
($expr:expr) => {
if $expr {
($cond:expr) => {
if $cond {
render!();
}
};
}
#[macro_export]
macro_rules! render_and {
($cond:expr) => {
if $cond {
render!();
true
} else {
false
}
};
}