From 2efda755f1179c29556fd5c8809247530ef84c00 Mon Sep 17 00:00:00 2001 From: rrveex <91850165+rrveex@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:12:09 +0200 Subject: [PATCH] feat: add hovered as `$0` for shell and opener (#738) --- yazi-core/src/manager/commands/open.rs | 58 +++++++++---------- yazi-core/src/manager/commands/rename.rs | 4 +- yazi-core/src/manager/manager.rs | 3 + yazi-core/src/tab/commands/shell.rs | 4 +- yazi-core/src/tab/tab.rs | 12 ++++ yazi-core/src/tasks/commands/mod.rs | 2 +- .../tasks/commands/{open.rs => open_with.rs} | 6 +- yazi-core/src/tasks/tasks.rs | 24 ++++---- yazi-fm/src/executor.rs | 2 +- yazi-plugin/src/external/shell.rs | 1 - 10 files changed, 63 insertions(+), 53 deletions(-) rename yazi-core/src/tasks/commands/{open.rs => open_with.rs} (67%) diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index bbeab28e..34dae598 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -9,37 +9,42 @@ use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, Layer, MIME_DI use crate::{folder::Folder, manager::Manager, select::Select, tasks::Tasks}; pub struct Opt { - targets: Vec<(Url, String)>, interactive: bool, hovered: bool, } impl From for Opt { - fn from(mut c: Cmd) -> Self { + fn from(c: Cmd) -> Self { Self { - targets: c.take_data().unwrap_or_default(), interactive: c.named.contains_key("interactive"), hovered: c.named.contains_key("hovered"), } } } +#[derive(Default)] +pub struct OptDo { + hovered: Url, + targets: Vec<(Url, String)>, + interactive: bool, +} + +impl From for OptDo { + fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() } +} + impl Manager { pub fn open(&mut self, opt: impl Into, tasks: &Tasks) { 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 { - self.selected_or_hovered() + let Some(hovered) = self.hovered().map(|h| h.url()) else { + return; }; - if selected.is_empty() { - return; - } else if Self::quit_with_selected(&selected) { + let opt = opt.into() as Opt; + let selected = if opt.hovered { vec![&hovered] } else { self.selected_or_hovered() }; + if Self::quit_with_selected(&selected) { return; } @@ -55,8 +60,7 @@ impl Manager { } if todo.is_empty() { - opt.targets = done; - return self.open_do(opt, tasks); + return self.open_do(OptDo { hovered, targets: done, interactive: opt.interactive }, tasks); } tokio::spawn(async move { @@ -69,27 +73,20 @@ impl Manager { done.extend(files.iter().map(|f| (f.url(), String::new()))); if let Err(e) = isolate::preload("mime", files, true).await { - error!("preload in watcher failed: {e}"); + error!("preload in open failed: {e}"); } - Self::_open_do(done, opt.interactive); + Self::_open_do(OptDo { hovered, targets: done, interactive: opt.interactive }); }); } #[inline] - pub fn _open_do(targets: Vec<(Url, String)>, interactive: bool) { - emit!(Call( - Cmd::new("open_do").with_bool("interactive", interactive).with_data(targets), - Layer::Manager - )); + pub fn _open_do(opt: OptDo) { + emit!(Call(Cmd::new("open_do").with_data(opt), Layer::Manager)); } - pub fn open_do(&mut self, opt: impl Into, tasks: &Tasks) { - let opt = opt.into() as Opt; - if opt.targets.is_empty() { - return; - } - + pub fn open_do(&mut self, opt: impl Into, tasks: &Tasks) { + let opt = opt.into() as OptDo; let targets: Vec<_> = opt .targets .into_iter() @@ -101,8 +98,7 @@ impl Manager { if targets.is_empty() { return; } else if !opt.interactive { - tasks.file_open(&targets); - return; + return tasks.file_open(&opt.hovered, &targets); } let openers: Vec<_> = OPEN.common_openers(&targets).into_iter().cloned().collect(); @@ -110,11 +106,11 @@ impl Manager { return; } - let urls = targets.into_iter().map(|(u, _)| u).collect(); + let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect(); tokio::spawn(async move { let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect())); if let Ok(choice) = result.await { - Tasks::_open(urls, openers[choice].clone()); + Tasks::_open_with(urls, openers[choice].clone()); } }); } diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 3164b40b..fd53d416 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, ffi::OsStr, io::{stdout, BufWriter, Write}, path::PathBuf}; +use std::{collections::BTreeMap, ffi::{OsStr, OsString}, io::{stdout, BufWriter, Write}, path::PathBuf}; use anyhow::{anyhow, bail, Result}; use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; @@ -129,7 +129,7 @@ impl Manager { let mut child = external::shell(ShellOpt { cmd: (*opener.exec).into(), - args: vec![tmp.to_owned().into()], + args: vec![OsString::new(), tmp.to_owned().into()], piped: false, orphan: false, })?; diff --git a/yazi-core/src/manager/manager.rs b/yazi-core/src/manager/manager.rs index 4250eab4..f536185c 100644 --- a/yazi-core/src/manager/manager.rs +++ b/yazi-core/src/manager/manager.rs @@ -52,4 +52,7 @@ impl Manager { #[inline] pub fn selected_or_hovered(&self) -> Vec<&Url> { self.tabs.active().selected_or_hovered() } + + #[inline] + pub fn hovered_and_selected(&self) -> Vec<&Url> { self.tabs.active().hovered_and_selected() } } diff --git a/yazi-core/src/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index a74ca0f0..50545948 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -26,7 +26,7 @@ impl Tab { } let mut opt = opt.into() as Opt; - let selected: Vec<_> = self.selected_or_hovered().into_iter().cloned().collect(); + let selected = self.hovered_and_selected().into_iter().cloned().collect(); tokio::spawn(async move { if !opt.confirm || opt.exec.is_empty() { @@ -37,7 +37,7 @@ impl Tab { } } - Tasks::_open(selected, Opener { + Tasks::_open_with(selected, Opener { exec: opt.exec, block: opt.block, orphan: false, diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index d354ed0b..42445a09 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -58,6 +58,18 @@ impl Tab { } } + pub fn hovered_and_selected(&self) -> Vec<&Url> { + let Some(h) = self.current.hovered() else { + return vec![]; + }; + + if self.selected.is_empty() { + vec![&h.url, &h.url] + } else { + [&h.url].into_iter().chain(self.selected.iter()).collect() + } + } + // --- History #[inline] pub fn history_new(&mut self, url: &Url) -> Folder { diff --git a/yazi-core/src/tasks/commands/mod.rs b/yazi-core/src/tasks/commands/mod.rs index f546135c..0fce8d2b 100644 --- a/yazi-core/src/tasks/commands/mod.rs +++ b/yazi-core/src/tasks/commands/mod.rs @@ -1,5 +1,5 @@ mod arrow; mod cancel; mod inspect; -mod open; +mod open_with; mod toggle; diff --git a/yazi-core/src/tasks/commands/open.rs b/yazi-core/src/tasks/commands/open_with.rs similarity index 67% rename from yazi-core/src/tasks/commands/open.rs rename to yazi-core/src/tasks/commands/open_with.rs index ad7c6b49..bba96769 100644 --- a/yazi-core/src/tasks/commands/open.rs +++ b/yazi-core/src/tasks/commands/open_with.rs @@ -15,11 +15,11 @@ impl TryFrom for Opt { } impl Tasks { - pub fn _open(targets: Vec, opener: Opener) { - emit!(Call(Cmd::new("open").with_data(Opt { targets, opener }), Layer::Tasks)); + pub fn _open_with(targets: Vec, opener: Opener) { + emit!(Call(Cmd::new("open_with").with_data(Opt { targets, opener }), Layer::Tasks)); } - pub fn open(&mut self, opt: impl TryInto) { + pub fn open_with(&mut self, opt: impl TryInto) { if let Ok(opt) = opt.try_into() { self.file_open_with(&opt.opener, &opt.targets); } diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index e5c72dee..4656e7ae 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -1,4 +1,4 @@ -use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, path::Path, sync::Arc, time::Duration}; +use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, sync::Arc, time::Duration}; use tokio::time::sleep; use tracing::debug; @@ -56,28 +56,28 @@ impl Tasks { running.values().take(Self::limit()).map(Into::into).collect() } - pub fn file_open(&self, targets: &[(impl AsRef, impl AsRef)]) -> bool { + pub fn file_open(&self, hovered: &Url, targets: &[(Url, String)]) { let mut openers = BTreeMap::new(); - for (path, mime) in targets { - if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first().copied()) { - openers.entry(opener).or_insert_with(Vec::new).push(path.as_ref().as_os_str()); + for (url, mime) in targets { + if let Some(opener) = OPEN.openers(url, mime).and_then(|o| o.first().copied()) { + openers.entry(opener).or_insert_with(|| vec![hovered]).push(url); } } for (opener, args) in openers { self.file_open_with(opener, &args); } - false } - pub fn file_open_with(&self, opener: &Opener, args: &[impl AsRef]) -> bool { - if opener.spread { + pub fn file_open_with(&self, opener: &Opener, args: &[impl AsRef]) { + if args.len() < 2 { + return; + } else if opener.spread { self.scheduler.process_open(opener, args); - return false; + return; } - for target in args { - self.scheduler.process_open(opener, &[target]); + for target in args.iter().skip(1) { + self.scheduler.process_open(opener, &[&args[0], target]); } - false } pub fn file_cut(&self, src: &HashSet, dest: &Url, force: bool) { diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 127d8123..e3163577 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -151,11 +151,11 @@ impl<'a> Executor<'a> { }; } - on!(open); on!(toggle, "close"); on!(arrow); on!(inspect); on!(cancel); + on!(open_with); #[allow(clippy::single_match)] match cmd.name.as_str() { diff --git a/yazi-plugin/src/external/shell.rs b/yazi-plugin/src/external/shell.rs index 76d1ca73..0ba75fcd 100644 --- a/yazi-plugin/src/external/shell.rs +++ b/yazi-plugin/src/external/shell.rs @@ -40,7 +40,6 @@ pub fn shell(opt: ShellOpt) -> Result { .stdout(opt.stdio()) .stderr(opt.stdio()) .arg(opt.cmd) - .arg("") // $0 is the command name .args(opt.args) .kill_on_drop(!opt.orphan) .pre_exec(move || {