mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-25 09:46:37 +03:00
feat: add hovered as $0
for shell and opener (#738)
This commit is contained in:
parent
b6fb02fda3
commit
2efda755f1
@ -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<Cmd> 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<Cmd> for OptDo {
|
||||
fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() }
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn open(&mut self, opt: impl Into<Opt>, 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<Opt>, tasks: &Tasks) {
|
||||
let opt = opt.into() as Opt;
|
||||
if opt.targets.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn open_do(&mut self, opt: impl Into<OptDo>, 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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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,
|
||||
})?;
|
||||
|
@ -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() }
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -1,5 +1,5 @@
|
||||
mod arrow;
|
||||
mod cancel;
|
||||
mod inspect;
|
||||
mod open;
|
||||
mod open_with;
|
||||
mod toggle;
|
||||
|
@ -15,11 +15,11 @@ impl TryFrom<Cmd> for Opt {
|
||||
}
|
||||
|
||||
impl Tasks {
|
||||
pub fn _open(targets: Vec<Url>, opener: Opener) {
|
||||
emit!(Call(Cmd::new("open").with_data(Opt { targets, opener }), Layer::Tasks));
|
||||
pub fn _open_with(targets: Vec<Url>, opener: Opener) {
|
||||
emit!(Call(Cmd::new("open_with").with_data(Opt { targets, opener }), Layer::Tasks));
|
||||
}
|
||||
|
||||
pub fn open(&mut self, opt: impl TryInto<Opt>) {
|
||||
pub fn open_with(&mut self, opt: impl TryInto<Opt>) {
|
||||
if let Ok(opt) = opt.try_into() {
|
||||
self.file_open_with(&opt.opener, &opt.targets);
|
||||
}
|
@ -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<Path>, impl AsRef<str>)]) -> 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<OsStr>]) -> bool {
|
||||
if opener.spread {
|
||||
pub fn file_open_with(&self, opener: &Opener, args: &[impl AsRef<OsStr>]) {
|
||||
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<Url>, dest: &Url, force: bool) {
|
||||
|
@ -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() {
|
||||
|
1
yazi-plugin/src/external/shell.rs
vendored
1
yazi-plugin/src/external/shell.rs
vendored
@ -40,7 +40,6 @@ pub fn shell(opt: ShellOpt) -> Result<Child> {
|
||||
.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 || {
|
||||
|
Loading…
Reference in New Issue
Block a user