From 682664a149c46e01caa5f11f2b9531da2add8a65 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Wed, 28 Feb 2024 11:46:23 +0800 Subject: [PATCH] Support opening files --- yazi-core/src/manager/commands/open.rs | 58 +++++++++---------- yazi-core/src/manager/commands/rename.rs | 4 +- yazi-core/src/tab/commands/shell.rs | 2 +- yazi-core/src/tab/tab.rs | 8 +-- yazi-core/src/tasks/commands/mod.rs | 2 +- .../tasks/commands/{open.rs => open_with.rs} | 6 +- yazi-core/src/tasks/tasks.rs | 20 +++---- yazi-fm/src/executor.rs | 2 +- yazi-plugin/src/external/shell.rs | 3 +- 9 files changed, 49 insertions(+), 56 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/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index 0dadec4a..9eecbc1b 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -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 b8119004..076c76a9 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -59,13 +59,11 @@ impl Tab { } pub fn hovered_and_selected(&self) -> Vec<&Url> { - let mut a = self.current.hovered().map(|h| vec![&h.url]).unwrap_or_default(); - if self.selected.is_empty() { - a.extend(a.clone()); + if let Some(h) = self.current.hovered() { + [&h.url].iter().copied().chain(self.selected.iter()).collect() } else { - a.extend(self.selected.iter()); + vec![] } - a } // --- History 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..4fc77c57 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()); + openers.entry(opener).or_insert_with(|| vec![hovered]).push(path); } } 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 375c85a2..0ba75fcd 100644 --- a/yazi-plugin/src/external/shell.rs +++ b/yazi-plugin/src/external/shell.rs @@ -40,8 +40,7 @@ pub fn shell(opt: ShellOpt) -> Result { .stdout(opt.stdio()) .stderr(opt.stdio()) .arg(opt.cmd) - .arg(&opt.args[0]) // $0 is the hovered file - .args(&opt.args[1..]) + .args(opt.args) .kill_on_drop(!opt.orphan) .pre_exec(move || { if opt.orphan && libc::setpgid(0i32, 0i32) < 0 {