mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: new of open command
This commit is contained in:
parent
11c289229a
commit
521c7271b6
12 changed files with 78 additions and 26 deletions
|
|
@ -26,6 +26,8 @@ keymap = [
|
|||
{ on = [ "<C-r>" ], exec = "select_all --state=none" },
|
||||
|
||||
# Operation
|
||||
{ on = [ "o" ], exec = "open" },
|
||||
{ on = [ "O" ], exec = "open --select" },
|
||||
{ on = [ "y" ], exec = "yank" },
|
||||
{ on = [ "x" ], exec = "yank --cut" },
|
||||
{ on = [ "p" ], exec = "paste" },
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::BTreeMap, path::Path};
|
||||
use std::{collections::{BTreeMap, BTreeSet}, path::Path};
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
|
|
@ -22,17 +22,23 @@ struct OpenRule {
|
|||
impl Open {
|
||||
pub fn new() -> Self { toml::from_str(&MERGED_YAZI).unwrap() }
|
||||
|
||||
pub fn opener(&self, path: &Path, mime: &str) -> Option<&Opener> {
|
||||
pub fn openers(&self, path: &Path, mime: &str) -> Option<Vec<&Opener>> {
|
||||
self.rules.iter().find_map(|rule| {
|
||||
if rule.name.as_ref().map_or(false, |e| e.match_path(path, Some(false)))
|
||||
|| rule.mime.as_ref().map_or(false, |m| m.matches(mime))
|
||||
{
|
||||
self.openers.get(&rule.use_).and_then(|v| v.first())
|
||||
self.openers.get(&rule.use_).map(|v| v.iter().collect())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn common_openers(&self, targets: Vec<(&Path, &str)>) -> Vec<&Opener> {
|
||||
let grouped = targets.into_iter().filter_map(|(p, m)| self.openers(p, m)).collect::<Vec<_>>();
|
||||
let flat = grouped.iter().flatten().cloned().collect::<BTreeSet<_>>();
|
||||
flat.into_iter().filter(|o| grouped.iter().all(|g| g.contains(o))).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Open {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Opener {
|
||||
pub cmd: String,
|
||||
pub args: Vec<String>,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pub enum Event {
|
|||
Input(InputOpt, oneshot::Sender<Result<String>>),
|
||||
|
||||
// Tasks
|
||||
Open(Vec<PathBuf>),
|
||||
Open(Vec<(PathBuf, String)>),
|
||||
Progress(u8, u32),
|
||||
}
|
||||
|
||||
|
|
|
|||
6
src/core/external/file.rs
vendored
6
src/core/external/file.rs
vendored
|
|
@ -1,11 +1,11 @@
|
|||
use std::{collections::BTreeMap, path::PathBuf};
|
||||
use std::{collections::BTreeMap, ffi::OsStr, path::PathBuf};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::misc::MimeKind;
|
||||
|
||||
pub async fn file(files: &[PathBuf]) -> Result<BTreeMap<PathBuf, String>> {
|
||||
pub async fn file(files: &[impl AsRef<OsStr>]) -> Result<BTreeMap<PathBuf, String>> {
|
||||
if files.is_empty() {
|
||||
bail!("no files to get mime types for");
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ pub async fn file(files: &[PathBuf]) -> Result<BTreeMap<PathBuf, String>> {
|
|||
.into_iter()
|
||||
.zip(output.trim().lines())
|
||||
.filter(|(_, m)| MimeKind::valid(m))
|
||||
.map(|(f, m)| (f.clone(), m.to_string())),
|
||||
.map(|(f, m)| (f.as_ref().into(), m.to_string())),
|
||||
);
|
||||
|
||||
if mimes.len() == 0 {
|
||||
|
|
|
|||
2
src/core/external/mod.rs
vendored
2
src/core/external/mod.rs
vendored
|
|
@ -5,6 +5,7 @@ mod fzf;
|
|||
mod jq;
|
||||
mod lsar;
|
||||
mod rg;
|
||||
mod unar;
|
||||
mod zoxide;
|
||||
|
||||
pub use fd::*;
|
||||
|
|
@ -14,4 +15,5 @@ pub use fzf::*;
|
|||
pub use jq::*;
|
||||
pub use lsar::*;
|
||||
pub use rg::*;
|
||||
pub use unar::*;
|
||||
pub use zoxide::*;
|
||||
|
|
|
|||
25
src/core/external/unar.rs
vendored
Normal file
25
src/core/external/unar.rs
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use std::{path::Path, process::Stdio};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use tokio::{io::{AsyncReadExt, BufReader}, process::Command};
|
||||
|
||||
pub async fn unar_head(path: &Path, target: &Path) -> Result<Vec<u8>> {
|
||||
let mut child = Command::new("unar")
|
||||
.args(&[path, target])
|
||||
.args(&["-o", "-"])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null())
|
||||
.kill_on_drop(true)
|
||||
.spawn()?;
|
||||
|
||||
let mut buf = vec![0; 1024];
|
||||
let mut reader = BufReader::new(child.stdout.take().unwrap());
|
||||
|
||||
reader.read(&mut buf).await.ok();
|
||||
child.kill().await.ok();
|
||||
|
||||
if buf.is_empty() {
|
||||
bail!("failed to get head of unar");
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ impl Manager {
|
|||
self.active_mut().preview.go(&hovered.path, &mime);
|
||||
} else {
|
||||
tokio::spawn(async move {
|
||||
if let Ok(mimes) = external::file(&[hovered.path()]).await {
|
||||
if let Ok(mimes) = external::file(&[hovered.path]).await {
|
||||
emit!(Mimetype(mimes));
|
||||
}
|
||||
});
|
||||
|
|
@ -111,6 +111,36 @@ impl Manager {
|
|||
self.quit(tasks)
|
||||
}
|
||||
|
||||
pub fn open(&mut self, select: bool) -> bool {
|
||||
let mut selected = self
|
||||
.selected()
|
||||
.into_iter()
|
||||
.map(|p| {
|
||||
let mime = self.mimetype.get(&p).cloned();
|
||||
(p, mime)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if selected.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
let todo = selected.iter().filter(|(_, m)| m.is_none()).map(|(p, _)| p).collect::<Vec<_>>();
|
||||
if let Ok(mut mimes) = external::file(&todo).await {
|
||||
selected = selected
|
||||
.into_iter()
|
||||
.map(|(p, m)| {
|
||||
let mime = m.or_else(|| mimes.remove(&p));
|
||||
(p, mime)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
emit!(Open(selected.into_iter().filter_map(|(p, m)| m.map(|m| (p, m))).collect::<Vec<_>>()));
|
||||
});
|
||||
false
|
||||
}
|
||||
|
||||
pub fn create(&self) -> bool {
|
||||
let cwd = self.current().cwd.clone();
|
||||
tokio::spawn(async move {
|
||||
|
|
@ -167,17 +197,6 @@ impl Manager {
|
|||
self.current().selected().or_else(|| self.hovered().map(|h| vec![h.path()])).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub async fn mimetypes(&mut self, files: &[PathBuf]) -> Vec<Option<String>> {
|
||||
let todo =
|
||||
files.iter().filter(|&p| !self.mimetype.contains_key(p)).cloned().collect::<Vec<_>>();
|
||||
|
||||
if let Ok(mimes) = external::file(&todo).await {
|
||||
self.mimetype.extend(mimes);
|
||||
}
|
||||
|
||||
files.into_iter().map(|p| self.mimetype.get(p).cloned()).collect()
|
||||
}
|
||||
|
||||
pub fn update_read(&mut self, op: FilesOp) -> bool {
|
||||
let path = op.path();
|
||||
let cwd = self.current().cwd.clone();
|
||||
|
|
|
|||
|
|
@ -120,7 +120,6 @@ impl Tab {
|
|||
return false;
|
||||
};
|
||||
if !hovered.meta.is_dir() {
|
||||
emit!(Open(self.current.selected().unwrap_or(vec![hovered.path])));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,9 +98,9 @@ impl Tasks {
|
|||
pub fn file_open(&self, targets: Vec<(PathBuf, String)>) -> bool {
|
||||
let mut openers = BTreeMap::new();
|
||||
for target in targets {
|
||||
if let Some(opener) = OPEN.opener(&target.0, &target.1) {
|
||||
if let Some(opener) = OPEN.openers(&target.0, &target.1).and_then(|o| o.first().cloned()) {
|
||||
openers
|
||||
.entry(opener.clone())
|
||||
.entry(opener)
|
||||
.or_insert_with(|| vec![])
|
||||
.push(target.0.to_string_lossy().into_owned());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,9 +127,7 @@ impl App {
|
|||
}
|
||||
|
||||
Event::Open(files) => {
|
||||
let mime = manager.mimetypes(&files).await;
|
||||
let targets = files.into_iter().zip(mime).map_while(|(f, m)| m.map(|m| (f, m))).collect();
|
||||
self.cx.tasks.file_open(targets);
|
||||
self.cx.tasks.file_open(files);
|
||||
}
|
||||
Event::Progress(percent, left) => {
|
||||
self.cx.tasks.progress = (percent, left);
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ impl Executor {
|
|||
}
|
||||
|
||||
// Operation
|
||||
"open" => cx.manager.open(exec.named.contains_key("select")),
|
||||
"yank" => cx.manager.yank(exec.named.contains_key("cut")),
|
||||
"paste" => {
|
||||
let dest = cx.manager.current().cwd.clone();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue