This commit is contained in:
sxyazi 2023-11-09 10:33:20 +08:00
parent 4a72dea37e
commit eac62492aa
No known key found for this signature in database
10 changed files with 196 additions and 117 deletions

View file

@ -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"]}

View file

@ -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<Url> 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<Opt>) -> 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<Opt>) -> 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));

View file

@ -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>(())
});

View file

@ -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<Opt>) -> bool {
let opt = opt.into() as Opt;
if opt.step > 0 { self.next() } else { self.prev() }
}
}

View file

@ -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<Opt>) -> 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
}
}

View file

@ -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<Opt>) -> 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
}
}

View file

@ -0,0 +1,3 @@
mod arrow;
mod cancel;
mod inspect;

View file

@ -1,3 +1,4 @@
mod commands;
mod running;
mod scheduler;
mod task;

View file

@ -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<Scheduler>,
pub(super) scheduler: Arc<Scheduler>,
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<TaskSummary> {
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<Path>, impl AsRef<str>)]) -> bool {
let mut openers = BTreeMap::new();
for (path, mime) in targets {

View file

@ -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,
}