yazi/yazi-core/src/manager/commands/open.rs
2024-01-02 08:38:26 +08:00

115 lines
2.8 KiB
Rust

use std::ffi::OsString;
use tokio::fs;
use tracing::error;
use yazi_config::{popup::SelectCfg, ARGS, OPEN};
use yazi_plugin::isolate;
use yazi_shared::{emit, event::Exec, fs::{File, Url}, Layer, MIME_DIR};
use crate::{manager::Manager, select::Select, tasks::Tasks};
pub struct Opt {
targets: Option<Vec<(Url, Option<String>)>>,
interactive: bool,
}
impl From<&Exec> for Opt {
fn from(e: &Exec) -> Self {
Self { targets: e.take_data(), interactive: e.named.contains_key("interactive") }
}
}
impl Manager {
pub fn open(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
let selected = self.selected();
if selected.is_empty() {
return;
} else if Self::quit_with_selected(&selected) {
return;
}
let (mut done, mut todo) = (Vec::with_capacity(selected.len()), vec![]);
for f in selected {
if f.is_dir() {
done.push((f.url(), Some(MIME_DIR.to_owned())));
} else if self.mimetype.get(&f.url).is_some() {
done.push((f.url(), None));
} else {
todo.push(f.clone());
}
}
let mut opt = opt.into() as Opt;
if todo.is_empty() {
opt.targets = Some(done);
return self.open_do(opt, tasks);
}
tokio::spawn(async move {
done.extend(todo.iter().map(|f| (f.url(), None)));
if let Err(e) = isolate::preload("mime.lua", todo, true).await {
error!("preload in watcher failed: {e}");
}
Self::_open_do(opt.interactive, done);
});
}
#[inline]
pub fn _open_do(interactive: bool, targets: Vec<(Url, Option<String>)>) {
emit!(Call(
Exec::call("open_do", vec![]).with_bool("interactive", interactive).with_data(targets).vec(),
Layer::Manager
));
}
pub fn open_do(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
let opt = opt.into() as Opt;
let Some(targets) = opt.targets else {
return;
};
let targets: Vec<_> = targets
.into_iter()
.filter_map(|(u, m)| m.or_else(|| self.mimetype.get(&u).cloned()).map(|m| (u, m)))
.collect();
if targets.is_empty() {
return;
} else if !opt.interactive {
tasks.file_open(&targets);
return;
}
let openers: Vec<_> = OPEN.common_openers(&targets).into_iter().cloned().collect();
if openers.is_empty() {
return;
}
let urls = 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());
}
});
}
fn quit_with_selected(selected: &[&File]) -> bool {
let Some(p) = ARGS.chooser_file.clone() else {
return false;
};
let paths = selected.iter().fold(OsString::new(), |mut s, &f| {
s.push(f.url.as_os_str());
s.push("\n");
s
});
tokio::spawn(async move {
fs::write(p, paths.as_encoded_bytes()).await.ok();
emit!(Quit(false));
});
true
}
}