mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
136 lines
3.3 KiB
Rust
136 lines
3.3 KiB
Rust
use std::{borrow::Cow, iter};
|
|
|
|
use anyhow::Result;
|
|
use tokio::sync::oneshot;
|
|
use yazi_config::{YAZI, popup::PickCfg};
|
|
use yazi_core::tab::Folder;
|
|
use yazi_fs::File;
|
|
use yazi_macro::{act, succ};
|
|
use yazi_parser::mgr::{OpenDoOpt, OpenOpt};
|
|
use yazi_proxy::{MgrProxy, PickProxy, TasksProxy};
|
|
use yazi_shared::{MIME_DIR, event::Data, url::UrlBuf};
|
|
|
|
use crate::{Actor, Ctx, mgr::Quit};
|
|
|
|
pub struct Open;
|
|
|
|
impl Actor for Open {
|
|
type Options = OpenOpt;
|
|
|
|
const NAME: &str = "open";
|
|
|
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
|
act!(mgr:escape_visual, cx)?;
|
|
|
|
let Some(hovered) = cx.hovered().map(|h| h.url_owned()) else { succ!() };
|
|
|
|
let mut selected =
|
|
if opt.hovered { Box::new(iter::once(&hovered)) } else { cx.tab().selected_or_hovered() };
|
|
if Quit::quit_with_selected(opt, &mut selected) {
|
|
succ!();
|
|
}
|
|
|
|
let mut todo = vec![];
|
|
let targets: Vec<_> = selected
|
|
.cloned()
|
|
.enumerate()
|
|
.map(|(i, u)| {
|
|
if cx.mgr.mimetype.contains(&u) {
|
|
(u, "")
|
|
} else if Self::guess_folder(cx, &u) {
|
|
(u, MIME_DIR)
|
|
} else {
|
|
todo.push(i);
|
|
(u, "")
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let cwd = cx.cwd().clone();
|
|
if todo.is_empty() {
|
|
return act!(mgr:open_do, cx, OpenDoOpt { cwd, hovered, targets, interactive: opt.interactive });
|
|
}
|
|
|
|
let scheduler = cx.tasks.scheduler.clone();
|
|
tokio::spawn(async move {
|
|
let mut files = Vec::with_capacity(todo.len());
|
|
for i in todo {
|
|
if let Ok(f) = File::new(&targets[i].0).await {
|
|
files.push(f);
|
|
}
|
|
}
|
|
|
|
let mut wg = vec![];
|
|
for (fetcher, files) in YAZI.plugin.mime_fetchers(files) {
|
|
let (tx, rx) = oneshot::channel();
|
|
scheduler.fetch_paged(fetcher, files, Some(tx));
|
|
wg.push(rx);
|
|
}
|
|
|
|
for rx in wg {
|
|
if !rx.await.is_ok_and(|canceled| !canceled) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
MgrProxy::open_do(OpenDoOpt { cwd, hovered, targets, interactive: opt.interactive });
|
|
});
|
|
succ!();
|
|
}
|
|
}
|
|
|
|
// --- Do
|
|
pub struct OpenDo;
|
|
|
|
impl Actor for OpenDo {
|
|
type Options = OpenDoOpt;
|
|
|
|
const NAME: &str = "open_do";
|
|
|
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
|
let mut targets = opt.targets;
|
|
targets.iter_mut().filter(|(_, m)| m.is_empty()).for_each(|(u, m)| {
|
|
*m = cx.mgr.mimetype.by_url(u).unwrap_or_default();
|
|
});
|
|
|
|
targets.retain(|(_, m)| !m.is_empty());
|
|
if targets.is_empty() {
|
|
succ!();
|
|
} else if !opt.interactive {
|
|
succ!(cx.tasks.process_from_files(opt.cwd, opt.hovered, targets));
|
|
}
|
|
|
|
let openers: Vec<_> = YAZI.opener.all(YAZI.open.common(&targets).into_iter());
|
|
if openers.is_empty() {
|
|
succ!();
|
|
}
|
|
|
|
let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
|
|
let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
|
|
tokio::spawn(async move {
|
|
if let Ok(choice) = pick.await {
|
|
TasksProxy::open_with(Cow::Borrowed(openers[choice]), opt.cwd, urls);
|
|
}
|
|
});
|
|
succ!();
|
|
}
|
|
}
|
|
|
|
impl Open {
|
|
fn guess_folder(cx: &Ctx, url: &UrlBuf) -> bool {
|
|
let Some(p) = url.parent() else {
|
|
return true;
|
|
};
|
|
|
|
let find = |folder: Option<&Folder>| {
|
|
folder.is_some_and(|folder| {
|
|
p == folder.url && folder.files.iter().any(|f| f.is_dir() && f.urn() == url.urn())
|
|
})
|
|
};
|
|
|
|
find(Some(cx.current()))
|
|
|| find(cx.parent())
|
|
|| find(cx.hovered_folder())
|
|
|| find(cx.tab().history.get(&p))
|
|
}
|
|
}
|