From eac62492aa6e53a04ac6e0245c717b11db786031 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Thu, 9 Nov 2023 10:33:20 +0800 Subject: [PATCH] .. --- cspell.json | 2 +- yazi-core/src/tab/commands/cd.rs | 42 +++++++++-- yazi-core/src/tab/commands/jump.rs | 8 +- yazi-core/src/tasks/commands/arrow.rs | 36 +++++++++ yazi-core/src/tasks/commands/cancel.rs | 22 ++++++ yazi-core/src/tasks/commands/inspect.rs | 77 +++++++++++++++++++ yazi-core/src/tasks/commands/mod.rs | 3 + yazi-core/src/tasks/mod.rs | 1 + yazi-core/src/tasks/tasks.rs | 98 +------------------------ yazi-fm/src/executor.rs | 24 +++--- 10 files changed, 196 insertions(+), 117 deletions(-) create mode 100644 yazi-core/src/tasks/commands/arrow.rs create mode 100644 yazi-core/src/tasks/commands/cancel.rs create mode 100644 yazi-core/src/tasks/commands/inspect.rs create mode 100644 yazi-core/src/tasks/commands/mod.rs diff --git a/cspell.json b/cspell.json index 89b6f851..118d17d3 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags"],"language":"en","flagWords":[]} +{"language":"en","flagWords":[],"version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE"]} diff --git a/yazi-core/src/tab/commands/cd.rs b/yazi-core/src/tab/commands/cd.rs index 4ff3240f..bf065572 100644 --- a/yazi-core/src/tab/commands/cd.rs +++ b/yazi-core/src/tab/commands/cd.rs @@ -7,9 +7,31 @@ use yazi_shared::{expand_path, Debounce, InputError, Url}; use crate::{emit, input::InputOpt, tab::Tab}; +pub struct Opt { + target: Url, + interactive: bool, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { + Self { + target: e.args.first().map(Url::from).unwrap_or_default(), + interactive: e.named.contains_key("interactive"), + } + } +} +impl From for Opt { + fn from(target: Url) -> Self { Self { target, interactive: false } } +} + impl Tab { - pub fn cd(&mut self, target: Url) -> bool { - if self.current.cwd == target { + pub fn cd(&mut self, opt: impl Into) -> bool { + let opt = opt.into() as Opt; + if opt.interactive { + return self.cd_interactive(opt); + } + + if self.current.cwd == opt.target { return false; } @@ -19,30 +41,34 @@ impl Tab { } // Current - let rep = self.history_new(&target); + let rep = self.history_new(&opt.target); let rep = mem::replace(&mut self.current, rep); if rep.cwd.is_regular() { self.history.insert(rep.cwd.clone(), rep); } // Parent - if let Some(parent) = target.parent_url() { + if let Some(parent) = opt.target.parent_url() { self.parent = Some(self.history_new(&parent)); } // Backstack - if target.is_regular() { - self.backstack.push(target.clone()); + if opt.target.is_regular() { + self.backstack.push(opt.target.clone()); } emit!(Refresh); true } - pub fn cd_interactive(&mut self, target: Url) -> bool { + pub fn cd_interactive(&mut self, opt: impl Into) -> bool { + let opt = opt.into() as Opt; + tokio::spawn(async move { let rx = emit!(Input( - InputOpt::top("Change directory:").with_value(target.to_string_lossy()).with_completion() + InputOpt::top("Change directory:") + .with_value(opt.target.to_string_lossy()) + .with_completion() )); let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); diff --git a/yazi-core/src/tab/commands/jump.rs b/yazi-core/src/tab/commands/jump.rs index 901a83ba..a3ab16fb 100644 --- a/yazi-core/src/tab/commands/jump.rs +++ b/yazi-core/src/tab/commands/jump.rs @@ -39,13 +39,13 @@ impl Tab { let _defer = Defer::new(|| Event::Stop(false, None).emit()); emit!(Stop(true)).await; - let rx = if opt.type_ == OptType::Fzf { - external::fzf(FzfOpt { cwd }) + let url = if opt.type_ == OptType::Fzf { + external::fzf(FzfOpt { cwd }).await } else { - external::zoxide(ZoxideOpt { cwd }) + external::zoxide(ZoxideOpt { cwd }).await }?; - let op = if global && !ends_with_slash(&url) { "reveal" } else { "cd" }; + let op = if opt.type_ == OptType::Fzf && !ends_with_slash(&url) { "reveal" } else { "cd" }; emit!(Call(Exec::call(op, vec![url.to_string()]).vec(), KeymapLayer::Manager)); Ok::<(), anyhow::Error>(()) }); diff --git a/yazi-core/src/tasks/commands/arrow.rs b/yazi-core/src/tasks/commands/arrow.rs new file mode 100644 index 00000000..22333208 --- /dev/null +++ b/yazi-core/src/tasks/commands/arrow.rs @@ -0,0 +1,36 @@ +use yazi_config::keymap::Exec; + +use crate::tasks::Tasks; + +pub struct Opt { + step: isize, +} + +impl From<&Exec> for Opt { + fn from(e: &Exec) -> Self { + Self { step: e.args.first().and_then(|s| s.parse().ok()).unwrap_or(0) } + } +} + +impl Tasks { + #[allow(clippy::should_implement_trait)] + fn next(&mut self) -> bool { + let limit = Self::limit().min(self.len()); + + let old = self.cursor; + self.cursor = limit.saturating_sub(1).min(self.cursor + 1); + + old != self.cursor + } + + fn prev(&mut self) -> bool { + let old = self.cursor; + self.cursor = self.cursor.saturating_sub(1); + old != self.cursor + } + + pub fn arrow(&mut self, opt: impl Into) -> bool { + let opt = opt.into() as Opt; + if opt.step > 0 { self.next() } else { self.prev() } + } +} diff --git a/yazi-core/src/tasks/commands/cancel.rs b/yazi-core/src/tasks/commands/cancel.rs new file mode 100644 index 00000000..ea028859 --- /dev/null +++ b/yazi-core/src/tasks/commands/cancel.rs @@ -0,0 +1,22 @@ +use yazi_config::keymap::Exec; + +use crate::tasks::Tasks; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Tasks { + pub fn cancel(&mut self, _: impl Into) -> bool { + let id = self.scheduler.running.read().get_id(self.cursor); + if id.map(|id| self.scheduler.cancel(id)) != Some(true) { + return false; + } + + let len = self.scheduler.running.read().len(); + self.cursor = self.cursor.min(len.saturating_sub(1)); + true + } +} diff --git a/yazi-core/src/tasks/commands/inspect.rs b/yazi-core/src/tasks/commands/inspect.rs new file mode 100644 index 00000000..6649a6e8 --- /dev/null +++ b/yazi-core/src/tasks/commands/inspect.rs @@ -0,0 +1,77 @@ +use std::io::{stdout, Write}; + +use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; +use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time}; +use yazi_config::keymap::Exec; +use yazi_shared::{Defer, Term}; + +use crate::{emit, tasks::Tasks, Event, BLOCKER}; + +pub struct Opt; + +impl From<&Exec> for Opt { + fn from(_: &Exec) -> Self { Self } +} + +impl Tasks { + pub fn inspect(&self, _: impl Into) -> bool { + let Some(id) = self.scheduler.running.read().get_id(self.cursor) else { + return false; + }; + + let scheduler = self.scheduler.clone(); + tokio::spawn(async move { + let _guard = BLOCKER.acquire().await.unwrap(); + let (tx, mut rx) = mpsc::unbounded_channel(); + + let buffered = { + let mut running = scheduler.running.write(); + let Some(task) = running.get_mut(id) else { return }; + + task.logger = Some(tx); + task.logs.clone() + }; + + emit!(Stop(true)).await; + let _defer = Defer::new(|| { + disable_raw_mode().ok(); + Event::Stop(false, None).emit(); + }); + + Term::clear(&mut stdout()).ok(); + stdout().write_all(buffered.as_bytes()).ok(); + enable_raw_mode().ok(); + + let mut stdin = stdin(); + let mut quit = [0; 10]; + loop { + select! { + Some(line) = rx.recv() => { + let mut stdout = stdout().lock(); + stdout.write_all(line.as_bytes()).ok(); + stdout.write_all(b"\r\n").ok(); + } + _ = time::sleep(time::Duration::from_millis(100)) => { + if scheduler.running.read().get(id).is_none() { + stdout().write_all(b"Task finished, press `q` to quit\r\n").ok(); + break; + } + }, + Ok(_) = stdin.read(&mut quit) => { + if quit[0] == b'q' { + break; + } + } + } + } + + if let Some(task) = scheduler.running.write().get_mut(id) { + task.logger = None; + } + while quit[0] != b'q' { + stdin.read(&mut quit).await.ok(); + } + }); + false + } +} diff --git a/yazi-core/src/tasks/commands/mod.rs b/yazi-core/src/tasks/commands/mod.rs new file mode 100644 index 00000000..cbc6548c --- /dev/null +++ b/yazi-core/src/tasks/commands/mod.rs @@ -0,0 +1,3 @@ +mod arrow; +mod cancel; +mod inspect; diff --git a/yazi-core/src/tasks/mod.rs b/yazi-core/src/tasks/mod.rs index b79f612a..79983611 100644 --- a/yazi-core/src/tasks/mod.rs +++ b/yazi-core/src/tasks/mod.rs @@ -1,3 +1,4 @@ +mod commands; mod running; mod scheduler; mod task; diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 00a6ee02..60270e8c 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -1,17 +1,15 @@ -use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, io::{stdout, Write}, path::Path, sync::Arc}; +use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, path::Path, sync::Arc}; -use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use serde::Serialize; -use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time}; use tracing::debug; use yazi_config::{manager::SortBy, open::Opener, OPEN}; -use yazi_shared::{Defer, MimeKind, Term, Url}; +use yazi_shared::{MimeKind, Term, Url}; use super::{running::Running, task::TaskSummary, Scheduler, TASKS_PADDING, TASKS_PERCENT}; -use crate::{emit, files::{File, Files}, input::InputOpt, Event, BLOCKER}; +use crate::{emit, files::{File, Files}, input::InputOpt}; pub struct Tasks { - scheduler: Arc, + pub(super) scheduler: Arc, pub visible: bool, pub cursor: usize, @@ -39,99 +37,11 @@ impl Tasks { true } - #[allow(clippy::should_implement_trait)] - pub fn next(&mut self) -> bool { - let limit = Self::limit().min(self.len()); - - let old = self.cursor; - self.cursor = limit.saturating_sub(1).min(self.cursor + 1); - - old != self.cursor - } - - pub fn prev(&mut self) -> bool { - let old = self.cursor; - self.cursor = self.cursor.saturating_sub(1); - old != self.cursor - } - pub fn paginate(&self) -> Vec { let running = self.scheduler.running.read(); running.values().take(Self::limit()).map(Into::into).collect() } - pub fn inspect(&self) -> bool { - let Some(id) = self.scheduler.running.read().get_id(self.cursor) else { - return false; - }; - - let scheduler = self.scheduler.clone(); - tokio::spawn(async move { - let _guard = BLOCKER.acquire().await.unwrap(); - let (tx, mut rx) = mpsc::unbounded_channel(); - - let buffered = { - let mut running = scheduler.running.write(); - let Some(task) = running.get_mut(id) else { return }; - - task.logger = Some(tx); - task.logs.clone() - }; - - emit!(Stop(true)).await; - let _defer = Defer::new(|| { - disable_raw_mode().ok(); - Event::Stop(false, None).emit(); - }); - - Term::clear(&mut stdout()).ok(); - stdout().write_all(buffered.as_bytes()).ok(); - enable_raw_mode().ok(); - - let mut stdin = stdin(); - let mut quit = [0; 10]; - loop { - select! { - Some(line) = rx.recv() => { - let mut stdout = stdout().lock(); - stdout.write_all(line.as_bytes()).ok(); - stdout.write_all(b"\r\n").ok(); - } - _ = time::sleep(time::Duration::from_millis(100)) => { - if scheduler.running.read().get(id).is_none() { - stdout().write_all(b"Task finished, press `q` to quit\r\n").ok(); - break; - } - }, - Ok(_) = stdin.read(&mut quit) => { - if quit[0] == b'q' { - break; - } - } - } - } - - if let Some(task) = scheduler.running.write().get_mut(id) { - task.logger = None; - } - while quit[0] != b'q' { - stdin.read(&mut quit).await.ok(); - } - }); - false - } - - pub fn cancel(&mut self) -> bool { - let id = self.scheduler.running.read().get_id(self.cursor); - if id.map(|id| self.scheduler.cancel(id)) != Some(true) { - return false; - } - - let len = self.scheduler.running.read().len(); - self.cursor = self.cursor.min(len.saturating_sub(1)); - true - } - pub fn file_open(&self, targets: &[(impl AsRef, impl AsRef)]) -> bool { let mut openers = BTreeMap::new(); for (path, mime) in targets { diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 26a02c78..d0da7d2b 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -100,7 +100,8 @@ impl<'a> Executor<'a> { on!(ACTIVE, enter); on!(ACTIVE, back); on!(ACTIVE, forward); - // on!(A, cd); + on!(ACTIVE, cd); + on!(ACTIVE, reveal); // Selection on!(ACTIVE, select); @@ -145,17 +146,20 @@ impl<'a> Executor<'a> { } fn tasks(&mut self, exec: &Exec) -> bool { + macro_rules! on { + ($name:ident) => { + if exec.cmd == stringify!($name) { + return self.cx.tasks.$name(exec); + } + }; + } + + on!(arrow); + on!(inspect); + on!(cancel); + match exec.cmd.as_str() { "close" => self.cx.tasks.toggle(), - - "arrow" => { - let step = exec.args.first().and_then(|s| s.parse().ok()).unwrap_or(0); - if step > 0 { self.cx.tasks.next() } else { self.cx.tasks.prev() } - } - - "inspect" => self.cx.tasks.inspect(), - "cancel" => self.cx.tasks.cancel(), - "help" => self.cx.help.toggle(KeymapLayer::Tasks), _ => false, }