diff --git a/README.md b/README.md
index 8e309793..d84437a3 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,14 @@ See https://yazi-rs.github.io/docs/image-preview for details.
[ueberzug]: https://github.com/jstkdng/ueberzugpp
[chafa]: https://hpjansson.org/chafa/
+## Special Thanks
+
+
+
+Thanks to RustRover team for providing open-source licenses to support the maintenance of Yazi.
+
+Active code contributors can contact @sxyazi to get a license (if any are still available).
+
## License
Yazi is MIT-licensed. For more information check the [LICENSE](LICENSE) file.
diff --git a/yazi-actor/src/actor.rs b/yazi-actor/src/actor.rs
index b17c90ad..ed8e15ef 100644
--- a/yazi-actor/src/actor.rs
+++ b/yazi-actor/src/actor.rs
@@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_dds::spark::SparkKind;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::Ctx;
diff --git a/yazi-actor/src/cmp/arrow.rs b/yazi-actor/src/cmp/arrow.rs
index 373719e0..88a868d9 100644
--- a/yazi-actor/src/cmp/arrow.rs
+++ b/yazi-actor/src/cmp/arrow.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use yazi_widgets::Scrollable;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/cmp/close.rs b/yazi-actor/src/cmp/close.rs
index 6551f604..a6c179c3 100644
--- a/yazi-actor/src/cmp/close.rs
+++ b/yazi-actor/src/cmp/close.rs
@@ -3,7 +3,7 @@ use std::mem;
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::{cmp::CloseOpt, input::CompleteOpt};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/cmp/show.rs b/yazi-actor/src/cmp/show.rs
index 43c410a8..99645d87 100644
--- a/yazi-actor/src/cmp/show.rs
+++ b/yazi-actor/src/cmp/show.rs
@@ -3,7 +3,7 @@ use std::{ffi::OsStr, mem, ops::ControlFlow};
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::cmp::{CmpItem, ShowOpt};
-use yazi_shared::{event::Data, osstr_contains, osstr_starts_with};
+use yazi_shared::{data::Data, osstr_contains, osstr_starts_with};
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/cmp/trigger.rs b/yazi-actor/src/cmp/trigger.rs
index 0ca7a0a5..3412e42e 100644
--- a/yazi-actor/src/cmp/trigger.rs
+++ b/yazi-actor/src/cmp/trigger.rs
@@ -5,7 +5,7 @@ use yazi_fs::{CWD, path::expand_url, provider::{DirReader, FileHolder}};
use yazi_macro::{act, render, succ};
use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt};
use yazi_proxy::CmpProxy;
-use yazi_shared::{OsStrSplit, event::Data, natsort, url::{UrlBuf, UrlCow, UrnBuf}};
+use yazi_shared::{OsStrSplit, data::Data, natsort, url::{UrlBuf, UrlCow, UrnBuf}};
use yazi_vfs::provider;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/confirm/arrow.rs b/yazi-actor/src/confirm/arrow.rs
index 74e23162..9b0c9f6f 100644
--- a/yazi-actor/src/confirm/arrow.rs
+++ b/yazi-actor/src/confirm/arrow.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/confirm/close.rs b/yazi-actor/src/confirm/close.rs
index 90551b8b..7bc18533 100644
--- a/yazi-actor/src/confirm/close.rs
+++ b/yazi-actor/src/confirm/close.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::confirm::CloseOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/confirm/show.rs b/yazi-actor/src/confirm/show.rs
index b313d6c1..71be83d9 100644
--- a/yazi-actor/src/confirm/show.rs
+++ b/yazi-actor/src/confirm/show.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::confirm::ShowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/context.rs b/yazi-actor/src/context.rs
index 9e5446ce..fdae7b95 100644
--- a/yazi-actor/src/context.rs
+++ b/yazi-actor/src/context.rs
@@ -3,7 +3,7 @@ use std::ops::{Deref, DerefMut};
use anyhow::{Result, anyhow};
use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}, tasks::Tasks};
use yazi_fs::File;
-use yazi_shared::{Source, event::Cmd, url::UrlBuf};
+use yazi_shared::{Id, Source, event::Cmd, url::UrlBuf};
pub struct Ctx<'a> {
pub core: &'a mut Core,
@@ -25,7 +25,7 @@ impl DerefMut for Ctx<'_> {
impl<'a> Ctx<'a> {
#[inline]
pub fn new(core: &'a mut Core, cmd: &Cmd) -> Result {
- let tab = if let Some(id) = cmd.id("tab") {
+ let tab = if let Ok(id) = cmd.get::("tab") {
core
.mgr
.tabs
diff --git a/yazi-actor/src/help/arrow.rs b/yazi-actor/src/help/arrow.rs
index fc2412b0..495e612d 100644
--- a/yazi-actor/src/help/arrow.rs
+++ b/yazi-actor/src/help/arrow.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use yazi_widgets::Scrollable;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/help/escape.rs b/yazi-actor/src/help/escape.rs
index 041b50f3..d45b1802 100644
--- a/yazi-actor/src/help/escape.rs
+++ b/yazi-actor/src/help/escape.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/help/filter.rs b/yazi-actor/src/help/filter.rs
index d20d2f72..f82c7c70 100644
--- a/yazi-actor/src/help/filter.rs
+++ b/yazi-actor/src/help/filter.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/help/toggle.rs b/yazi-actor/src/help/toggle.rs
index a2c90d52..37074dec 100644
--- a/yazi-actor/src/help/toggle.rs
+++ b/yazi-actor/src/help/toggle.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::help::ToggleOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/input/close.rs b/yazi-actor/src/input/close.rs
index a63097ab..f0261468 100644
--- a/yazi-actor/src/input/close.rs
+++ b/yazi-actor/src/input/close.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::input::CloseOpt;
-use yazi_shared::{errors::InputError, event::Data};
+use yazi_shared::{data::Data, errors::InputError};
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/input/escape.rs b/yazi-actor/src/input/escape.rs
index 1d84d0d9..0bde71e9 100644
--- a/yazi-actor/src/input/escape.rs
+++ b/yazi-actor/src/input/escape.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use yazi_widgets::input::InputOp;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/input/show.rs b/yazi-actor/src/input/show.rs
index b18a37e0..c498c7f2 100644
--- a/yazi-actor/src/input/show.rs
+++ b/yazi-actor/src/input/show.rs
@@ -4,7 +4,7 @@ use anyhow::Result;
use yazi_config::YAZI;
use yazi_macro::{act, render, succ};
use yazi_parser::input::ShowOpt;
-use yazi_shared::{errors::InputError, event::Data};
+use yazi_shared::{data::Data, errors::InputError};
use yazi_widgets::input::InputCallback;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/lives/file.rs b/yazi-actor/src/lives/file.rs
index ab731a0d..8bd20e9d 100644
--- a/yazi-actor/src/lives/file.rs
+++ b/yazi-actor/src/lives/file.rs
@@ -80,7 +80,7 @@ impl UserData for File {
});
methods.add_method("mime", |lua, me, ()| {
lua.named_registry_value::("cx")?.borrow_scoped(|core: &yazi_core::Core| {
- core.mgr.mimetype.by_url(&me.url).map(|s| lua.create_string(s)).transpose()
+ core.mgr.mimetype.get(&me.url).map(|s| lua.create_string(s)).transpose()
})?
});
methods.add_method("prefix", |lua, me, ()| {
@@ -97,7 +97,7 @@ impl UserData for File {
});
methods.add_method("style", |lua, me, ()| {
lua.named_registry_value::("cx")?.borrow_scoped(|core: &yazi_core::Core| {
- let mime = core.mgr.mimetype.by_file(me).unwrap_or_default();
+ let mime = core.mgr.mimetype.get(&me.url).unwrap_or_default();
THEME.filetype.iter().find(|&x| x.matches(me, mime)).map(|x| Style::from(x.style))
})
});
diff --git a/yazi-actor/src/mgr/arrow.rs b/yazi-actor/src/mgr/arrow.rs
index b2b3b71c..c2be4481 100644
--- a/yazi-actor/src/mgr/arrow.rs
+++ b/yazi-actor/src/mgr/arrow.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/back.rs b/yazi-actor/src/mgr/back.rs
index c6332deb..95f744f3 100644
--- a/yazi-actor/src/mgr/back.rs
+++ b/yazi-actor/src/mgr/back.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, mgr::CdSource};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/bulk_rename.rs b/yazi-actor/src/mgr/bulk_rename.rs
index dc135e72..c63491df 100644
--- a/yazi-actor/src/mgr/bulk_rename.rs
+++ b/yazi-actor/src/mgr/bulk_rename.rs
@@ -1,4 +1,4 @@
-use std::{borrow::Cow, ffi::{OsStr, OsString}, hash::Hash, io::{Read, Write}, ops::Deref, path::Path};
+use std::{borrow::Cow, ffi::{OsStr, OsString}, hash::Hash, io::{Read, Write}, ops::Deref};
use anyhow::{Result, anyhow};
use crossterm::{execute, style::Print};
@@ -11,7 +11,7 @@ use yazi_fs::{File, FilesOp, max_common_root, path::skip_url, provider::{FileBui
use yazi_macro::{err, succ};
use yazi_parser::VoidOpt;
use yazi_proxy::{AppProxy, HIDER, TasksProxy};
-use yazi_shared::{OsStrJoin, event::Data, terminal_clear, url::{Component, UrlBuf}};
+use yazi_shared::{OsStrJoin, data::Data, terminal_clear, url::{Component, Url, UrlBuf}};
use yazi_term::tty::TTY;
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@@ -52,8 +52,8 @@ impl Actor for BulkRename {
defer! { tokio::spawn(Local.remove_file(tmp.clone())); }
TasksProxy::process_exec(Cow::Borrowed(opener), cwd, vec![
- OsString::new(),
- tmp.to_owned().into(),
+ OsStr::new("").into(),
+ tmp.as_os_str().to_owned().into(),
])
.await;
@@ -147,7 +147,7 @@ impl BulkRename {
}
fn opener() -> Option<&'static OpenerRule> {
- YAZI.opener.block(YAZI.open.all(UrlBuf::from(Path::new("bulk-rename.txt")), "text/plain"))
+ YAZI.opener.block(YAZI.open.all(Url::regular("bulk-rename.txt"), "text/plain"))
}
async fn output_failed(failed: Vec<(Tuple, Tuple, anyhow::Error)>) -> Result<()> {
diff --git a/yazi-actor/src/mgr/cd.rs b/yazi-actor/src/mgr/cd.rs
index 5a7031b0..8a4118f2 100644
--- a/yazi-actor/src/mgr/cd.rs
+++ b/yazi-actor/src/mgr/cd.rs
@@ -9,7 +9,7 @@ use yazi_fs::{File, FilesOp, path::expand_url};
use yazi_macro::{act, err, render, succ};
use yazi_parser::mgr::CdOpt;
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy};
-use yazi_shared::{Debounce, errors::InputError, event::Data, url::UrlBuf};
+use yazi_shared::{Debounce, data::Data, errors::InputError, url::UrlBuf};
use yazi_vfs::VfsFile;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/close.rs b/yazi-actor/src/mgr/close.rs
index e2a6bd50..2e0620b2 100644
--- a/yazi-actor/src/mgr/close.rs
+++ b/yazi-actor/src/mgr/close.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::act;
use yazi_parser::mgr::CloseOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/copy.rs b/yazi-actor/src/mgr/copy.rs
index 9c6c2e3e..05baac1b 100644
--- a/yazi-actor/src/mgr/copy.rs
+++ b/yazi-actor/src/mgr/copy.rs
@@ -3,7 +3,7 @@ use std::ffi::OsString;
use anyhow::{Result, bail};
use yazi_macro::{act, succ};
use yazi_parser::mgr::CopyOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use yazi_widgets::CLIPBOARD;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/create.rs b/yazi-actor/src/mgr/create.rs
index c14c3077..51c57d1f 100644
--- a/yazi-actor/src/mgr/create.rs
+++ b/yazi-actor/src/mgr/create.rs
@@ -1,10 +1,10 @@
use anyhow::{Result, bail};
use yazi_config::popup::{ConfirmCfg, InputCfg};
-use yazi_fs::{File, FilesOp, ok_or_not_found};
-use yazi_macro::succ;
+use yazi_fs::{File, FilesOp};
+use yazi_macro::{ok_or_not_found, succ};
use yazi_parser::mgr::CreateOpt;
use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy};
-use yazi_shared::{event::Data, url::UrlBuf};
+use yazi_shared::{data::Data, url::UrlBuf};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@@ -50,12 +50,12 @@ impl Create {
} else if let Ok(real) = provider::casefold(&new).await
&& let Some((parent, urn)) = real.pair()
{
- ok_or_not_found(provider::remove_file(&new).await)?;
+ ok_or_not_found!(provider::remove_file(&new).await);
FilesOp::Deleting(parent.into(), [urn.into()].into()).emit();
provider::create(&new).await?;
} else if let Some(parent) = new.parent() {
provider::create_dir_all(parent).await.ok();
- ok_or_not_found(provider::remove_file(&new).await)?;
+ ok_or_not_found!(provider::remove_file(&new).await);
provider::create(&new).await?;
} else {
bail!("Cannot create file at root");
diff --git a/yazi-actor/src/mgr/download.rs b/yazi-actor/src/mgr/download.rs
new file mode 100644
index 00000000..d57dcce2
--- /dev/null
+++ b/yazi-actor/src/mgr/download.rs
@@ -0,0 +1,58 @@
+use std::{mem, time::{Duration, Instant}};
+
+use anyhow::Result;
+use futures::{StreamExt, stream::FuturesUnordered};
+use tokio::sync::oneshot;
+use yazi_fs::File;
+use yazi_macro::{act, succ};
+use yazi_parser::mgr::DownloadOpt;
+use yazi_shared::data::Data;
+use yazi_vfs::VfsFile;
+
+use crate::{Actor, Ctx};
+
+pub struct Download;
+
+impl Actor for Download {
+ type Options = DownloadOpt;
+
+ const NAME: &str = "download";
+
+ fn act(cx: &mut Ctx, opt: Self::Options) -> Result {
+ act!(mgr:escape_visual, cx)?;
+
+ let mut wg1 = FuturesUnordered::new();
+ for url in opt.urls {
+ let (tx, rx) = oneshot::channel();
+ cx.tasks.scheduler.file_download(url.to_owned(), Some(tx));
+ wg1.push(async move { (rx.await == Ok(true), url) });
+ }
+
+ let scheduler = cx.tasks.scheduler.clone();
+ tokio::spawn(async move {
+ let mut wg2 = vec![];
+ let mut files = Vec::with_capacity(wg1.len());
+ let mut instant = Instant::now();
+ while let Some((success, url)) = wg1.next().await {
+ if !success {
+ continue;
+ }
+
+ let Ok(f) = File::new(url).await else { continue };
+ files.push(f);
+
+ if instant.elapsed() >= Duration::from_secs(1) {
+ wg2.push(scheduler.fetch_mimetype(mem::take(&mut files)));
+ instant = Instant::now();
+ }
+ }
+
+ if !files.is_empty() {
+ wg2.push(scheduler.fetch_mimetype(files));
+ }
+ futures::future::join_all(wg2).await;
+ });
+
+ succ!();
+ }
+}
diff --git a/yazi-actor/src/mgr/enter.rs b/yazi-actor/src/mgr/enter.rs
index 7fd43280..efece673 100644
--- a/yazi-actor/src/mgr/enter.rs
+++ b/yazi-actor/src/mgr/enter.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, mgr::CdSource};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/escape.rs b/yazi-actor/src/mgr/escape.rs
index d6c9a349..aee89094 100644
--- a/yazi-actor/src/mgr/escape.rs
+++ b/yazi-actor/src/mgr/escape.rs
@@ -2,7 +2,7 @@ use anyhow::{Result, bail};
use yazi_macro::{act, render, render_and, succ};
use yazi_parser::{VoidOpt, mgr::EscapeOpt};
use yazi_proxy::AppProxy;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/filter.rs b/yazi-actor/src/mgr/filter.rs
index e09a5fda..c229588a 100644
--- a/yazi-actor/src/mgr/filter.rs
+++ b/yazi-actor/src/mgr/filter.rs
@@ -7,7 +7,7 @@ use yazi_config::popup::InputCfg;
use yazi_macro::succ;
use yazi_parser::mgr::FilterOpt;
use yazi_proxy::{InputProxy, MgrProxy};
-use yazi_shared::{Debounce, errors::InputError, event::Data};
+use yazi_shared::{Debounce, data::Data, errors::InputError};
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/filter_do.rs b/yazi-actor/src/mgr/filter_do.rs
index c68fc4da..d5de8094 100644
--- a/yazi-actor/src/mgr/filter_do.rs
+++ b/yazi-actor/src/mgr/filter_do.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_fs::Filter;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::FilterOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/find.rs b/yazi-actor/src/mgr/find.rs
index bd663090..6e8bc916 100644
--- a/yazi-actor/src/mgr/find.rs
+++ b/yazi-actor/src/mgr/find.rs
@@ -7,7 +7,7 @@ use yazi_config::popup::InputCfg;
use yazi_macro::succ;
use yazi_parser::mgr::{FindDoOpt, FindOpt};
use yazi_proxy::{InputProxy, MgrProxy};
-use yazi_shared::{Debounce, errors::InputError, event::Data};
+use yazi_shared::{Debounce, data::Data, errors::InputError};
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/find_arrow.rs b/yazi-actor/src/mgr/find_arrow.rs
index 6bdbd0b8..7ef3067a 100644
--- a/yazi-actor/src/mgr/find_arrow.rs
+++ b/yazi-actor/src/mgr/find_arrow.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::FindArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/find_do.rs b/yazi-actor/src/mgr/find_do.rs
index c4d75d0b..296c09f6 100644
--- a/yazi-actor/src/mgr/find_do.rs
+++ b/yazi-actor/src/mgr/find_do.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_core::tab::Finder;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::FindDoOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/follow.rs b/yazi-actor/src/mgr/follow.rs
index 8802b6d2..36817809 100644
--- a/yazi-actor/src/mgr/follow.rs
+++ b/yazi-actor/src/mgr/follow.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_fs::path::clean_url;
use yazi_macro::{act, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/forward.rs b/yazi-actor/src/mgr/forward.rs
index de572e74..a6642df5 100644
--- a/yazi-actor/src/mgr/forward.rs
+++ b/yazi-actor/src/mgr/forward.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, mgr::CdSource};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/hardlink.rs b/yazi-actor/src/mgr/hardlink.rs
index 1c51ec5c..59006581 100644
--- a/yazi-actor/src/mgr/hardlink.rs
+++ b/yazi-actor/src/mgr/hardlink.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::mgr::HardlinkOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/hidden.rs b/yazi-actor/src/mgr/hidden.rs
index fc44e3d5..efc55f03 100644
--- a/yazi-actor/src/mgr/hidden.rs
+++ b/yazi-actor/src/mgr/hidden.rs
@@ -3,7 +3,7 @@ use yazi_core::tab::Folder;
use yazi_fs::FolderStage;
use yazi_macro::{act, render, render_and, succ};
use yazi_parser::mgr::HiddenOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/hover.rs b/yazi-actor/src/mgr/hover.rs
index f4eb1d2f..ea6c1440 100644
--- a/yazi-actor/src/mgr/hover.rs
+++ b/yazi-actor/src/mgr/hover.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_dds::Pubsub;
use yazi_macro::{err, render, succ, tab};
use yazi_parser::mgr::HoverOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/leave.rs b/yazi-actor/src/mgr/leave.rs
index 4c1935d8..a7324850 100644
--- a/yazi-actor/src/mgr/leave.rs
+++ b/yazi-actor/src/mgr/leave.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, mgr::CdSource};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/linemode.rs b/yazi-actor/src/mgr/linemode.rs
index c5a23b52..3cd6dc91 100644
--- a/yazi-actor/src/mgr/linemode.rs
+++ b/yazi-actor/src/mgr/linemode.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::mgr::LinemodeOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/link.rs b/yazi-actor/src/mgr/link.rs
index cc86dce2..868489a7 100644
--- a/yazi-actor/src/mgr/link.rs
+++ b/yazi-actor/src/mgr/link.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::mgr::LinkOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/mod.rs b/yazi-actor/src/mgr/mod.rs
index 88ff46c4..49f9b3d4 100644
--- a/yazi-actor/src/mgr/mod.rs
+++ b/yazi-actor/src/mgr/mod.rs
@@ -6,6 +6,7 @@ yazi_macro::mod_flat!(
close
copy
create
+ download
enter
escape
filter
@@ -22,6 +23,7 @@ yazi_macro::mod_flat!(
linemode
link
open
+ open_do
paste
peek
quit
diff --git a/yazi-actor/src/mgr/open.rs b/yazi-actor/src/mgr/open.rs
index 3013ca26..6a7d6668 100644
--- a/yazi-actor/src/mgr/open.rs
+++ b/yazi-actor/src/mgr/open.rs
@@ -1,14 +1,10 @@
-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_boot::ARGS;
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 yazi_proxy::MgrProxy;
+use yazi_shared::{data::Data, url::UrlCow};
use yazi_vfs::VfsFile;
use crate::{Actor, Ctx, mgr::Quit};
@@ -20,118 +16,59 @@ impl Actor for Open {
const NAME: &str = "open";
- fn act(cx: &mut Ctx, opt: Self::Options) -> Result {
+ fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result {
act!(mgr:escape_visual, cx)?;
- let Some(hovered) = cx.hovered().map(|h| h.url_owned()) else { succ!() };
+ let Some(hovered) = cx.hovered().map(|h| h.url_owned()).map(UrlCow::from) 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!();
+ if !opt.interactive && ARGS.chooser_file.is_some() {
+ succ!(if !opt.targets.is_empty() {
+ Quit::with_selected(opt.targets)
+ } else if opt.hovered {
+ Quit::with_selected([hovered])
+ } else {
+ Quit::with_selected(cx.tab().selected_or_hovered())
+ });
}
- let mut todo = vec![];
- let targets: Vec<_> = selected
- .cloned()
+ if opt.targets.is_empty() {
+ opt.targets = if opt.hovered {
+ vec![hovered.clone()]
+ } else {
+ cx.tab().selected_or_hovered().cloned().map(Into::into).collect()
+ };
+ }
+
+ let todo: Vec<_> = opt
+ .targets
+ .iter()
.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, "")
- }
- })
+ .filter(|&(_, u)| !cx.mgr.mimetype.contains(u))
+ .map(|(i, _)| i)
.collect();
let cwd = cx.cwd().clone();
if todo.is_empty() {
- return act!(mgr:open_do, cx, OpenDoOpt { cwd, hovered, targets, interactive: opt.interactive });
+ return act!(mgr:open_do, cx, OpenDoOpt { cwd, hovered, targets: opt.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 {
+ if let Ok(f) = File::new(&opt.targets[i]).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 {
- 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);
+ if scheduler.fetch_mimetype(files).await {
+ MgrProxy::open_do(OpenDoOpt {
+ cwd,
+ hovered,
+ targets: opt.targets,
+ interactive: opt.interactive,
+ });
}
});
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))
- }
-}
diff --git a/yazi-actor/src/mgr/open_do.rs b/yazi-actor/src/mgr/open_do.rs
new file mode 100644
index 00000000..a3575b15
--- /dev/null
+++ b/yazi-actor/src/mgr/open_do.rs
@@ -0,0 +1,50 @@
+use std::borrow::Cow;
+
+use anyhow::Result;
+use yazi_config::{YAZI, popup::PickCfg};
+use yazi_macro::succ;
+use yazi_parser::mgr::OpenDoOpt;
+use yazi_proxy::{PickProxy, TasksProxy};
+use yazi_shared::data::Data;
+
+use crate::{Actor, Ctx};
+
+pub struct OpenDo;
+
+impl Actor for OpenDo {
+ type Options = OpenDoOpt;
+
+ const NAME: &str = "open_do";
+
+ fn act(cx: &mut Ctx, opt: Self::Options) -> Result {
+ let targets: Vec<_> = opt
+ .targets
+ .into_iter()
+ .map(|u| {
+ let m = cx.mgr.mimetype.get(&u).unwrap_or_default();
+ (u, m)
+ })
+ .filter(|(_, m)| !m.is_empty())
+ .collect();
+
+ 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!();
+ }
+}
diff --git a/yazi-actor/src/mgr/paste.rs b/yazi-actor/src/mgr/paste.rs
index a53eeffb..7cfc647a 100644
--- a/yazi-actor/src/mgr/paste.rs
+++ b/yazi-actor/src/mgr/paste.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_parser::mgr::PasteOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/peek.rs b/yazi-actor/src/mgr/peek.rs
index f9f2f83a..3940cd82 100644
--- a/yazi-actor/src/mgr/peek.rs
+++ b/yazi-actor/src/mgr/peek.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::mgr::PeekOpt;
use yazi_proxy::HIDER;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
@@ -21,7 +21,7 @@ impl Actor for Peek {
succ!(cx.tab_mut().preview.reset_image());
}
- let mime = cx.mgr.mimetype.by_file_owned(&hovered).unwrap_or_default();
+ let mime = cx.mgr.mimetype.owned(&hovered.url).unwrap_or_default();
let folder = cx.tab().hovered_folder().map(|f| (f.offset, f.cha));
if !cx.tab().preview.same_url(&hovered.url) {
diff --git a/yazi-actor/src/mgr/quit.rs b/yazi-actor/src/mgr/quit.rs
index 6d71b2e9..fc1a2a57 100644
--- a/yazi-actor/src/mgr/quit.rs
+++ b/yazi-actor/src/mgr/quit.rs
@@ -2,13 +2,12 @@ use std::{ffi::OsString, time::Duration};
use anyhow::Result;
use tokio::{select, time};
-use yazi_boot::ARGS;
use yazi_config::popup::ConfirmCfg;
use yazi_dds::spark::SparkKind;
use yazi_macro::{emit, succ};
-use yazi_parser::mgr::{OpenOpt, QuitOpt};
+use yazi_parser::mgr::QuitOpt;
use yazi_proxy::ConfirmProxy;
-use yazi_shared::{event::{Data, EventQuit}, url::UrlBuf};
+use yazi_shared::{data::Data, event::EventQuit, url::UrlCow};
use crate::{Actor, Ctx};
@@ -67,21 +66,17 @@ impl Actor for Quit {
}
impl Quit {
- pub(super) fn quit_with_selected<'a, I>(opt: OpenOpt, selected: I) -> bool
+ pub(super) fn with_selected<'a, I, T>(selected: I)
where
- I: Iterator- ,
+ I: IntoIterator
- ,
+ T: Into>,
{
- if opt.interactive || ARGS.chooser_file.is_none() {
- return false;
- }
-
- let paths = selected.fold(OsString::new(), |mut s, u| {
- s.push(u.os_str());
+ let paths = selected.into_iter().fold(OsString::new(), |mut s, u| {
+ s.push(u.into().as_url().os_str());
s.push("\n");
s
});
emit!(Quit(EventQuit { selected: Some(paths), ..Default::default() }));
- true
}
}
diff --git a/yazi-actor/src/mgr/refresh.rs b/yazi-actor/src/mgr/refresh.rs
index dca99f50..f5632716 100644
--- a/yazi-actor/src/mgr/refresh.rs
+++ b/yazi-actor/src/mgr/refresh.rs
@@ -5,7 +5,7 @@ use yazi_core::tab::Folder;
use yazi_fs::{CWD, Files, FilesOp, cha::Cha};
use yazi_macro::{act, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::{event::Data, url::UrlBuf};
+use yazi_shared::{data::Data, url::UrlBuf};
use yazi_term::tty::TTY;
use yazi_vfs::{VfsFiles, VfsFilesOp};
diff --git a/yazi-actor/src/mgr/remove.rs b/yazi-actor/src/mgr/remove.rs
index 23fb1d37..ee69f344 100644
--- a/yazi-actor/src/mgr/remove.rs
+++ b/yazi-actor/src/mgr/remove.rs
@@ -3,7 +3,7 @@ use yazi_config::popup::ConfirmCfg;
use yazi_macro::{act, succ};
use yazi_parser::mgr::RemoveOpt;
use yazi_proxy::{ConfirmProxy, MgrProxy};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/rename.rs b/yazi-actor/src/mgr/rename.rs
index fcd2da14..959385d1 100644
--- a/yazi-actor/src/mgr/rename.rs
+++ b/yazi-actor/src/mgr/rename.rs
@@ -1,11 +1,11 @@
use anyhow::Result;
use yazi_config::popup::{ConfirmCfg, InputCfg};
use yazi_dds::Pubsub;
-use yazi_fs::{File, FilesOp, ok_or_not_found};
-use yazi_macro::{act, err, succ};
+use yazi_fs::{File, FilesOp};
+use yazi_macro::{act, err, ok_or_not_found, succ};
use yazi_parser::mgr::RenameOpt;
use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy};
-use yazi_shared::{Id, event::Data, url::UrlBuf};
+use yazi_shared::{Id, data::Data, url::UrlBuf};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@@ -72,7 +72,7 @@ impl Rename {
if let Ok(u) = overwritten
&& let Some((parent, urn)) = u.pair()
{
- ok_or_not_found(provider::rename(&u, &new).await)?;
+ ok_or_not_found!(provider::rename(&u, &new).await);
FilesOp::Deleting(parent.to_owned(), [urn.into()].into()).emit();
}
diff --git a/yazi-actor/src/mgr/reveal.rs b/yazi-actor/src/mgr/reveal.rs
index c95d414b..1c148b73 100644
--- a/yazi-actor/src/mgr/reveal.rs
+++ b/yazi-actor/src/mgr/reveal.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_fs::{File, FilesOp};
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::RevealOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/search.rs b/yazi-actor/src/mgr/search.rs
index 33bd30ea..2b576afa 100644
--- a/yazi-actor/src/mgr/search.rs
+++ b/yazi-actor/src/mgr/search.rs
@@ -9,7 +9,7 @@ use yazi_macro::{act, succ};
use yazi_parser::{VoidOpt, mgr::{SearchOpt, SearchOptVia}};
use yazi_plugin::external;
use yazi_proxy::{InputProxy, MgrProxy};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/seek.rs b/yazi-actor/src/mgr/seek.rs
index 25fdbc78..dc1f13f5 100644
--- a/yazi-actor/src/mgr/seek.rs
+++ b/yazi-actor/src/mgr/seek.rs
@@ -3,7 +3,7 @@ use yazi_config::YAZI;
use yazi_macro::succ;
use yazi_parser::mgr::SeekOpt;
use yazi_plugin::isolate;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
@@ -19,11 +19,11 @@ impl Actor for Seek {
succ!(cx.tab_mut().preview.reset());
};
- let Some(mime) = cx.mgr.mimetype.by_file(hovered) else {
+ let Some(mime) = cx.mgr.mimetype.get(&hovered.url) else {
succ!(cx.tab_mut().preview.reset());
};
- let Some(previewer) = YAZI.plugin.previewer(&hovered.url, mime) else {
+ let Some(previewer) = YAZI.plugin.previewer(hovered, mime) else {
succ!(cx.tab_mut().preview.reset());
};
diff --git a/yazi-actor/src/mgr/shell.rs b/yazi-actor/src/mgr/shell.rs
index 1bbf900b..9b04fcfa 100644
--- a/yazi-actor/src/mgr/shell.rs
+++ b/yazi-actor/src/mgr/shell.rs
@@ -5,7 +5,7 @@ use yazi_config::{opener::OpenerRule, popup::InputCfg};
use yazi_macro::{act, succ};
use yazi_parser::mgr::ShellOpt;
use yazi_proxy::{InputProxy, TasksProxy};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
@@ -20,7 +20,7 @@ impl Actor for Shell {
act!(mgr:escape_visual, cx)?;
let cwd = opt.cwd.take().unwrap_or(cx.cwd().into()).into_owned();
- let selected = cx.tab().hovered_and_selected().cloned().collect();
+ let selected = cx.tab().hovered_and_selected().cloned().map(Into::into).collect();
let input = opt.interactive.then(|| {
InputProxy::show(InputCfg::shell(opt.block).with_value(&*opt.run).with_cursor(opt.cursor))
diff --git a/yazi-actor/src/mgr/sort.rs b/yazi-actor/src/mgr/sort.rs
index b5f55f8a..f0348b01 100644
--- a/yazi-actor/src/mgr/sort.rs
+++ b/yazi-actor/src/mgr/sort.rs
@@ -3,7 +3,7 @@ use yazi_core::tab::Folder;
use yazi_fs::{FilesSorter, FolderStage};
use yazi_macro::{act, render, render_and, succ};
use yazi_parser::mgr::SortOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/spot.rs b/yazi-actor/src/mgr/spot.rs
index 7fbedbc8..eafe6e69 100644
--- a/yazi-actor/src/mgr/spot.rs
+++ b/yazi-actor/src/mgr/spot.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::mgr::SpotOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
@@ -15,7 +15,7 @@ impl Actor for Spot {
fn act(cx: &mut Ctx, opt: Self::Options) -> Result {
let Some(hovered) = cx.hovered().cloned() else { succ!() };
- let mime = cx.mgr.mimetype.by_file_owned(&hovered).unwrap_or_default();
+ let mime = cx.mgr.mimetype.owned(&hovered.url).unwrap_or_default();
// if !self.active().spot.same_file(&hovered, &mime) {
// self.active_mut().spot.reset();
// }
diff --git a/yazi-actor/src/mgr/suspend.rs b/yazi-actor/src/mgr/suspend.rs
index 61da21fb..b34eac63 100644
--- a/yazi-actor/src/mgr/suspend.rs
+++ b/yazi-actor/src/mgr/suspend.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/tab_close.rs b/yazi-actor/src/mgr/tab_close.rs
index 2a0b9efb..23dde947 100644
--- a/yazi-actor/src/mgr/tab_close.rs
+++ b/yazi-actor/src/mgr/tab_close.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::TabCloseOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/tab_create.rs b/yazi-actor/src/mgr/tab_create.rs
index 5d9d4af8..9ddbcea9 100644
--- a/yazi-actor/src/mgr/tab_create.rs
+++ b/yazi-actor/src/mgr/tab_create.rs
@@ -3,7 +3,7 @@ use yazi_core::tab::Tab;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::{CdSource, TabCreateOpt};
use yazi_proxy::AppProxy;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/tab_swap.rs b/yazi-actor/src/mgr/tab_swap.rs
index 8f728946..6b4eaed2 100644
--- a/yazi-actor/src/mgr/tab_swap.rs
+++ b/yazi-actor/src/mgr/tab_swap.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_dds::Pubsub;
use yazi_macro::{err, render, succ};
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/tab_switch.rs b/yazi-actor/src/mgr/tab_switch.rs
index 0ff7ef29..bf86e002 100644
--- a/yazi-actor/src/mgr/tab_switch.rs
+++ b/yazi-actor/src/mgr/tab_switch.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::TabSwitchOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/toggle.rs b/yazi-actor/src/mgr/toggle.rs
index ef7fd091..153b2296 100644
--- a/yazi-actor/src/mgr/toggle.rs
+++ b/yazi-actor/src/mgr/toggle.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_macro::{render_and, succ};
use yazi_parser::mgr::ToggleOpt;
use yazi_proxy::AppProxy;
-use yazi_shared::{event::Data, url::UrlCow};
+use yazi_shared::{data::Data, url::UrlCow};
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/toggle_all.rs b/yazi-actor/src/mgr/toggle_all.rs
index 80dbc889..54ea0e0d 100644
--- a/yazi-actor/src/mgr/toggle_all.rs
+++ b/yazi-actor/src/mgr/toggle_all.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::mgr::ToggleAllOpt;
use yazi_proxy::AppProxy;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/unyank.rs b/yazi-actor/src/mgr/unyank.rs
index 4a896798..21f66e18 100644
--- a/yazi-actor/src/mgr/unyank.rs
+++ b/yazi-actor/src/mgr/unyank.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::Actor;
diff --git a/yazi-actor/src/mgr/update_files.rs b/yazi-actor/src/mgr/update_files.rs
index d2219931..d4b7e5f6 100644
--- a/yazi-actor/src/mgr/update_files.rs
+++ b/yazi-actor/src/mgr/update_files.rs
@@ -3,7 +3,7 @@ use yazi_core::tab::Folder;
use yazi_fs::FilesOp;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::UpdateFilesOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use yazi_watcher::LINKED;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/update_mimes.rs b/yazi-actor/src/mgr/update_mimes.rs
index aa44454f..181f820d 100644
--- a/yazi-actor/src/mgr/update_mimes.rs
+++ b/yazi-actor/src/mgr/update_mimes.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use hashbrown::HashMap;
use yazi_macro::{act, render, succ};
use yazi_parser::mgr::UpdateMimesOpt;
-use yazi_shared::{event::Data, pool::InternStr, url::UrlCov};
+use yazi_shared::{data::Data, pool::InternStr, url::UrlCov};
use yazi_watcher::LINKED;
use crate::{Actor, Ctx};
@@ -20,7 +20,7 @@ impl Actor for UpdateMimes {
.updates
.into_iter()
.flat_map(|(key, value)| key.into_url().zip(value.into_string()))
- .filter(|(url, mime)| cx.mgr.mimetype.by_url(url) != Some(mime))
+ .filter(|(url, mime)| cx.mgr.mimetype.get(url) != Some(mime))
.fold(HashMap::new(), |mut map, (u, m)| {
for u in linked.from_file(u.as_url()) {
map.insert(u.into(), m.intern());
diff --git a/yazi-actor/src/mgr/update_paged.rs b/yazi-actor/src/mgr/update_paged.rs
index 3b0fdbb1..90d501b3 100644
--- a/yazi-actor/src/mgr/update_paged.rs
+++ b/yazi-actor/src/mgr/update_paged.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::mgr::UpdatePagedOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/update_peeked.rs b/yazi-actor/src/mgr/update_peeked.rs
index 6d3a5867..0fa01a6b 100644
--- a/yazi-actor/src/mgr/update_peeked.rs
+++ b/yazi-actor/src/mgr/update_peeked.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::mgr::UpdatePeekedOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::Actor;
diff --git a/yazi-actor/src/mgr/update_spotted.rs b/yazi-actor/src/mgr/update_spotted.rs
index ad393ab5..9c15d0cc 100644
--- a/yazi-actor/src/mgr/update_spotted.rs
+++ b/yazi-actor/src/mgr/update_spotted.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::mgr::UpdateSpottedOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/update_yanked.rs b/yazi-actor/src/mgr/update_yanked.rs
index 8f45e326..2b3120e9 100644
--- a/yazi-actor/src/mgr/update_yanked.rs
+++ b/yazi-actor/src/mgr/update_yanked.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_core::mgr::Yanked;
use yazi_macro::{render, succ};
use yazi_parser::mgr::UpdateYankedOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/visual_mode.rs b/yazi-actor/src/mgr/visual_mode.rs
index c71c8f62..e805a96f 100644
--- a/yazi-actor/src/mgr/visual_mode.rs
+++ b/yazi-actor/src/mgr/visual_mode.rs
@@ -4,7 +4,7 @@ use anyhow::Result;
use yazi_core::tab::Mode;
use yazi_macro::{render, succ};
use yazi_parser::mgr::VisualModeOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/watch.rs b/yazi-actor/src/mgr/watch.rs
index a25591a6..48c28fef 100644
--- a/yazi-actor/src/mgr/watch.rs
+++ b/yazi-actor/src/mgr/watch.rs
@@ -3,7 +3,7 @@ use std::iter;
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/mgr/yank.rs b/yazi-actor/src/mgr/yank.rs
index 4926cbaf..6ba2569a 100644
--- a/yazi-actor/src/mgr/yank.rs
+++ b/yazi-actor/src/mgr/yank.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_core::mgr::Yanked;
use yazi_macro::{act, render};
use yazi_parser::mgr::YankOpt;
-use yazi_shared::{event::Data, url::UrlBufCov};
+use yazi_shared::{data::Data, url::UrlBufCov};
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/pick/arrow.rs b/yazi-actor/src/pick/arrow.rs
index af3934a9..0dbbdd27 100644
--- a/yazi-actor/src/pick/arrow.rs
+++ b/yazi-actor/src/pick/arrow.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use yazi_widgets::Scrollable;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/pick/close.rs b/yazi-actor/src/pick/close.rs
index 5028d66e..2962ab57 100644
--- a/yazi-actor/src/pick/close.rs
+++ b/yazi-actor/src/pick/close.rs
@@ -1,7 +1,7 @@
use anyhow::{Result, anyhow};
use yazi_macro::{render, succ};
use yazi_parser::pick::CloseOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/pick/show.rs b/yazi-actor/src/pick/show.rs
index ab653e81..8eecc38c 100644
--- a/yazi-actor/src/pick/show.rs
+++ b/yazi-actor/src/pick/show.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::pick::ShowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/spot/arrow.rs b/yazi-actor/src/spot/arrow.rs
index 2384a96e..5e2d3b7c 100644
--- a/yazi-actor/src/spot/arrow.rs
+++ b/yazi-actor/src/spot/arrow.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/spot/close.rs b/yazi-actor/src/spot/close.rs
index a14a18d4..d7656f43 100644
--- a/yazi-actor/src/spot/close.rs
+++ b/yazi-actor/src/spot/close.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/spot/copy.rs b/yazi-actor/src/spot/copy.rs
index 0cbf26a2..65ea3e4a 100644
--- a/yazi-actor/src/spot/copy.rs
+++ b/yazi-actor/src/spot/copy.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::spot::CopyOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use yazi_widgets::CLIPBOARD;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/spot/swipe.rs b/yazi-actor/src/spot/swipe.rs
index d2801424..f46c9d02 100644
--- a/yazi-actor/src/spot/swipe.rs
+++ b/yazi-actor/src/spot/swipe.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::act;
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/tasks/arrow.rs b/yazi-actor/src/tasks/arrow.rs
index 2289123b..66eccbd9 100644
--- a/yazi-actor/src/tasks/arrow.rs
+++ b/yazi-actor/src/tasks/arrow.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_core::tasks::Tasks;
use yazi_macro::{render, succ};
use yazi_parser::ArrowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/tasks/cancel.rs b/yazi-actor/src/tasks/cancel.rs
index 61acc0e0..bcb0219f 100644
--- a/yazi-actor/src/tasks/cancel.rs
+++ b/yazi-actor/src/tasks/cancel.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/tasks/close.rs b/yazi-actor/src/tasks/close.rs
index 386b28ca..bf0f87ef 100644
--- a/yazi-actor/src/tasks/close.rs
+++ b/yazi-actor/src/tasks/close.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/tasks/inspect.rs b/yazi-actor/src/tasks/inspect.rs
index 5d56a8d2..f3bd889d 100644
--- a/yazi-actor/src/tasks/inspect.rs
+++ b/yazi-actor/src/tasks/inspect.rs
@@ -7,7 +7,7 @@ use tokio::{io::{AsyncReadExt, stdin}, select, sync::mpsc, time};
use yazi_macro::succ;
use yazi_parser::VoidOpt;
use yazi_proxy::{AppProxy, HIDER};
-use yazi_shared::{event::Data, terminal_clear};
+use yazi_shared::{data::Data, terminal_clear};
use yazi_term::tty::TTY;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/tasks/open_with.rs b/yazi-actor/src/tasks/open_with.rs
index ee73c406..b927d1a0 100644
--- a/yazi-actor/src/tasks/open_with.rs
+++ b/yazi-actor/src/tasks/open_with.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::mgr::OpenWithOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
@@ -16,7 +16,7 @@ impl Actor for OpenWith {
succ!(cx.tasks.process_from_opener(
opt.cwd,
opt.opener,
- opt.targets.into_iter().map(|u| u.into_path2().into_os_string()).collect(),
+ opt.targets.into_iter().map(|u| u.into_os_str2()).collect(),
));
}
}
diff --git a/yazi-actor/src/tasks/process_exec.rs b/yazi-actor/src/tasks/process_exec.rs
index bdb3586b..c5ece66f 100644
--- a/yazi-actor/src/tasks/process_exec.rs
+++ b/yazi-actor/src/tasks/process_exec.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::tasks::ProcessExecOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/tasks/show.rs b/yazi-actor/src/tasks/show.rs
index a47b3175..1827b265 100644
--- a/yazi-actor/src/tasks/show.rs
+++ b/yazi-actor/src/tasks/show.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/tasks/update_succeed.rs b/yazi-actor/src/tasks/update_succeed.rs
index e10b7dcd..cf3b78c5 100644
--- a/yazi-actor/src/tasks/update_succeed.rs
+++ b/yazi-actor/src/tasks/update_succeed.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::tasks::UpdateSucceedOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/which/callback.rs b/yazi-actor/src/which/callback.rs
index 508f24af..7a672f65 100644
--- a/yazi-actor/src/which/callback.rs
+++ b/yazi-actor/src/which/callback.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::which::CallbackOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-actor/src/which/show.rs b/yazi-actor/src/which/show.rs
index 0bbdede9..40b13c25 100644
--- a/yazi-actor/src/which/show.rs
+++ b/yazi-actor/src/which/show.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::which::ShowOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Actor, Ctx};
diff --git a/yazi-boot/src/actions/debug.rs b/yazi-boot/src/actions/debug.rs
index 1563812c..414486d7 100644
--- a/yazi-boot/src/actions/debug.rs
+++ b/yazi-boot/src/actions/debug.rs
@@ -1,9 +1,9 @@
-use std::{env, ffi::OsStr, fmt::Write, path::Path};
+use std::{env, ffi::OsStr, fmt::Write};
use regex::Regex;
use yazi_adapter::Mux;
use yazi_config::YAZI;
-use yazi_shared::{timestamp_us, url::UrlBuf};
+use yazi_shared::{timestamp_us, url::Url};
use super::Actions;
@@ -59,17 +59,17 @@ impl Actions {
writeln!(
s,
" default : {:?}",
- YAZI.opener.first(YAZI.open.all(UrlBuf::from(Path::new("f75a.txt")), "text/plain"))
+ YAZI.opener.first(YAZI.open.all(Url::regular("f75a.txt"), "text/plain"))
)?;
writeln!(
s,
" block-create: {:?}",
- YAZI.opener.block(YAZI.open.all(UrlBuf::from(Path::new("bulk-create.txt")), "text/plain"))
+ YAZI.opener.block(YAZI.open.all(Url::regular("bulk-create.txt"), "text/plain"))
)?;
writeln!(
s,
" block-rename: {:?}",
- YAZI.opener.block(YAZI.open.all(UrlBuf::from(Path::new("bulk-rename.txt")), "text/plain"))
+ YAZI.opener.block(YAZI.open.all(Url::regular("bulk-rename.txt"), "text/plain"))
)?;
writeln!(s, "\nMultiplexers")?;
diff --git a/yazi-cli/src/package/hash.rs b/yazi-cli/src/package/hash.rs
index 1730b464..88430f01 100644
--- a/yazi-cli/src/package/hash.rs
+++ b/yazi-cli/src/package/hash.rs
@@ -1,6 +1,7 @@
use anyhow::{Context, Result, bail};
use twox_hash::XxHash3_128;
-use yazi_fs::{ok_or_not_found, provider::{DirReader, FileHolder, Provider, local::Local}};
+use yazi_fs::provider::{DirReader, FileHolder, Provider, local::Local};
+use yazi_macro::ok_or_not_found;
use super::Dependency;
@@ -14,7 +15,7 @@ impl Dependency {
for file in files {
h.write(file.as_bytes());
h.write(b"VpvFw9Atb7cWGOdqhZCra634CcJJRlsRl72RbZeV0vpG1\0");
- h.write(&ok_or_not_found(Local.read(dir.join(file)).await)?);
+ h.write(&ok_or_not_found!(Local.read(dir.join(file)).await));
}
let mut assets = vec![];
diff --git a/yazi-cli/src/package/package.rs b/yazi-cli/src/package/package.rs
index f07cfe26..617635da 100644
--- a/yazi-cli/src/package/package.rs
+++ b/yazi-cli/src/package/package.rs
@@ -3,7 +3,7 @@ use std::{path::PathBuf, str::FromStr};
use anyhow::{Context, Result, bail};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use yazi_fs::{Xdg, provider::{Provider, local::Local}};
-use yazi_macro::outln;
+use yazi_macro::{ok_or_not_found, outln};
use super::Dependency;
@@ -15,11 +15,8 @@ pub(crate) struct Package {
impl Package {
pub(crate) async fn load() -> Result {
- Ok(match Local.read_to_string(Self::toml()).await {
- Ok(s) => toml::from_str(&s)?,
- Err(e) if e.kind() == std::io::ErrorKind::NotFound => Self::default(),
- Err(e) => Err(e)?,
- })
+ let s = ok_or_not_found!(Local.read_to_string(Self::toml()).await);
+ Ok(toml::from_str(&s)?)
}
pub(crate) async fn add_many(&mut self, uses: &[String]) -> Result<()> {
diff --git a/yazi-cli/src/shared/shared.rs b/yazi-cli/src/shared/shared.rs
index 23a27561..f2077dd9 100644
--- a/yazi-cli/src/shared/shared.rs
+++ b/yazi-cli/src/shared/shared.rs
@@ -1,7 +1,8 @@
use std::{io, path::Path};
use tokio::io::AsyncWriteExt;
-use yazi_fs::{ok_or_not_found, provider::{FileBuilder, Provider, local::{Gate, Local}}};
+use yazi_fs::provider::{FileBuilder, Provider, local::{Gate, Local}};
+use yazi_macro::ok_or_not_found;
#[inline]
pub async fn must_exists(path: impl AsRef) -> bool {
@@ -18,7 +19,7 @@ pub async fn maybe_exists(path: impl AsRef) -> bool {
pub async fn copy_and_seal(from: &Path, to: &Path) -> io::Result<()> {
let b = Local.read(from).await?;
- ok_or_not_found(remove_sealed(to).await)?;
+ ok_or_not_found!(remove_sealed(to).await);
let mut file = Gate::default().create_new(true).write(true).truncate(true).open(to).await?;
file.write_all(&b).await?;
diff --git a/yazi-config/preset/yazi-default.toml b/yazi-config/preset/yazi-default.toml
index fe16a463..42a431c0 100644
--- a/yazi-config/preset/yazi-default.toml
+++ b/yazi-config/preset/yazi-default.toml
@@ -34,6 +34,14 @@ edit = [
{ run = 'code %*', orphan = true, desc = "code", for = "windows" },
{ run = 'code -w %*', block = true, desc = "code (block)", for = "windows" },
]
+play = [
+ { run = 'xdg-open "$1"', desc = "Play", for = "linux" },
+ { run = 'open "$@"', desc = "Play", for = "macos" },
+ { run = 'start "" "%1"', orphan = true, desc = "Play", for = "windows" },
+ { run = 'termux-open "$1"', desc = "Play", for = "android" },
+ { run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" },
+ { run = 'mediainfo "%1" & pause', block = true, desc = "Show media info", for = "windows" },
+]
open = [
{ run = 'xdg-open "$1"', desc = "Open", for = "linux" },
{ run = 'open "$@"', desc = "Open", for = "macos" },
@@ -51,13 +59,11 @@ extract = [
{ run = 'ya pub extract --list "$@"', desc = "Extract here", for = "unix" },
{ run = 'ya pub extract --list %*', desc = "Extract here", for = "windows" },
]
-play = [
- { run = 'xdg-open "$1"', desc = "Play", for = "linux" },
- { run = 'open "$@"', desc = "Play", for = "macos" },
- { run = 'start "" "%1"', orphan = true, desc = "Play", for = "windows" },
- { run = 'termux-open "$1"', desc = "Play", for = "android" },
- { run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" },
- { run = 'mediainfo "%1" & pause', block = true, desc = "Show media info", for = "windows" },
+download = [
+ { run = 'ya emit download --open "$@"', desc = "Download and open", for = "unix" },
+ { run = 'ya emit download --open %*', desc = "Download and open", for = "windows" },
+ { run = 'ya emit download "$@"', desc = "Download", for = "unix" },
+ { run = 'ya emit download %*', desc = "Download", for = "windows" },
]
[open]
@@ -77,6 +83,8 @@ rules = [
{ mime = "*/javascript", use = [ "edit", "reveal" ] },
# Empty file
{ mime = "inode/empty", use = [ "edit", "reveal" ] },
+ # Virtual file system
+ { mime = "vfs/*", use = "download" },
# Fallback
{ url = "*", use = [ "open", "reveal" ] },
]
@@ -92,7 +100,8 @@ suppress_preload = false
[plugin]
fetchers = [
# Mimetype
- { id = "mime", url = "*", run = "mime", prio = "high" },
+ { id = "mime", url = "*/", run = "mime.dir", prio = "high" },
+ { id = "mime", url = "*", run = "mime.file", prio = "high" },
]
spotters = [
{ url = "*/", run = "folder" },
diff --git a/yazi-config/src/open/open.rs b/yazi-config/src/open/open.rs
index 424a3c91..4fcd7d70 100644
--- a/yazi-config/src/open/open.rs
+++ b/yazi-config/src/open/open.rs
@@ -4,7 +4,7 @@ use anyhow::Result;
use indexmap::IndexSet;
use serde::Deserialize;
use yazi_codegen::DeserializeOver2;
-use yazi_shared::{MIME_DIR, url::UrlBuf};
+use yazi_shared::url::AsUrl;
use crate::{Preset, open::OpenRule};
@@ -24,30 +24,31 @@ impl Deref for Open {
}
impl Open {
- pub fn all<'a, 'b, P, M>(&'a self, url: P, mime: M) -> impl Iterator
- + 'b
+ pub fn all<'a, 'b, U, M>(&'a self, url: U, mime: M) -> impl Iterator
- + 'b
where
'a: 'b,
- P: AsRef + 'b,
+ U: AsUrl + 'b,
M: AsRef + 'b,
{
- let is_dir = mime.as_ref() == MIME_DIR;
+ let is_dir = mime.as_ref().starts_with("folder/");
self
.rules
.iter()
.filter(move |&r| {
r.mime.as_ref().is_some_and(|p| p.match_mime(&mime))
- || r.url.as_ref().is_some_and(|p| p.match_url(&url, is_dir))
+ || r.url.as_ref().is_some_and(|p| p.match_url(url.as_url(), is_dir))
})
.flat_map(|r| &r.r#use)
.map(String::as_str)
}
- pub fn common<'a>(
- &'a self,
- targets: &[(impl AsRef, impl AsRef)],
- ) -> IndexSet<&'a str> {
+ pub fn common<'a, 'b, U, M>(&'a self, targets: &'b [(U, M)]) -> IndexSet<&'a str>
+ where
+ &'b U: AsUrl,
+ M: AsRef,
+ {
let each: Vec> = targets
- .iter()
+ .into_iter()
.map(|(u, m)| self.all(u, m).collect::>())
.filter(|s| !s.is_empty())
.collect();
diff --git a/yazi-config/src/pattern.rs b/yazi-config/src/pattern.rs
index e922ed98..66e0cfb6 100644
--- a/yazi-config/src/pattern.rs
+++ b/yazi-config/src/pattern.rs
@@ -3,7 +3,7 @@ use std::str::FromStr;
use anyhow::Result;
use globset::GlobBuilder;
use serde::Deserialize;
-use yazi_shared::{scheme::{SchemeCow, SchemeRef}, url::UrlBuf};
+use yazi_shared::{scheme::{SchemeCow, SchemeRef}, url::Url};
#[derive(Debug, Deserialize)]
#[serde(try_from = "String")]
@@ -17,14 +17,14 @@ pub struct Pattern {
}
impl Pattern {
- pub fn match_url(&self, url: impl AsRef, is_dir: bool) -> bool {
- let url = url.as_ref();
+ pub fn match_url<'a>(&self, url: impl Into>, is_dir: bool) -> bool {
+ let url = url.into();
if is_dir != self.is_dir {
return false;
} else if self.is_star {
return true;
- } else if !self.scheme.matches(&url.scheme) {
+ } else if !self.scheme.matches(url.scheme) {
return false;
}
@@ -121,10 +121,12 @@ impl PatternScheme {
// --- Tests
#[cfg(test)]
mod tests {
+ use yazi_shared::url::UrlCow;
+
use super::*;
fn matches(glob: &str, url: &str) -> bool {
- Pattern::from_str(glob).unwrap().match_url(UrlBuf::from_str(url).unwrap(), false)
+ Pattern::from_str(glob).unwrap().match_url(&UrlCow::try_from(url).unwrap(), false)
}
#[cfg(unix)]
diff --git a/yazi-config/src/plugin/fetcher.rs b/yazi-config/src/plugin/fetcher.rs
index a4d0f14a..6638c319 100644
--- a/yazi-config/src/plugin/fetcher.rs
+++ b/yazi-config/src/plugin/fetcher.rs
@@ -1,5 +1,6 @@
use serde::Deserialize;
-use yazi_shared::{MIME_DIR, event::Cmd, url::UrlBuf};
+use yazi_fs::File;
+use yazi_shared::event::Cmd;
use crate::{Pattern, Priority};
@@ -18,8 +19,8 @@ pub struct Fetcher {
impl Fetcher {
#[inline]
- pub fn matches(&self, url: &UrlBuf, mime: &str) -> bool {
+ pub fn matches(&self, file: &File, mime: &str) -> bool {
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
- || self.url.as_ref().is_some_and(|p| p.match_url(url, mime == MIME_DIR))
+ || self.url.as_ref().is_some_and(|p| p.match_url(&file.url, file.is_dir()))
}
}
diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs
index d4c54ed1..db83c78a 100644
--- a/yazi-config/src/plugin/plugin.rs
+++ b/yazi-config/src/plugin/plugin.rs
@@ -4,7 +4,6 @@ use serde::Deserialize;
use tracing::warn;
use yazi_codegen::DeserializeOver2;
use yazi_fs::File;
-use yazi_shared::url::UrlBuf;
use super::{Fetcher, Preloader, Previewer, Spotter};
use crate::{Preset, plugin::MAX_PREWORKERS};
@@ -39,12 +38,12 @@ pub struct Plugin {
impl Plugin {
pub fn fetchers<'a, 'b: 'a>(
&'b self,
- url: &'a UrlBuf,
+ file: &'a File,
mime: &'a str,
) -> impl Iterator
- + 'a {
let mut seen = HashSet::new();
self.fetchers.iter().filter(move |&f| {
- if seen.contains(&f.id) || !f.matches(url, mime) {
+ if seen.contains(&f.id) || !f.matches(file, mime) {
return false;
}
seen.insert(&f.id);
@@ -55,7 +54,7 @@ impl Plugin {
pub fn mime_fetchers(&self, files: Vec) -> impl Iterator
- )> {
let mut tasks: [Vec<_>; MAX_PREWORKERS as usize] = Default::default();
for f in files {
- let found = self.fetchers.iter().find(|&g| g.id == "mime" && g.matches(&f.url, ""));
+ let found = self.fetchers.iter().find(|&g| g.id == "mime" && g.matches(&f, ""));
if let Some(g) = found {
tasks[g.idx as usize].push(f);
} else {
@@ -68,18 +67,18 @@ impl Plugin {
})
}
- pub fn spotter(&self, url: &UrlBuf, mime: &str) -> Option<&Spotter> {
- self.spotters.iter().find(|&p| p.matches(url, mime))
+ pub fn spotter(&self, file: &File, mime: &str) -> Option<&Spotter> {
+ self.spotters.iter().find(|&p| p.matches(file, mime))
}
pub fn preloaders<'a, 'b: 'a>(
&'b self,
- url: &'a UrlBuf,
+ file: &'a File,
mime: &'a str,
) -> impl Iterator
- + 'a {
let mut next = true;
self.preloaders.iter().filter(move |&p| {
- if !next || !p.matches(url, mime) {
+ if !next || !p.matches(file, mime) {
return false;
}
next = p.next;
@@ -87,8 +86,8 @@ impl Plugin {
})
}
- pub fn previewer(&self, url: &UrlBuf, mime: &str) -> Option<&Previewer> {
- self.previewers.iter().find(|&p| p.matches(url, mime))
+ pub fn previewer(&self, file: &File, mime: &str) -> Option<&Previewer> {
+ self.previewers.iter().find(|&p| p.matches(file, mime))
}
}
diff --git a/yazi-config/src/plugin/preloader.rs b/yazi-config/src/plugin/preloader.rs
index b7ed9e3e..3af758ca 100644
--- a/yazi-config/src/plugin/preloader.rs
+++ b/yazi-config/src/plugin/preloader.rs
@@ -1,5 +1,6 @@
use serde::Deserialize;
-use yazi_shared::{MIME_DIR, event::Cmd, url::UrlBuf};
+use yazi_fs::File;
+use yazi_shared::event::Cmd;
use crate::{Pattern, Priority};
@@ -19,8 +20,8 @@ pub struct Preloader {
impl Preloader {
#[inline]
- pub fn matches(&self, url: &UrlBuf, mime: &str) -> bool {
+ pub fn matches(&self, file: &File, mime: &str) -> bool {
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
- || self.url.as_ref().is_some_and(|p| p.match_url(url, mime == MIME_DIR))
+ || self.url.as_ref().is_some_and(|p| p.match_url(&file.url, file.is_dir()))
}
}
diff --git a/yazi-config/src/plugin/previewer.rs b/yazi-config/src/plugin/previewer.rs
index 2aff9340..e98cb24c 100644
--- a/yazi-config/src/plugin/previewer.rs
+++ b/yazi-config/src/plugin/previewer.rs
@@ -1,5 +1,6 @@
use serde::Deserialize;
-use yazi_shared::{MIME_DIR, event::Cmd, url::UrlBuf};
+use yazi_fs::File;
+use yazi_shared::event::Cmd;
use crate::Pattern;
@@ -12,9 +13,9 @@ pub struct Previewer {
impl Previewer {
#[inline]
- pub fn matches(&self, url: &UrlBuf, mime: &str) -> bool {
+ pub fn matches(&self, file: &File, mime: &str) -> bool {
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
- || self.url.as_ref().is_some_and(|p| p.match_url(url, mime == MIME_DIR))
+ || self.url.as_ref().is_some_and(|p| p.match_url(&file.url, file.is_dir()))
}
#[inline]
diff --git a/yazi-config/src/plugin/spotter.rs b/yazi-config/src/plugin/spotter.rs
index f8a1d3eb..9994370a 100644
--- a/yazi-config/src/plugin/spotter.rs
+++ b/yazi-config/src/plugin/spotter.rs
@@ -1,5 +1,6 @@
use serde::Deserialize;
-use yazi_shared::{MIME_DIR, event::Cmd, url::UrlBuf};
+use yazi_fs::File;
+use yazi_shared::event::Cmd;
use crate::Pattern;
@@ -12,9 +13,9 @@ pub struct Spotter {
impl Spotter {
#[inline]
- pub fn matches(&self, url: &UrlBuf, mime: &str) -> bool {
+ pub fn matches(&self, file: &File, mime: &str) -> bool {
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
- || self.url.as_ref().is_some_and(|p| p.match_url(url, mime == MIME_DIR))
+ || self.url.as_ref().is_some_and(|p| p.match_url(&file.url, file.is_dir()))
}
#[inline]
diff --git a/yazi-config/src/vfs/provider.rs b/yazi-config/src/vfs/provider.rs
index e21a7700..24f5b5f0 100644
--- a/yazi-config/src/vfs/provider.rs
+++ b/yazi-config/src/vfs/provider.rs
@@ -1,6 +1,8 @@
use std::{io, path::PathBuf};
use serde::{Deserialize, Serialize};
+use yazi_fs::path::expand_url;
+use yazi_shared::url::Url;
#[derive(Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
@@ -19,9 +21,9 @@ impl TryFrom<&'static Provider> for &'static ProviderSftp {
}
impl Provider {
- pub(super) fn reshape(self) -> io::Result {
+ pub(super) fn reshape(&mut self) -> io::Result<()> {
match self {
- Self::Sftp(p) => p.reshape().map(Self::Sftp),
+ Self::Sftp(p) => p.reshape(),
}
}
}
@@ -33,18 +35,32 @@ pub struct ProviderSftp {
pub user: String,
pub port: u16,
pub password: Option,
- pub key_file: Option,
+ #[serde(default)]
+ pub key_file: PathBuf,
pub key_passphrase: Option,
- // FIXME: set default: $SSH_AUTH_SOCK
- pub identity_agent: Option,
+ #[serde(default)]
+ pub identity_agent: PathBuf,
}
impl ProviderSftp {
- fn reshape(self) -> io::Result {
- // FIXME: expand the path
- // if let Some(key_file) = self.key_file {}
- // if let Some(identity_agent) = self.identity_agent {}
+ fn reshape(&mut self) -> io::Result<()> {
+ if !self.key_file.as_os_str().is_empty() {
+ self.key_file = expand_url(Url::regular(&self.key_file))
+ .into_path()
+ .ok_or_else(|| io::Error::other("key_file must be a path within local filesystem"))?;
+ }
- Ok(self)
+ self.identity_agent = if self.identity_agent.as_os_str().is_empty() {
+ std::env::var_os("SSH_AUTH_SOCK")
+ .map(PathBuf::from)
+ .filter(|p| p.is_absolute())
+ .unwrap_or_default()
+ } else {
+ expand_url(Url::regular(&self.identity_agent))
+ .into_path()
+ .ok_or_else(|| io::Error::other("identity_agent must be a path within local filesystem"))?
+ };
+
+ Ok(())
}
}
diff --git a/yazi-config/src/vfs/vfs.rs b/yazi-config/src/vfs/vfs.rs
index 715a34a0..3459ad82 100644
--- a/yazi-config/src/vfs/vfs.rs
+++ b/yazi-config/src/vfs/vfs.rs
@@ -47,13 +47,15 @@ impl Vfs {
})))
}
- fn reshape(self) -> io::Result {
- for name in self.providers.keys() {
+ fn reshape(mut self) -> io::Result {
+ for (name, provider) in &mut self.providers {
if name.is_empty() || name.len() > 20 {
Err(io::Error::other(format!("VFS name `{name}` must be between 1 and 20 characters")))?;
} else if !name.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'z' | b'-')) {
Err(io::Error::other(format!("VFS name `{name}` must be in kebab-case")))?;
}
+
+ provider.reshape()?;
}
Ok(self)
diff --git a/yazi-core/src/mgr/mimetype.rs b/yazi-core/src/mgr/mimetype.rs
index 029232cf..56f104f5 100644
--- a/yazi-core/src/mgr/mimetype.rs
+++ b/yazi-core/src/mgr/mimetype.rs
@@ -1,27 +1,18 @@
use hashbrown::HashMap;
-use yazi_fs::File;
-use yazi_shared::{MIME_DIR, pool::{InternStr, Symbol}, url::{Url, UrlBufCov, UrlCov}};
+use yazi_shared::{pool::Symbol, url::{Url, UrlBufCov, UrlCov}};
#[derive(Default)]
pub struct Mimetype(HashMap>);
impl Mimetype {
- pub fn by_url<'a>(&self, url: impl Into>) -> Option<&str> {
+ pub fn get<'a, 'b>(&'a self, url: impl Into>) -> Option<&'a str> {
self.0.get(&UrlCov::new(url)).map(|s| s.as_ref())
}
- pub fn by_url_owned<'a>(&self, url: impl Into>) -> Option> {
+ pub fn owned<'a>(&self, url: impl Into>) -> Option> {
self.0.get(&UrlCov::new(url)).cloned()
}
- pub fn by_file(&self, file: &File) -> Option<&str> {
- if file.is_dir() { Some(MIME_DIR) } else { self.by_url(&file.url) }
- }
-
- pub fn by_file_owned(&self, file: &File) -> Option> {
- if file.is_dir() { Some(MIME_DIR.intern()) } else { self.by_url_owned(&file.url) }
- }
-
pub fn contains<'a>(&self, url: impl Into>) -> bool {
self.0.contains_key(&UrlCov::new(url))
}
diff --git a/yazi-core/src/spot/spot.rs b/yazi-core/src/spot/spot.rs
index 2e661146..34034168 100644
--- a/yazi-core/src/spot/spot.rs
+++ b/yazi-core/src/spot/spot.rs
@@ -22,7 +22,7 @@ impl Spot {
return;
}
- let Some(spotter) = YAZI.plugin.spotter(&file.url, &mime) else {
+ let Some(spotter) = YAZI.plugin.spotter(&file, &mime) else {
return self.reset();
};
diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs
index d1ad23ca..7d48d9f5 100644
--- a/yazi-core/src/tab/preview.rs
+++ b/yazi-core/src/tab/preview.rs
@@ -9,7 +9,7 @@ use yazi_fs::{File, Files, FilesOp, cha::Cha};
use yazi_macro::render;
use yazi_parser::mgr::PreviewLock;
use yazi_plugin::{external::Highlighter, isolate};
-use yazi_shared::{MIME_DIR, pool::{InternStr, Symbol}, url::UrlBuf};
+use yazi_shared::{pool::{InternStr, Symbol}, url::UrlBuf};
use yazi_vfs::{VfsFiles, VfsFilesOp};
#[derive(Default)]
@@ -29,7 +29,7 @@ impl Preview {
return;
}
- let Some(previewer) = YAZI.plugin.previewer(&file.url, &mime) else {
+ let Some(previewer) = YAZI.plugin.previewer(&file, &mime) else {
return self.reset();
};
@@ -38,16 +38,18 @@ impl Preview {
}
pub fn go_folder(&mut self, file: File, dir: Option, force: bool) {
- let same = self.same_file(&file, MIME_DIR);
+ const MIME: &str = "folder/lock";
+
+ let same = self.same_file(&file, MIME);
let (wd, cha, internal) = (file.url_owned(), file.cha, file.url.is_internal());
- self.go(file, MIME_DIR.intern(), force);
+ self.go(file, MIME.intern(), force);
if same || !internal {
return;
}
self.lock =
- Some(PreviewLock { url: wd.clone(), cha, mime: MIME_DIR.to_owned(), ..Default::default() });
+ Some(PreviewLock { url: wd.clone(), cha, mime: MIME.to_owned(), ..Default::default() });
self.folder_loader.take().map(|h| h.abort());
self.folder_loader = Some(tokio::spawn(async move {
let Some(new) = Files::assert_stale(&wd, dir.unwrap_or_default()).await else { return };
diff --git a/yazi-core/src/tasks/preload.rs b/yazi-core/src/tasks/preload.rs
index 42e7b716..3139f14e 100644
--- a/yazi-core/src/tasks/preload.rs
+++ b/yazi-core/src/tasks/preload.rs
@@ -10,7 +10,7 @@ impl Tasks {
let mut tasks: [Vec<_>; MAX_PREWORKERS as usize] = Default::default();
for f in paged {
let hash = f.hash_u64();
- for g in YAZI.plugin.fetchers(&f.url, mimetype.by_file(f).unwrap_or_default()) {
+ for g in YAZI.plugin.fetchers(f, mimetype.get(&f.url).unwrap_or_default()) {
match loaded.get_mut(&hash) {
Some(n) if *n & (1 << g.idx) != 0 => continue,
Some(n) => *n |= 1 << g.idx,
@@ -32,7 +32,7 @@ impl Tasks {
let mut loaded = self.scheduler.prework.loaded.lock();
for f in paged {
let hash = f.hash_u64();
- for p in YAZI.plugin.preloaders(&f.url, mimetype.by_file(f).unwrap_or_default()) {
+ for p in YAZI.plugin.preloaders(f, mimetype.get(&f.url).unwrap_or_default()) {
match loaded.get_mut(&hash) {
Some(n) if *n & (1 << p.idx) != 0 => continue,
Some(n) => *n |= 1 << p.idx,
diff --git a/yazi-core/src/tasks/process.rs b/yazi-core/src/tasks/process.rs
index efaca89d..72db4644 100644
--- a/yazi-core/src/tasks/process.rs
+++ b/yazi-core/src/tasks/process.rs
@@ -1,14 +1,19 @@
-use std::{borrow::Cow, ffi::OsString, mem};
+use std::{borrow::Cow, ffi::OsStr, mem};
use hashbrown::HashMap;
use yazi_config::{YAZI, opener::OpenerRule};
use yazi_parser::tasks::ProcessExecOpt;
-use yazi_shared::url::UrlBuf;
+use yazi_shared::url::{UrlBuf, UrlCow};
use super::Tasks;
impl Tasks {
- pub fn process_from_files(&self, cwd: UrlBuf, hovered: UrlBuf, targets: Vec<(UrlBuf, &str)>) {
+ pub fn process_from_files(
+ &self,
+ cwd: UrlBuf,
+ hovered: UrlCow<'static>,
+ targets: Vec<(UrlCow<'static>, &str)>,
+ ) {
let mut openers = HashMap::new();
for (url, mime) in targets {
if let Some(opener) = YAZI.opener.first(YAZI.open.all(&url, mime)) {
@@ -19,7 +24,7 @@ impl Tasks {
self.process_from_opener(
cwd.clone(),
Cow::Borrowed(opener),
- args.into_iter().map(|u| u.into_path2().into_os_string()).collect(),
+ args.into_iter().map(|u| u.into_os_str2()).collect(),
);
}
}
@@ -28,7 +33,7 @@ impl Tasks {
&self,
cwd: UrlBuf,
opener: Cow<'static, OpenerRule>,
- mut args: Vec,
+ mut args: Vec>,
) {
if opener.spread {
self.scheduler.process_open(ProcessExecOpt { cwd, opener, args, done: None });
diff --git a/yazi-dds/src/ember/custom.rs b/yazi-dds/src/ember/custom.rs
index e7fb40e1..4b05f30a 100644
--- a/yazi-dds/src/ember/custom.rs
+++ b/yazi-dds/src/ember/custom.rs
@@ -1,6 +1,6 @@
use mlua::{IntoLua, Lua, Value};
use serde::Serialize;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use super::Ember;
use crate::Sendable;
diff --git a/yazi-dds/src/sendable.rs b/yazi-dds/src/sendable.rs
index faa3bb06..a1703985 100644
--- a/yazi-dds/src/sendable.rs
+++ b/yazi-dds/src/sendable.rs
@@ -3,7 +3,7 @@ use std::{any::TypeId, borrow::Cow};
use hashbrown::HashMap;
use mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value};
use ordered_float::OrderedFloat;
-use yazi_shared::{event::{Data, DataKey}, replace_cow};
+use yazi_shared::{data::{Data, DataKey}, replace_cow};
pub struct Sendable;
diff --git a/yazi-dds/src/spark/spark.rs b/yazi-dds/src/spark/spark.rs
index e5c6eb36..8d881a8f 100644
--- a/yazi-dds/src/spark/spark.rs
+++ b/yazi-dds/src/spark/spark.rs
@@ -15,6 +15,7 @@ pub enum Spark<'a> {
Close(yazi_parser::mgr::CloseOpt),
Copy(yazi_parser::mgr::CopyOpt),
Create(yazi_parser::mgr::CreateOpt),
+ Download(yazi_parser::mgr::DownloadOpt),
Enter(yazi_parser::VoidOpt),
Escape(yazi_parser::mgr::EscapeOpt),
EscapeFilter(yazi_parser::VoidOpt),
@@ -137,6 +138,7 @@ impl<'a> IntoLua for Spark<'a> {
Self::Close(b) => b.into_lua(lua),
Self::Copy(b) => b.into_lua(lua),
Self::Create(b) => b.into_lua(lua),
+ Self::Download(b) => b.into_lua(lua),
Self::Enter(b) => b.into_lua(lua),
Self::Escape(b) => b.into_lua(lua),
Self::EscapeFilter(b) => b.into_lua(lua),
@@ -280,6 +282,7 @@ try_from_spark!(mgr::CdOpt, mgr:cd);
try_from_spark!(mgr::CloseOpt, mgr:close);
try_from_spark!(mgr::CopyOpt, mgr:copy);
try_from_spark!(mgr::CreateOpt, mgr:create);
+try_from_spark!(mgr::DownloadOpt, mgr:download);
try_from_spark!(mgr::EscapeOpt, mgr:escape);
try_from_spark!(mgr::FilterOpt, mgr:filter, mgr:filter_do);
try_from_spark!(mgr::FindArrowOpt, mgr:find_arrow);
diff --git a/yazi-fm/src/app/commands/accept_payload.rs b/yazi-fm/src/app/commands/accept_payload.rs
index a95b70eb..2648869f 100644
--- a/yazi-fm/src/app/commands/accept_payload.rs
+++ b/yazi-fm/src/app/commands/accept_payload.rs
@@ -6,7 +6,7 @@ use yazi_binding::runtime_mut;
use yazi_dds::{LOCAL, Payload, REMOTE};
use yazi_macro::succ;
use yazi_plugin::LUA;
-use yazi_shared::event::{CmdCow, Data};
+use yazi_shared::{data::Data, event::CmdCow};
use crate::app::App;
diff --git a/yazi-fm/src/app/commands/bootstrap.rs b/yazi-fm/src/app/commands/bootstrap.rs
index eb08a1ff..b4733cfd 100644
--- a/yazi-fm/src/app/commands/bootstrap.rs
+++ b/yazi-fm/src/app/commands/bootstrap.rs
@@ -3,7 +3,7 @@ use yazi_actor::Ctx;
use yazi_boot::BOOT;
use yazi_macro::act;
use yazi_parser::{VoidOpt, mgr::CdSource};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::app::App;
diff --git a/yazi-fm/src/app/commands/deprecate.rs b/yazi-fm/src/app/commands/deprecate.rs
index 96a36d7c..5537a547 100644
--- a/yazi-fm/src/app/commands/deprecate.rs
+++ b/yazi-fm/src/app/commands/deprecate.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::app::{DeprecateOpt, NotifyLevel, NotifyOpt};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::app::App;
@@ -9,7 +9,7 @@ impl App {
pub(crate) fn deprecate(&mut self, opt: DeprecateOpt) -> Result {
succ!(self.core.notify.push(NotifyOpt {
title: "Deprecated API".to_owned(),
- content: opt.content,
+ content: opt.content.into_owned(),
level: NotifyLevel::Warn,
timeout: std::time::Duration::from_secs(20),
}));
diff --git a/yazi-fm/src/app/commands/mouse.rs b/yazi-fm/src/app/commands/mouse.rs
index 70cefe6e..2cba3fe2 100644
--- a/yazi-fm/src/app/commands/mouse.rs
+++ b/yazi-fm/src/app/commands/mouse.rs
@@ -7,7 +7,7 @@ use yazi_config::YAZI;
use yazi_macro::succ;
use yazi_parser::app::MouseOpt;
use yazi_plugin::LUA;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::app::App;
diff --git a/yazi-fm/src/app/commands/notify.rs b/yazi-fm/src/app/commands/notify.rs
index 127a1876..49ad97dc 100644
--- a/yazi-fm/src/app/commands/notify.rs
+++ b/yazi-fm/src/app/commands/notify.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::app::NotifyOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::app::App;
diff --git a/yazi-fm/src/app/commands/plugin.rs b/yazi-fm/src/app/commands/plugin.rs
index 5a71d4a2..e8986f19 100644
--- a/yazi-fm/src/app/commands/plugin.rs
+++ b/yazi-fm/src/app/commands/plugin.rs
@@ -9,7 +9,7 @@ use yazi_macro::succ;
use yazi_parser::app::{PluginMode, PluginOpt};
use yazi_plugin::{LUA, loader::{LOADER, Loader}};
use yazi_proxy::AppProxy;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::app::App;
diff --git a/yazi-fm/src/app/commands/reflow.rs b/yazi-fm/src/app/commands/reflow.rs
index f6f19314..c0cfa38f 100644
--- a/yazi-fm/src/app/commands/reflow.rs
+++ b/yazi-fm/src/app/commands/reflow.rs
@@ -6,7 +6,7 @@ use yazi_actor::lives::Lives;
use yazi_config::LAYOUT;
use yazi_macro::{render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Root, app::App};
diff --git a/yazi-fm/src/app/commands/render.rs b/yazi-fm/src/app/commands/render.rs
index 8d71a41c..123ccfe6 100644
--- a/yazi-fm/src/app/commands/render.rs
+++ b/yazi-fm/src/app/commands/render.rs
@@ -7,7 +7,7 @@ use yazi_actor::{Ctx, lives::Lives};
use yazi_binding::elements::COLLISION;
use yazi_macro::{act, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::{Data, NEED_RENDER};
+use yazi_shared::{data::Data, event::NEED_RENDER};
use yazi_term::tty::TTY;
use crate::{app::App, root::Root};
diff --git a/yazi-fm/src/app/commands/resize.rs b/yazi-fm/src/app/commands/resize.rs
index 97f11e3b..24ab0a42 100644
--- a/yazi-fm/src/app/commands/resize.rs
+++ b/yazi-fm/src/app/commands/resize.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::act;
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::app::App;
diff --git a/yazi-fm/src/app/commands/resume.rs b/yazi-fm/src/app/commands/resume.rs
index b0a17ef4..1df67288 100644
--- a/yazi-fm/src/app/commands/resume.rs
+++ b/yazi-fm/src/app/commands/resume.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{Term, app::App};
diff --git a/yazi-fm/src/app/commands/stop.rs b/yazi-fm/src/app/commands/stop.rs
index 45c796c0..4868639b 100644
--- a/yazi-fm/src/app/commands/stop.rs
+++ b/yazi-fm/src/app/commands/stop.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::app::StopOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::app::App;
diff --git a/yazi-fm/src/app/commands/update_notify.rs b/yazi-fm/src/app/commands/update_notify.rs
index 369f0d1c..cb1a5e79 100644
--- a/yazi-fm/src/app/commands/update_notify.rs
+++ b/yazi-fm/src/app/commands/update_notify.rs
@@ -3,7 +3,7 @@ use ratatui::layout::Rect;
use yazi_adapter::Dimension;
use yazi_macro::act;
use yazi_parser::notify::TickOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::{app::App, notify};
diff --git a/yazi-fm/src/app/commands/update_progress.rs b/yazi-fm/src/app/commands/update_progress.rs
index b406fde8..6931a12d 100644
--- a/yazi-fm/src/app/commands/update_progress.rs
+++ b/yazi-fm/src/app/commands/update_progress.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::{act, render, succ};
use yazi_parser::app::UpdateProgressOpt;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use crate::app::App;
diff --git a/yazi-fm/src/dispatcher.rs b/yazi-fm/src/dispatcher.rs
index 83025aba..ebc2bd73 100644
--- a/yazi-fm/src/dispatcher.rs
+++ b/yazi-fm/src/dispatcher.rs
@@ -4,7 +4,7 @@ use anyhow::Result;
use crossterm::event::KeyEvent;
use yazi_config::keymap::Key;
use yazi_macro::{act, emit, succ};
-use yazi_shared::event::{CmdCow, Data, Event, NEED_RENDER};
+use yazi_shared::{data::Data, event::{CmdCow, Event, NEED_RENDER}};
use yazi_widgets::input::InputMode;
use crate::{Executor, Router, app::App};
diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs
index 5dfea864..84b3151e 100644
--- a/yazi-fm/src/executor.rs
+++ b/yazi-fm/src/executor.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::{act, succ};
-use yazi_shared::{Layer, event::{CmdCow, Data}};
+use yazi_shared::{Layer, data::Data, event::CmdCow};
use yazi_widgets::input::InputMode;
use crate::app::App;
@@ -134,6 +134,9 @@ impl<'a> Executor<'a> {
on!(tab_switch);
on!(tab_swap);
+ // VFS
+ on!(download);
+
match cmd.name.as_ref() {
// Help
"help" => act!(help:toggle, cx, Layer::Mgr),
diff --git a/yazi-fs/src/url.rs b/yazi-fs/src/url.rs
index 3b4137ad..6b2e00e3 100644
--- a/yazi-fs/src/url.rs
+++ b/yazi-fs/src/url.rs
@@ -1,7 +1,7 @@
use std::path::PathBuf;
use twox_hash::XxHash3_128;
-use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf}};
+use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf, UrlCow}};
use crate::Xdg;
@@ -30,3 +30,7 @@ impl FsUrl for Url<'_> {
impl FsUrl for UrlBuf {
fn cache(&self) -> Option { self.as_url().cache() }
}
+
+impl FsUrl for UrlCow<'_> {
+ fn cache(&self) -> Option { self.as_url().cache() }
+}
diff --git a/yazi-macro/src/event.rs b/yazi-macro/src/event.rs
index a42092b4..2dc0447e 100644
--- a/yazi-macro/src/event.rs
+++ b/yazi-macro/src/event.rs
@@ -30,9 +30,9 @@ macro_rules! relay {
#[macro_export]
macro_rules! succ {
($data:expr) => {
- return Ok(yazi_shared::event::Data::from($data))
+ return Ok(yazi_shared::data::Data::from($data))
};
() => {
- return Ok(yazi_shared::event::Data::Nil)
+ return Ok(yazi_shared::data::Data::Nil)
};
}
diff --git a/yazi-parser/src/app/deprecate.rs b/yazi-parser/src/app/deprecate.rs
index 6bd47bb6..10c72ed9 100644
--- a/yazi-parser/src/app/deprecate.rs
+++ b/yazi-parser/src/app/deprecate.rs
@@ -1,18 +1,18 @@
use anyhow::bail;
-use yazi_shared::event::CmdCow;
+use yazi_shared::{SStr, event::CmdCow};
pub struct DeprecateOpt {
- pub content: String,
+ pub content: SStr,
}
impl TryFrom for DeprecateOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result {
- let Some(content) = c.take_str("content") else {
+ let Ok(content) = c.take("content") else {
bail!("Invalid 'content' in DeprecateOpt");
};
- Ok(Self { content: content.into_owned() })
+ Ok(Self { content })
}
}
diff --git a/yazi-parser/src/app/plugin.rs b/yazi-parser/src/app/plugin.rs
index 75068fd2..4b7114bf 100644
--- a/yazi-parser/src/app/plugin.rs
+++ b/yazi-parser/src/app/plugin.rs
@@ -1,9 +1,10 @@
-use std::{borrow::Cow, fmt::Debug};
+use std::{borrow::Cow, fmt::Debug, str::FromStr};
use anyhow::bail;
use hashbrown::HashMap;
use mlua::{Lua, Table};
-use yazi_shared::{SStr, event::{Cmd, CmdCow, Data, DataKey}};
+use serde::Deserialize;
+use yazi_shared::{SStr, data::{Data, DataKey}, event::{Cmd, CmdCow}};
pub type PluginCallback = Box mlua::Result<()> + Send + Sync>;
@@ -23,18 +24,18 @@ impl TryFrom for PluginOpt {
return Ok(opt);
}
- let Some(id) = c.take_first_str().filter(|s| !s.is_empty()) else {
+ let Some(id) = c.take_first::().ok().filter(|s| !s.is_empty()) else {
bail!("plugin id cannot be empty");
};
- let args = if let Some(s) = c.second_str() {
+ let args = if let Ok(s) = c.second() {
let (words, last) = yazi_shared::shell::split_unix(s, true)?;
Cmd::parse_args(words.into_iter(), last, true)?
} else {
Default::default()
};
- let mode = c.str("mode").map(Into::into).unwrap_or_default();
+ let mode = c.str("mode").parse().unwrap_or_default();
Ok(Self { id: Self::normalize_id(id), args, mode, cb: c.take_any("callback") })
}
}
@@ -72,7 +73,8 @@ impl PluginOpt {
}
// --- Mode
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
+#[serde(rename_all = "kebab-case")]
pub enum PluginMode {
#[default]
Auto,
@@ -80,13 +82,11 @@ pub enum PluginMode {
Async,
}
-impl From<&str> for PluginMode {
- fn from(s: &str) -> Self {
- match s {
- "sync" => Self::Sync,
- "async" => Self::Async,
- _ => Self::Auto,
- }
+impl FromStr for PluginMode {
+ type Err = serde::de::value::Error;
+
+ fn from_str(s: &str) -> Result {
+ Self::deserialize(serde::de::value::StrDeserializer::new(s))
}
}
diff --git a/yazi-parser/src/arrow.rs b/yazi-parser/src/arrow.rs
index a8a164c4..9aa9627d 100644
--- a/yazi-parser/src/arrow.rs
+++ b/yazi-parser/src/arrow.rs
@@ -13,11 +13,11 @@ impl TryFrom for ArrowOpt {
type Error = anyhow::Error;
fn try_from(c: CmdCow) -> Result {
- let Some(step) = c.first() else {
+ let Ok(step) = c.first() else {
bail!("'step' is required for ArrowOpt");
};
- Ok(Self { step: step.try_into()? })
+ Ok(Self { step })
}
}
diff --git a/yazi-parser/src/cmp/trigger.rs b/yazi-parser/src/cmp/trigger.rs
index c5b310e2..2e60f2d0 100644
--- a/yazi-parser/src/cmp/trigger.rs
+++ b/yazi-parser/src/cmp/trigger.rs
@@ -9,7 +9,7 @@ pub struct TriggerOpt {
impl From for TriggerOpt {
fn from(mut c: CmdCow) -> Self {
- Self { word: c.take_first_str().unwrap_or_default(), ticket: c.id("ticket") }
+ Self { word: c.take_first().unwrap_or_default(), ticket: c.get("ticket").ok() }
}
}
diff --git a/yazi-parser/src/help/toggle.rs b/yazi-parser/src/help/toggle.rs
index c841ab54..f333bbd5 100644
--- a/yazi-parser/src/help/toggle.rs
+++ b/yazi-parser/src/help/toggle.rs
@@ -1,4 +1,3 @@
-use anyhow::bail;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use yazi_shared::{Layer, event::CmdCow};
@@ -10,13 +9,7 @@ pub struct ToggleOpt {
impl TryFrom for ToggleOpt {
type Error = anyhow::Error;
- fn try_from(c: CmdCow) -> Result {
- let Some(layer) = c.first_str() else {
- bail!("Invalid 'layer' in Toggle");
- };
-
- Ok(Self { layer: layer.parse()? })
- }
+ fn try_from(c: CmdCow) -> Result { Ok(Self { layer: c.str(0).parse()? }) }
}
impl From for ToggleOpt {
diff --git a/yazi-parser/src/input/complete.rs b/yazi-parser/src/input/complete.rs
index b4db4c56..557aad21 100644
--- a/yazi-parser/src/input/complete.rs
+++ b/yazi-parser/src/input/complete.rs
@@ -18,7 +18,7 @@ impl TryFrom for CompleteOpt {
bail!("Invalid 'item' in CompleteOpt");
};
- Ok(Self { item, _ticket: c.id("ticket").unwrap_or_default() })
+ Ok(Self { item, _ticket: c.get("ticket").unwrap_or_default() })
}
}
diff --git a/yazi-parser/src/input/kill.rs b/yazi-parser/src/input/kill.rs
index 267e5f6b..51cbd962 100644
--- a/yazi-parser/src/input/kill.rs
+++ b/yazi-parser/src/input/kill.rs
@@ -7,7 +7,7 @@ pub struct KillOpt {
}
impl From for KillOpt {
- fn from(mut c: CmdCow) -> Self { Self { kind: c.take_first_str().unwrap_or_default() } }
+ fn from(mut c: CmdCow) -> Self { Self { kind: c.take_first().unwrap_or_default() } }
}
impl FromLua for KillOpt {
diff --git a/yazi-parser/src/input/move.rs b/yazi-parser/src/input/move.rs
index c2b14ab9..8870e7bc 100644
--- a/yazi-parser/src/input/move.rs
+++ b/yazi-parser/src/input/move.rs
@@ -2,7 +2,7 @@ use std::{num::ParseIntError, str::FromStr};
use anyhow::bail;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::event::{CmdCow, Data};
+use yazi_shared::{data::Data, event::CmdCow};
#[derive(Debug, Default)]
pub struct MoveOpt {
@@ -12,10 +12,7 @@ pub struct MoveOpt {
impl From for MoveOpt {
fn from(c: CmdCow) -> Self {
- Self {
- step: c.first().and_then(|d| d.try_into().ok()).unwrap_or_default(),
- in_operating: c.bool("in-operating"),
- }
+ Self { step: c.first().ok().unwrap_or_default(), in_operating: c.bool("in-operating") }
}
}
diff --git a/yazi-parser/src/mgr/cd.rs b/yazi-parser/src/mgr/cd.rs
index ba7578a7..d26c3baa 100644
--- a/yazi-parser/src/mgr/cd.rs
+++ b/yazi-parser/src/mgr/cd.rs
@@ -11,7 +11,7 @@ pub struct CdOpt {
impl From for CdOpt {
fn from(mut c: CmdCow) -> Self {
- let mut target = c.take_first_url().unwrap_or_default();
+ let mut target = c.take_first().unwrap_or_default();
if !c.bool("raw") {
target = expand_url(target).into();
diff --git a/yazi-parser/src/mgr/copy.rs b/yazi-parser/src/mgr/copy.rs
index a47b083e..6468bd17 100644
--- a/yazi-parser/src/mgr/copy.rs
+++ b/yazi-parser/src/mgr/copy.rs
@@ -1,6 +1,7 @@
-use std::{borrow::Cow, ffi::OsStr, path::Path};
+use std::{borrow::Cow, ffi::OsStr, path::Path, str::FromStr};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
+use serde::Deserialize;
use yazi_shared::{SStr, event::CmdCow};
#[derive(Debug)]
@@ -13,8 +14,8 @@ pub struct CopyOpt {
impl From for CopyOpt {
fn from(mut c: CmdCow) -> Self {
Self {
- r#type: c.take_first_str().unwrap_or_default(),
- separator: c.str("separator").unwrap_or_default().into(),
+ r#type: c.take_first().unwrap_or_default(),
+ separator: c.str("separator").parse().unwrap_or_default(),
hovered: c.bool("hovered"),
}
}
@@ -29,18 +30,19 @@ impl IntoLua for CopyOpt {
}
// --- Separator
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Deserialize)]
+#[serde(rename_all = "kebab-case")]
pub enum CopySeparator {
+ #[default]
Auto,
Unix,
}
-impl From<&str> for CopySeparator {
- fn from(value: &str) -> Self {
- match value {
- "unix" => Self::Unix,
- _ => Self::Auto,
- }
+impl FromStr for CopySeparator {
+ type Err = serde::de::value::Error;
+
+ fn from_str(s: &str) -> Result {
+ Self::deserialize(serde::de::value::StrDeserializer::new(s))
}
}
diff --git a/yazi-parser/src/mgr/download.rs b/yazi-parser/src/mgr/download.rs
new file mode 100644
index 00000000..a22c85e1
--- /dev/null
+++ b/yazi-parser/src/mgr/download.rs
@@ -0,0 +1,20 @@
+use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
+use yazi_shared::{event::CmdCow, url::UrlCow};
+
+#[derive(Debug, Default)]
+pub struct DownloadOpt {
+ pub urls: Vec>,
+ pub open: bool,
+}
+
+impl From for DownloadOpt {
+ fn from(mut c: CmdCow) -> Self { Self { urls: c.take_seq(), open: c.bool("open") } }
+}
+
+impl FromLua for DownloadOpt {
+ fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) }
+}
+
+impl IntoLua for DownloadOpt {
+ fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) }
+}
diff --git a/yazi-parser/src/mgr/escape.rs b/yazi-parser/src/mgr/escape.rs
index c4457362..145b0390 100644
--- a/yazi-parser/src/mgr/escape.rs
+++ b/yazi-parser/src/mgr/escape.rs
@@ -16,7 +16,7 @@ bitflags! {
impl From for EscapeOpt {
fn from(c: CmdCow) -> Self {
c.args.iter().fold(Self::empty(), |acc, (k, v)| {
- match (k.as_str().unwrap_or(""), v.as_bool().unwrap_or(false)) {
+ match (k.as_str().unwrap_or(""), v.try_into().unwrap_or(false)) {
("all", true) => Self::all(),
("find", true) => acc | Self::FIND,
("visual", true) => acc | Self::VISUAL,
diff --git a/yazi-parser/src/mgr/filter.rs b/yazi-parser/src/mgr/filter.rs
index b84b16cb..a6f693a3 100644
--- a/yazi-parser/src/mgr/filter.rs
+++ b/yazi-parser/src/mgr/filter.rs
@@ -18,7 +18,7 @@ impl TryFrom for FilterOpt {
}
Ok(Self {
- query: c.take_first_str().unwrap_or_default(),
+ query: c.take_first().unwrap_or_default(),
case: FilterCase::from(&*c),
done: c.bool("done"),
})
diff --git a/yazi-parser/src/mgr/find_do.rs b/yazi-parser/src/mgr/find_do.rs
index d52bf5fc..28438523 100644
--- a/yazi-parser/src/mgr/find_do.rs
+++ b/yazi-parser/src/mgr/find_do.rs
@@ -18,7 +18,7 @@ impl TryFrom for FindDoOpt {
return opt;
}
- let Some(query) = c.take_first_str() else {
+ let Ok(query) = c.take_first() else {
bail!("'query' is required for FindDoOpt");
};
diff --git a/yazi-parser/src/mgr/hidden.rs b/yazi-parser/src/mgr/hidden.rs
index 99069bc0..36f84d27 100644
--- a/yazi-parser/src/mgr/hidden.rs
+++ b/yazi-parser/src/mgr/hidden.rs
@@ -13,7 +13,7 @@ impl TryFrom for HiddenOpt {
type Error = anyhow::Error;
fn try_from(c: CmdCow) -> Result {
- Ok(Self { state: c.first_str().map(FromStr::from_str).transpose()?.unwrap_or_default() })
+ Ok(Self { state: c.str(0).parse().unwrap_or_default() })
}
}
diff --git a/yazi-parser/src/mgr/linemode.rs b/yazi-parser/src/mgr/linemode.rs
index b37985a5..6c416b89 100644
--- a/yazi-parser/src/mgr/linemode.rs
+++ b/yazi-parser/src/mgr/linemode.rs
@@ -11,7 +11,7 @@ impl TryFrom for LinemodeOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result {
- let Some(new) = c.take_first_str() else {
+ let Ok(new) = c.take_first::() else {
bail!("a string argument is required for LinemodeOpt");
};
diff --git a/yazi-parser/src/mgr/mod.rs b/yazi-parser/src/mgr/mod.rs
index 59e083df..18a27e70 100644
--- a/yazi-parser/src/mgr/mod.rs
+++ b/yazi-parser/src/mgr/mod.rs
@@ -3,6 +3,7 @@ yazi_macro::mod_flat!(
close
copy
create
+ download
escape
filter
find
diff --git a/yazi-parser/src/mgr/open.rs b/yazi-parser/src/mgr/open.rs
index 552a71d6..95d05397 100644
--- a/yazi-parser/src/mgr/open.rs
+++ b/yazi-parser/src/mgr/open.rs
@@ -1,15 +1,20 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::event::CmdCow;
+use yazi_shared::{event::CmdCow, url::UrlCow};
-#[derive(Clone, Copy, Debug)]
+#[derive(Debug)]
pub struct OpenOpt {
+ pub targets: Vec>,
pub interactive: bool,
pub hovered: bool,
}
impl From for OpenOpt {
- fn from(c: CmdCow) -> Self {
- Self { interactive: c.bool("interactive"), hovered: c.bool("hovered") }
+ fn from(mut c: CmdCow) -> Self {
+ Self {
+ targets: c.take_seq(),
+ interactive: c.bool("interactive"),
+ hovered: c.bool("hovered"),
+ }
}
}
diff --git a/yazi-parser/src/mgr/open_do.rs b/yazi-parser/src/mgr/open_do.rs
index eaf55cbc..38c7cc4a 100644
--- a/yazi-parser/src/mgr/open_do.rs
+++ b/yazi-parser/src/mgr/open_do.rs
@@ -1,11 +1,11 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::{event::CmdCow, url::UrlBuf};
+use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
#[derive(Debug, Default)]
pub struct OpenDoOpt {
pub cwd: UrlBuf,
- pub hovered: UrlBuf,
- pub targets: Vec<(UrlBuf, &'static str)>,
+ pub hovered: UrlCow<'static>,
+ pub targets: Vec>,
pub interactive: bool,
}
diff --git a/yazi-parser/src/mgr/open_with.rs b/yazi-parser/src/mgr/open_with.rs
index 300ef569..229adc93 100644
--- a/yazi-parser/src/mgr/open_with.rs
+++ b/yazi-parser/src/mgr/open_with.rs
@@ -3,13 +3,13 @@ use std::borrow::Cow;
use anyhow::anyhow;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use yazi_config::opener::OpenerRule;
-use yazi_shared::{event::CmdCow, url::UrlBuf};
+use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
#[derive(Debug)]
pub struct OpenWithOpt {
pub opener: Cow<'static, OpenerRule>,
pub cwd: UrlBuf,
- pub targets: Vec,
+ pub targets: Vec>,
}
impl TryFrom for OpenWithOpt {
diff --git a/yazi-parser/src/mgr/peek.rs b/yazi-parser/src/mgr/peek.rs
index d92a4181..881e5376 100644
--- a/yazi-parser/src/mgr/peek.rs
+++ b/yazi-parser/src/mgr/peek.rs
@@ -1,5 +1,5 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::{event::{CmdCow, Data}, url::UrlCow};
+use yazi_shared::{event::CmdCow, url::UrlCow};
#[derive(Debug, Default)]
pub struct PeekOpt {
@@ -12,9 +12,9 @@ pub struct PeekOpt {
impl From for PeekOpt {
fn from(mut c: CmdCow) -> Self {
Self {
- skip: c.first().and_then(Data::as_usize),
+ skip: c.first().ok(),
force: c.bool("force"),
- only_if: c.take_url("only-if"),
+ only_if: c.take("only-if").ok(),
upper_bound: c.bool("upper-bound"),
}
}
diff --git a/yazi-parser/src/mgr/quit.rs b/yazi-parser/src/mgr/quit.rs
index e2c9ef9c..057b40e8 100644
--- a/yazi-parser/src/mgr/quit.rs
+++ b/yazi-parser/src/mgr/quit.rs
@@ -1,6 +1,6 @@
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
use serde::{Deserialize, Serialize};
-use yazi_shared::event::{CmdCow, Data, EventQuit};
+use yazi_shared::event::{CmdCow, EventQuit};
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct QuitOpt {
@@ -10,10 +10,7 @@ pub struct QuitOpt {
impl From for QuitOpt {
fn from(c: CmdCow) -> Self {
- Self {
- code: c.get("code").and_then(Data::as_i32).unwrap_or_default(),
- no_cwd_file: c.bool("no-cwd-file"),
- }
+ Self { code: c.get("code").unwrap_or_default(), no_cwd_file: c.bool("no-cwd-file") }
}
}
diff --git a/yazi-parser/src/mgr/rename.rs b/yazi-parser/src/mgr/rename.rs
index 79b713f1..93830bfa 100644
--- a/yazi-parser/src/mgr/rename.rs
+++ b/yazi-parser/src/mgr/rename.rs
@@ -14,8 +14,8 @@ impl From for RenameOpt {
Self {
hovered: c.bool("hovered"),
force: c.bool("force"),
- empty: c.take_str("empty").unwrap_or_default(),
- cursor: c.take_str("cursor").unwrap_or_default(),
+ empty: c.take("empty").unwrap_or_default(),
+ cursor: c.take("cursor").unwrap_or_default(),
}
}
}
diff --git a/yazi-parser/src/mgr/reveal.rs b/yazi-parser/src/mgr/reveal.rs
index a3fe73f8..8b790982 100644
--- a/yazi-parser/src/mgr/reveal.rs
+++ b/yazi-parser/src/mgr/reveal.rs
@@ -13,7 +13,7 @@ pub struct RevealOpt {
impl From for RevealOpt {
fn from(mut c: CmdCow) -> Self {
- let mut target = c.take_first_url().unwrap_or_default();
+ let mut target = c.take_first().unwrap_or_default();
if !c.bool("raw") {
target = expand_url(target).into();
diff --git a/yazi-parser/src/mgr/search.rs b/yazi-parser/src/mgr/search.rs
index ffb7f1ec..ffef434e 100644
--- a/yazi-parser/src/mgr/search.rs
+++ b/yazi-parser/src/mgr/search.rs
@@ -1,5 +1,8 @@
+use std::str::FromStr;
+
use anyhow::bail;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
+use serde::Deserialize;
use yazi_shared::{SStr, event::CmdCow};
#[derive(Debug)]
@@ -15,13 +18,13 @@ impl TryFrom for SearchOpt {
fn try_from(mut c: CmdCow) -> Result {
// TODO: remove this
- let (via, subject) = if let Some(s) = c.take_str("via") {
- (s.as_ref().into(), c.take_first_str().unwrap_or_default())
+ let (via, subject) = if let Ok(s) = c.get("via") {
+ (str::parse(s)?, c.take_first().unwrap_or_default())
} else {
- (c.take_first_str().unwrap_or_default().as_ref().into(), "".into())
+ (c.str(0).parse()?, "".into())
};
- let Ok(args) = yazi_shared::shell::split_unix(c.str("args").unwrap_or_default(), false) else {
+ let Ok(args) = yazi_shared::shell::split_unix(c.str("args"), false) else {
bail!("Invalid 'args' argument in SearchOpt");
};
@@ -30,7 +33,7 @@ impl TryFrom for SearchOpt {
subject,
// TODO: use second positional argument instead of `args` parameter
args: args.0,
- args_raw: c.take_str("args").unwrap_or_default(),
+ args_raw: c.take("args").unwrap_or_default(),
})
}
}
@@ -44,20 +47,19 @@ impl IntoLua for SearchOpt {
}
// Via
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
+#[serde(rename_all = "kebab-case")]
pub enum SearchOptVia {
Rg,
Rga,
Fd,
}
-impl From<&str> for SearchOptVia {
- fn from(value: &str) -> Self {
- match value {
- "rg" => Self::Rg,
- "rga" => Self::Rga,
- _ => Self::Fd,
- }
+impl FromStr for SearchOptVia {
+ type Err = serde::de::value::Error;
+
+ fn from_str(s: &str) -> Result {
+ Self::deserialize(serde::de::value::StrDeserializer::new(s))
}
}
diff --git a/yazi-parser/src/mgr/seek.rs b/yazi-parser/src/mgr/seek.rs
index f64d36cb..bc8183da 100644
--- a/yazi-parser/src/mgr/seek.rs
+++ b/yazi-parser/src/mgr/seek.rs
@@ -1,5 +1,5 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::event::{CmdCow, Data};
+use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct SeekOpt {
@@ -7,7 +7,7 @@ pub struct SeekOpt {
}
impl From for SeekOpt {
- fn from(c: CmdCow) -> Self { Self { units: c.first().and_then(Data::as_i16).unwrap_or(0) } }
+ fn from(c: CmdCow) -> Self { Self { units: c.first().unwrap_or(0) } }
}
impl FromLua for SeekOpt {
diff --git a/yazi-parser/src/mgr/shell.rs b/yazi-parser/src/mgr/shell.rs
index f295621f..d4fcebc1 100644
--- a/yazi-parser/src/mgr/shell.rs
+++ b/yazi-parser/src/mgr/shell.rs
@@ -1,6 +1,6 @@
use anyhow::bail;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::{SStr, event::{CmdCow, Data}, url::UrlCow};
+use yazi_shared::{SStr, event::CmdCow, url::UrlCow};
#[derive(Debug)]
pub struct ShellOpt {
@@ -19,14 +19,14 @@ impl TryFrom for ShellOpt {
fn try_from(mut c: CmdCow) -> Result {
let me = Self {
- run: c.take_first_str().unwrap_or_default(),
- cwd: c.take_url("cwd"),
+ run: c.take_first().unwrap_or_default(),
+ cwd: c.take("cwd").ok(),
block: c.bool("block"),
orphan: c.bool("orphan"),
interactive: c.bool("interactive"),
- cursor: c.get("cursor").and_then(Data::as_usize),
+ cursor: c.get("cursor").ok(),
};
if me.cursor.is_some_and(|c| c > me.run.chars().count()) {
diff --git a/yazi-parser/src/mgr/sort.rs b/yazi-parser/src/mgr/sort.rs
index 93f35934..0e411696 100644
--- a/yazi-parser/src/mgr/sort.rs
+++ b/yazi-parser/src/mgr/sort.rs
@@ -1,5 +1,3 @@
-use std::str::FromStr;
-
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use yazi_fs::SortBy;
use yazi_shared::event::CmdCow;
@@ -18,11 +16,11 @@ impl TryFrom for SortOpt {
fn try_from(c: CmdCow) -> Result {
Ok(Self {
- by: c.first_str().map(SortBy::from_str).transpose()?,
- reverse: c.maybe_bool("reverse"),
- dir_first: c.maybe_bool("dir-first"),
- sensitive: c.maybe_bool("sensitive"),
- translit: c.maybe_bool("translit"),
+ by: c.first().ok().map(str::parse).transpose()?,
+ reverse: c.get("reverse").ok(),
+ dir_first: c.get("dir-first").ok(),
+ sensitive: c.get("sensitive").ok(),
+ translit: c.get("translit").ok(),
})
}
}
diff --git a/yazi-parser/src/mgr/spot.rs b/yazi-parser/src/mgr/spot.rs
index 6a825b50..02591edf 100644
--- a/yazi-parser/src/mgr/spot.rs
+++ b/yazi-parser/src/mgr/spot.rs
@@ -1,5 +1,5 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::event::{CmdCow, Data};
+use yazi_shared::event::CmdCow;
#[derive(Debug, Default)]
pub struct SpotOpt {
@@ -7,7 +7,7 @@ pub struct SpotOpt {
}
impl From for SpotOpt {
- fn from(c: CmdCow) -> Self { Self { skip: c.get("skip").and_then(Data::as_usize) } }
+ fn from(c: CmdCow) -> Self { Self { skip: c.get("skip").ok() } }
}
impl From for SpotOpt {
diff --git a/yazi-parser/src/mgr/tab_close.rs b/yazi-parser/src/mgr/tab_close.rs
index 9a482821..da54de94 100644
--- a/yazi-parser/src/mgr/tab_close.rs
+++ b/yazi-parser/src/mgr/tab_close.rs
@@ -1,5 +1,5 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::event::{CmdCow, Data};
+use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct TabCloseOpt {
@@ -7,7 +7,7 @@ pub struct TabCloseOpt {
}
impl From for TabCloseOpt {
- fn from(c: CmdCow) -> Self { Self { idx: c.first().and_then(Data::as_usize).unwrap_or(0) } }
+ fn from(c: CmdCow) -> Self { Self { idx: c.first().unwrap_or(0) } }
}
impl From for TabCloseOpt {
diff --git a/yazi-parser/src/mgr/tab_create.rs b/yazi-parser/src/mgr/tab_create.rs
index 10f7ce17..f1bc2b13 100644
--- a/yazi-parser/src/mgr/tab_create.rs
+++ b/yazi-parser/src/mgr/tab_create.rs
@@ -13,7 +13,7 @@ impl From for TabCreateOpt {
if c.bool("current") {
return Self { wd: None };
}
- let Some(mut wd) = c.take_first_url() else {
+ let Ok(mut wd) = c.take_first() else {
return Self { wd: Some(UrlCow::from(&BOOT.cwds[0])) };
};
if !c.bool("raw") {
diff --git a/yazi-parser/src/mgr/tab_switch.rs b/yazi-parser/src/mgr/tab_switch.rs
index 5d923e98..f6652330 100644
--- a/yazi-parser/src/mgr/tab_switch.rs
+++ b/yazi-parser/src/mgr/tab_switch.rs
@@ -1,5 +1,5 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::event::{CmdCow, Data};
+use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct TabSwitchOpt {
@@ -8,9 +8,7 @@ pub struct TabSwitchOpt {
}
impl From for TabSwitchOpt {
- fn from(c: CmdCow) -> Self {
- Self { step: c.first().and_then(Data::as_isize).unwrap_or(0), relative: c.bool("relative") }
- }
+ fn from(c: CmdCow) -> Self { Self { step: c.first().unwrap_or(0), relative: c.bool("relative") } }
}
impl FromLua for TabSwitchOpt {
diff --git a/yazi-parser/src/mgr/toggle.rs b/yazi-parser/src/mgr/toggle.rs
index f01a23e3..585c80c2 100644
--- a/yazi-parser/src/mgr/toggle.rs
+++ b/yazi-parser/src/mgr/toggle.rs
@@ -10,10 +10,10 @@ pub struct ToggleOpt {
impl From for ToggleOpt {
fn from(mut c: CmdCow) -> Self {
Self {
- url: c.take_first_url(),
- state: match c.str("state") {
- Some("on") => Some(true),
- Some("off") => Some(false),
+ url: c.take_first().ok(),
+ state: match c.get("state") {
+ Ok("on") => Some(true),
+ Ok("off") => Some(false),
_ => None,
},
}
diff --git a/yazi-parser/src/mgr/toggle_all.rs b/yazi-parser/src/mgr/toggle_all.rs
index 9cebbeae..6ccd8e10 100644
--- a/yazi-parser/src/mgr/toggle_all.rs
+++ b/yazi-parser/src/mgr/toggle_all.rs
@@ -9,19 +9,11 @@ pub struct ToggleAllOpt {
impl From for ToggleAllOpt {
fn from(mut c: CmdCow) -> Self {
- let mut urls = Vec::with_capacity(c.len());
- for i in 0..c.len() {
- match c.take_url(i) {
- Some(url) => urls.push(url),
- None => break,
- }
- }
-
Self {
- urls,
- state: match c.str("state") {
- Some("on") => Some(true),
- Some("off") => Some(false),
+ urls: c.take_seq(),
+ state: match c.get("state") {
+ Ok("on") => Some(true),
+ Ok("off") => Some(false),
_ => None,
},
}
diff --git a/yazi-parser/src/mgr/update_mimes.rs b/yazi-parser/src/mgr/update_mimes.rs
index fc159f84..7758e8e7 100644
--- a/yazi-parser/src/mgr/update_mimes.rs
+++ b/yazi-parser/src/mgr/update_mimes.rs
@@ -1,7 +1,7 @@
use anyhow::bail;
use hashbrown::HashMap;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::event::{CmdCow, Data, DataKey};
+use yazi_shared::{data::{Data, DataKey}, event::CmdCow};
#[derive(Debug)]
pub struct UpdateMimesOpt {
@@ -12,7 +12,7 @@ impl TryFrom for UpdateMimesOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result {
- let Some(updates) = c.try_take("updates").and_then(Data::into_dict) else {
+ let Ok(updates) = c.take("updates") else {
bail!("Invalid 'updates' argument in UpdateMimesOpt");
};
diff --git a/yazi-parser/src/mgr/update_paged.rs b/yazi-parser/src/mgr/update_paged.rs
index 85f793a5..e943e7e2 100644
--- a/yazi-parser/src/mgr/update_paged.rs
+++ b/yazi-parser/src/mgr/update_paged.rs
@@ -1,5 +1,5 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::{event::{CmdCow, Data}, url::UrlCow};
+use yazi_shared::{event::CmdCow, url::UrlCow};
#[derive(Debug, Default)]
pub struct UpdatePagedOpt {
@@ -8,9 +8,7 @@ pub struct UpdatePagedOpt {
}
impl From for UpdatePagedOpt {
- fn from(mut c: CmdCow) -> Self {
- Self { page: c.first().and_then(Data::as_usize), only_if: c.take_url("only-if") }
- }
+ fn from(mut c: CmdCow) -> Self { Self { page: c.first().ok(), only_if: c.take("only-if").ok() } }
}
impl From<()> for UpdatePagedOpt {
diff --git a/yazi-parser/src/notify/tick.rs b/yazi-parser/src/notify/tick.rs
index 771fd72a..ade39d53 100644
--- a/yazi-parser/src/notify/tick.rs
+++ b/yazi-parser/src/notify/tick.rs
@@ -2,7 +2,7 @@ use std::time::Duration;
use anyhow::bail;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
-use yazi_shared::event::{CmdCow, Data};
+use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct TickOpt {
@@ -13,7 +13,7 @@ impl TryFrom for TickOpt {
type Error = anyhow::Error;
fn try_from(c: CmdCow) -> Result {
- let Some(interval) = c.first().and_then(Data::as_f64) else {
+ let Ok(interval) = c.first() else {
bail!("Invalid 'interval' argument in TickOpt");
};
diff --git a/yazi-parser/src/spot/copy.rs b/yazi-parser/src/spot/copy.rs
index 2ebcfb87..f5799fef 100644
--- a/yazi-parser/src/spot/copy.rs
+++ b/yazi-parser/src/spot/copy.rs
@@ -7,7 +7,7 @@ pub struct CopyOpt {
}
impl From for CopyOpt {
- fn from(mut c: CmdCow) -> Self { Self { r#type: c.take_first_str().unwrap_or_default() } }
+ fn from(mut c: CmdCow) -> Self { Self { r#type: c.take_first().unwrap_or_default() } }
}
impl FromLua for CopyOpt {
diff --git a/yazi-parser/src/step.rs b/yazi-parser/src/step.rs
index ee522d48..a0c195fd 100644
--- a/yazi-parser/src/step.rs
+++ b/yazi-parser/src/step.rs
@@ -1,6 +1,6 @@
use std::{num::ParseIntError, str::FromStr};
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
#[derive(Clone, Copy, Debug)]
pub enum Step {
diff --git a/yazi-parser/src/tasks/process_exec.rs b/yazi-parser/src/tasks/process_exec.rs
index 45157f55..e6cf3325 100644
--- a/yazi-parser/src/tasks/process_exec.rs
+++ b/yazi-parser/src/tasks/process_exec.rs
@@ -1,4 +1,4 @@
-use std::{borrow::Cow, ffi::OsString};
+use std::{borrow::Cow, ffi::OsStr};
use anyhow::anyhow;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
@@ -11,7 +11,7 @@ use yazi_shared::{event::CmdCow, url::UrlBuf};
pub struct ProcessExecOpt {
pub cwd: UrlBuf,
pub opener: Cow<'static, OpenerRule>,
- pub args: Vec,
+ pub args: Vec>,
pub done: Option>,
}
diff --git a/yazi-parser/src/which/callback.rs b/yazi-parser/src/which/callback.rs
index b5818429..61f5a4e4 100644
--- a/yazi-parser/src/which/callback.rs
+++ b/yazi-parser/src/which/callback.rs
@@ -1,7 +1,7 @@
use anyhow::bail;
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use tokio::sync::mpsc;
-use yazi_shared::event::{CmdCow, Data};
+use yazi_shared::event::CmdCow;
#[derive(Debug)]
pub struct CallbackOpt {
@@ -17,7 +17,7 @@ impl TryFrom for CallbackOpt {
bail!("Invalid 'tx' argument in CallbackOpt");
};
- let Some(idx) = c.first().and_then(Data::as_usize) else {
+ let Ok(idx) = c.first() else {
bail!("Invalid 'idx' argument in CallbackOpt");
};
diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua
index 15fb615b..d8364d90 100644
--- a/yazi-plugin/preset/components/current.lua
+++ b/yazi-plugin/preset/components/current.lua
@@ -20,7 +20,7 @@ function Current:empty()
end
return {
- ui.Line(s):area(self._area):align(ui.Align.CENTER),
+ ui.Text(s):area(self._area):align(ui.Align.CENTER):wrap(ui.Wrap.YES),
}
end
diff --git a/yazi-plugin/preset/plugins/file.lua b/yazi-plugin/preset/plugins/file.lua
index 1399f474..f1663260 100644
--- a/yazi-plugin/preset/plugins/file.lua
+++ b/yazi-plugin/preset/plugins/file.lua
@@ -30,11 +30,11 @@ function M:spot(job)
end
function M:spot_base(job)
- local url, cha = job.file.url, job.file.cha
- local spotter = rt.plugin.spotter(url, job.mime)
- local previewer = rt.plugin.previewer(url, job.mime)
+ local cha = job.file.cha
+ local spotter = rt.plugin.spotter(job.file, job.mime)
+ local previewer = rt.plugin.previewer(job.file, job.mime)
local fetchers = rt.plugin.fetchers(job.file, job.mime)
- local preloaders = rt.plugin.preloaders(url, job.mime)
+ local preloaders = rt.plugin.preloaders(job.file, job.mime)
for i, v in ipairs(fetchers) do
fetchers[i] = v.cmd
diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua
index fb3d2dcd..91ecf720 100644
--- a/yazi-plugin/preset/plugins/folder.lua
+++ b/yazi-plugin/preset/plugins/folder.lua
@@ -18,7 +18,7 @@ function M:peek(job)
if #folder.files == 0 then
local done, err = folder.stage()
local s = not done and "Loading..." or not err and "No items" or string.format("Error: %s", err)
- return ya.preview_widget(job, ui.Line(s):area(job.area):align(ui.Align.CENTER))
+ return ya.preview_widget(job, ui.Text(s):area(job.area):align(ui.Align.CENTER):wrap(ui.Wrap.YES))
end
local items = {}
diff --git a/yazi-plugin/preset/plugins/mime-dir.lua b/yazi-plugin/preset/plugins/mime-dir.lua
new file mode 100644
index 00000000..7ee392cf
--- /dev/null
+++ b/yazi-plugin/preset/plugins/mime-dir.lua
@@ -0,0 +1,11 @@
+local function fetch(_, job)
+ local updates = {}
+ for _, file in ipairs(job.files) do
+ updates[file.url] = "folder/local"
+ end
+
+ ya.emit("update_mimes", { updates = updates })
+ return true
+end
+
+return { fetch = fetch }
diff --git a/yazi-plugin/preset/plugins/mime-file.lua b/yazi-plugin/preset/plugins/mime-file.lua
new file mode 100644
index 00000000..4c7f45ef
--- /dev/null
+++ b/yazi-plugin/preset/plugins/mime-file.lua
@@ -0,0 +1,83 @@
+-- stylua: ignore
+local TYPE_PATS = { "text", "image", "video", "application", "audio", "font", "inode", "message", "model", "vector", "biosig", "chemical", "rinex", "x%-epoc" }
+
+local M = {}
+
+local function match_mimetype(line)
+ for _, pat in ipairs(TYPE_PATS) do
+ local typ, sub = line:match(string.format("(%s/)([+-.a-zA-Z0-9]+)%%s+$", pat))
+ if not sub then
+ elseif line:find(typ .. sub, 1, true) == 1 then
+ return typ:gsub("^x%-", "", 1) .. sub:gsub("^x%-", "", 1):gsub("^vnd%.", "", 1)
+ else
+ return nil, true
+ end
+ end
+end
+
+local function miss_cache(cache, line)
+ if line:match("^cannot open `.+' %(No such file or directory%)%s+$") then
+ return true
+ else
+ local _, err = fs.cha(Url(cache))
+ return err and err.code == 2
+ end
+end
+
+function M:fetch(job)
+ local paths, origins = {}, {}
+ for i, file in ipairs(job.files) do
+ if file.cache then
+ paths[i], origins[i] = tostring(file.cache), tostring(file.url)
+ else
+ paths[i] = tostring(file.url)
+ end
+ end
+
+ local cmd = os.getenv("YAZI_FILE_ONE") or "file"
+ local child, err = Command(cmd):arg({ "-bL", "--mime-type", "--" }):arg(paths):stdout(Command.PIPED):spawn()
+ if not child then
+ return true, Err("Failed to start `%s`, error: %s", cmd, err)
+ end
+
+ local updates, last = {}, ya.time()
+ local flush = function(force)
+ if not force and ya.time() - last < 0.3 then
+ return
+ end
+ if next(updates) then
+ ya.emit("update_mimes", { updates = updates })
+ updates, last = {}, ya.time()
+ end
+ end
+
+ local i, state, match, ignore = 1, {}, nil, nil
+ repeat
+ local line, event = child:read_line_with { timeout = 300 }
+ if event == 3 then
+ flush(true)
+ goto continue
+ elseif event ~= 0 then
+ break
+ end
+
+ match, ignore = match_mimetype(line)
+ if match then
+ updates[origins[i] or paths[i]], state[i], i = match, true, i + 1
+ flush(false)
+ elseif ignore then
+ goto continue
+ elseif origins[i] and miss_cache(paths[i], line) then
+ updates[origins[i]], state[i], i = "vfs/todo", true, i + 1
+ flush(false)
+ else
+ state[i], i = false, i + 1
+ end
+ ::continue::
+ until i > #paths
+
+ flush(true)
+ return state
+end
+
+return M
diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua
index 4c7f45ef..e9ac3634 100644
--- a/yazi-plugin/preset/plugins/mime.lua
+++ b/yazi-plugin/preset/plugins/mime.lua
@@ -1,83 +1,12 @@
--- stylua: ignore
-local TYPE_PATS = { "text", "image", "video", "application", "audio", "font", "inode", "message", "model", "vector", "biosig", "chemical", "rinex", "x%-epoc" }
+local function fetch(_, job)
+ ya.notify {
+ title = "Deprecated plugin",
+ content = "The `mime` fetcher is deprecated, use `mime.file` instead in your `yazi.toml`\n\nSee https://github.com/sxyazi/yazi/pull/3222 for more details.",
+ timeout = 15,
+ level = "warn",
+ }
-local M = {}
-
-local function match_mimetype(line)
- for _, pat in ipairs(TYPE_PATS) do
- local typ, sub = line:match(string.format("(%s/)([+-.a-zA-Z0-9]+)%%s+$", pat))
- if not sub then
- elseif line:find(typ .. sub, 1, true) == 1 then
- return typ:gsub("^x%-", "", 1) .. sub:gsub("^x%-", "", 1):gsub("^vnd%.", "", 1)
- else
- return nil, true
- end
- end
+ return require("mime.file"):fetch(job)
end
-local function miss_cache(cache, line)
- if line:match("^cannot open `.+' %(No such file or directory%)%s+$") then
- return true
- else
- local _, err = fs.cha(Url(cache))
- return err and err.code == 2
- end
-end
-
-function M:fetch(job)
- local paths, origins = {}, {}
- for i, file in ipairs(job.files) do
- if file.cache then
- paths[i], origins[i] = tostring(file.cache), tostring(file.url)
- else
- paths[i] = tostring(file.url)
- end
- end
-
- local cmd = os.getenv("YAZI_FILE_ONE") or "file"
- local child, err = Command(cmd):arg({ "-bL", "--mime-type", "--" }):arg(paths):stdout(Command.PIPED):spawn()
- if not child then
- return true, Err("Failed to start `%s`, error: %s", cmd, err)
- end
-
- local updates, last = {}, ya.time()
- local flush = function(force)
- if not force and ya.time() - last < 0.3 then
- return
- end
- if next(updates) then
- ya.emit("update_mimes", { updates = updates })
- updates, last = {}, ya.time()
- end
- end
-
- local i, state, match, ignore = 1, {}, nil, nil
- repeat
- local line, event = child:read_line_with { timeout = 300 }
- if event == 3 then
- flush(true)
- goto continue
- elseif event ~= 0 then
- break
- end
-
- match, ignore = match_mimetype(line)
- if match then
- updates[origins[i] or paths[i]], state[i], i = match, true, i + 1
- flush(false)
- elseif ignore then
- goto continue
- elseif origins[i] and miss_cache(paths[i], line) then
- updates[origins[i]], state[i], i = "vfs/todo", true, i + 1
- flush(false)
- else
- state[i], i = false, i + 1
- end
- ::continue::
- until i > #paths
-
- flush(true)
- return state
-end
-
-return M
+return { fetch = fetch }
diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-plugin/src/loader/loader.rs
index 9d5946e2..8aa1d6ed 100644
--- a/yazi-plugin/src/loader/loader.rs
+++ b/yazi-plugin/src/loader/loader.rs
@@ -38,7 +38,9 @@ impl Default for Loader {
("image".to_owned(), preset!("plugins/image").into()),
("json".to_owned(), preset!("plugins/json").into()),
("magick".to_owned(), preset!("plugins/magick").into()),
- ("mime".to_owned(), preset!("plugins/mime").into()),
+ ("mime".to_owned(), preset!("plugins/mime").into()), // TODO: remove this
+ ("mime.dir".to_owned(), preset!("plugins/mime-dir").into()),
+ ("mime.file".to_owned(), preset!("plugins/mime-file").into()),
("noop".to_owned(), preset!("plugins/noop").into()),
("pdf".to_owned(), preset!("plugins/pdf").into()),
("session".to_owned(), preset!("plugins/session").into()),
diff --git a/yazi-plugin/src/runtime/plugin.rs b/yazi-plugin/src/runtime/plugin.rs
index 09203648..3978e6cc 100644
--- a/yazi-plugin/src/runtime/plugin.rs
+++ b/yazi-plugin/src/runtime/plugin.rs
@@ -1,5 +1,5 @@
use mlua::{Function, IntoLua, Lua, UserData, Value};
-use yazi_binding::{Composer, ComposerGet, ComposerSet, FileRef, UrlRef, cached_field};
+use yazi_binding::{Composer, ComposerGet, ComposerSet, FileRef, cached_field};
use yazi_config::YAZI;
pub(super) fn plugin() -> Composer {
@@ -21,25 +21,25 @@ pub(super) fn plugin() -> Composer {
fn fetchers(lua: &Lua) -> mlua::Result {
lua.create_function(|lua, (file, mime): (FileRef, mlua::String)| {
- lua.create_sequence_from(YAZI.plugin.fetchers(&file.url, &mime.to_str()?).map(Fetcher::new))
+ lua.create_sequence_from(YAZI.plugin.fetchers(&file, &mime.to_str()?).map(Fetcher::new))
})
}
fn spotter(lua: &Lua) -> mlua::Result {
- lua.create_function(|_, (url, mime): (UrlRef, mlua::String)| {
- Ok(YAZI.plugin.spotter(&url, &mime.to_str()?).map(Spotter::new))
+ lua.create_function(|_, (file, mime): (FileRef, mlua::String)| {
+ Ok(YAZI.plugin.spotter(&file, &mime.to_str()?).map(Spotter::new))
})
}
fn preloaders(lua: &Lua) -> mlua::Result {
- lua.create_function(|lua, (url, mime): (UrlRef, mlua::String)| {
- lua.create_sequence_from(YAZI.plugin.preloaders(&url, &mime.to_str()?).map(Preloader::new))
+ lua.create_function(|lua, (file, mime): (FileRef, mlua::String)| {
+ lua.create_sequence_from(YAZI.plugin.preloaders(&file, &mime.to_str()?).map(Preloader::new))
})
}
fn previewer(lua: &Lua) -> mlua::Result {
- lua.create_function(|_, (url, mime): (UrlRef, mlua::String)| {
- Ok(YAZI.plugin.previewer(&url, &mime.to_str()?).map(Previewer::new))
+ lua.create_function(|_, (file, mime): (FileRef, mlua::String)| {
+ Ok(YAZI.plugin.previewer(&file, &mime.to_str()?).map(Previewer::new))
})
}
diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs
index 37382129..ec689459 100644
--- a/yazi-plugin/src/utils/sync.rs
+++ b/yazi-plugin/src/utils/sync.rs
@@ -6,7 +6,7 @@ use yazi_binding::{runtime, runtime_mut};
use yazi_dds::Sendable;
use yazi_parser::app::{PluginCallback, PluginOpt};
use yazi_proxy::AppProxy;
-use yazi_shared::event::Data;
+use yazi_shared::data::Data;
use super::Utils;
use crate::{bindings::{MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx}, loader::LOADER};
diff --git a/yazi-proxy/src/tasks.rs b/yazi-proxy/src/tasks.rs
index a74e5af8..c05411e9 100644
--- a/yazi-proxy/src/tasks.rs
+++ b/yazi-proxy/src/tasks.rs
@@ -1,19 +1,23 @@
-use std::{borrow::Cow, ffi::OsString};
+use std::{borrow::Cow, ffi::OsStr};
use tokio::sync::oneshot;
use yazi_config::opener::OpenerRule;
use yazi_macro::{emit, relay};
use yazi_parser::{mgr::OpenWithOpt, tasks::ProcessExecOpt};
-use yazi_shared::url::UrlBuf;
+use yazi_shared::url::{UrlBuf, UrlCow};
pub struct TasksProxy;
impl TasksProxy {
- pub fn open_with(opener: Cow<'static, OpenerRule>, cwd: UrlBuf, targets: Vec) {
+ pub fn open_with(opener: Cow<'static, OpenerRule>, cwd: UrlBuf, targets: Vec>) {
emit!(Call(relay!(tasks:open_with).with_any("option", OpenWithOpt { opener, cwd, targets })));
}
- pub async fn process_exec(opener: Cow<'static, OpenerRule>, cwd: UrlBuf, args: Vec) {
+ pub async fn process_exec(
+ opener: Cow<'static, OpenerRule>,
+ cwd: UrlBuf,
+ args: Vec>,
+ ) {
let (tx, rx) = oneshot::channel();
emit!(Call(relay!(tasks:process_exec).with_any("option", ProcessExecOpt {
cwd,
diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs
index 733ad83d..a7722175 100644
--- a/yazi-scheduler/src/file/file.rs
+++ b/yazi-scheduler/src/file/file.rs
@@ -5,6 +5,7 @@ use tokio::{io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
use tracing::warn;
use yazi_config::YAZI;
use yazi_fs::{cha::Cha, ok_or_not_found, path::{path_relative_to, skip_url}, provider::{DirReader, FileHolder}};
+use yazi_macro::ok_or_not_found;
use yazi_shared::url::{Url, UrlBuf, UrlCow};
use yazi_vfs::{VfsCha, copy_with_progress, maybe_exists, provider::{self, DirEntry}};
@@ -94,7 +95,7 @@ impl File {
}
pub(crate) async fn paste_do(&self, mut task: FileInPaste) -> Result<(), FileOutPasteDo> {
- ok_or_not_found(provider::remove_file(&task.to).await)?;
+ ok_or_not_found!(provider::remove_file(&task.to).await);
let mut it = copy_with_progress(&task.from, &task.to, task.cha.unwrap());
while let Some(res) = it.recv().await {
@@ -132,13 +133,11 @@ impl File {
pub(crate) async fn link_do(&self, task: FileInLink) -> Result<(), FileOutLink> {
let src: Cow<_> = if task.resolve {
- match provider::read_link(&task.from).await {
- Ok(p) => p.into(),
- Err(e) if e.kind() == NotFound => {
- return Ok(self.ops.out(task.id, FileOutLink::Succ));
- }
- Err(e) => Err(e)?,
- }
+ ok_or_not_found!(
+ provider::read_link(&task.from).await,
+ return Ok(self.ops.out(task.id, FileOutLink::Succ))
+ )
+ .into()
} else if task.from.scheme.covariant(&task.to.scheme) {
task.from.loc.as_path().into()
} else {
@@ -151,7 +150,7 @@ impl File {
src
};
- ok_or_not_found(provider::remove_file(&task.to).await)?;
+ ok_or_not_found!(provider::remove_file(&task.to).await);
provider::symlink(&src, &task.to, async || {
Ok(match task.cha {
Some(cha) => cha.is_dir(),
@@ -231,8 +230,8 @@ impl File {
UrlCow::from(&task.from)
};
- ok_or_not_found(provider::remove_file(&task.to).await)?;
- ok_or_not_found(provider::hard_link(&src, &task.to).await)?;
+ ok_or_not_found!(provider::remove_file(&task.to).await);
+ ok_or_not_found!(provider::hard_link(&src, &task.to).await);
Ok(self.ops.out(task.id, FileOutHardlinkDo::Succ))
}
diff --git a/yazi-scheduler/src/process/in.rs b/yazi-scheduler/src/process/in.rs
index 20667213..7f6d25b9 100644
--- a/yazi-scheduler/src/process/in.rs
+++ b/yazi-scheduler/src/process/in.rs
@@ -1,4 +1,4 @@
-use std::ffi::OsString;
+use std::{borrow::Cow, ffi::{OsStr, OsString}};
use tokio::sync::mpsc;
use yazi_shared::{Id, url::UrlBuf};
@@ -11,7 +11,7 @@ pub(crate) struct ProcessInBlock {
pub(crate) id: Id,
pub(crate) cwd: UrlBuf,
pub(crate) cmd: OsString,
- pub(crate) args: Vec,
+ pub(crate) args: Vec>,
}
impl From for ShellOpt {
@@ -26,7 +26,7 @@ pub(crate) struct ProcessInOrphan {
pub(crate) id: Id,
pub(crate) cwd: UrlBuf,
pub(crate) cmd: OsString,
- pub(crate) args: Vec,
+ pub(crate) args: Vec>,
}
impl From for ShellOpt {
@@ -41,7 +41,7 @@ pub(crate) struct ProcessInBg {
pub(crate) id: Id,
pub(crate) cwd: UrlBuf,
pub(crate) cmd: OsString,
- pub(crate) args: Vec,
+ pub(crate) args: Vec>,
pub(crate) cancel: mpsc::Receiver<()>,
}
diff --git a/yazi-scheduler/src/process/shell.rs b/yazi-scheduler/src/process/shell.rs
index fd33f8c6..81f2b731 100644
--- a/yazi-scheduler/src/process/shell.rs
+++ b/yazi-scheduler/src/process/shell.rs
@@ -1,4 +1,4 @@
-use std::{borrow::Cow, ffi::OsString, process::Stdio};
+use std::{borrow::Cow, ffi::{OsStr, OsString}, process::Stdio};
use anyhow::{Result, bail};
use tokio::process::{Child, Command};
@@ -8,7 +8,7 @@ use yazi_shared::url::UrlBuf;
pub(crate) struct ShellOpt {
pub(crate) cwd: UrlBuf,
pub(crate) cmd: OsString,
- pub(crate) args: Vec,
+ pub(crate) args: Vec>,
pub(crate) piped: bool,
pub(crate) orphan: bool,
}
@@ -75,7 +75,7 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result {
#[cfg(windows)]
mod parser {
- use std::{ffi::{OsStr, OsString}, iter::Peekable, os::windows::ffi::{EncodeWide, OsStrExt, OsStringExt}};
+ use std::{borrow::Cow, ffi::{OsStr, OsString}, iter::Peekable, os::windows::ffi::{EncodeWide, OsStrExt, OsStringExt}};
macro_rules! w {
($c:literal) => {
@@ -83,7 +83,7 @@ mod parser {
};
}
- pub(super) fn parse(cmd: &OsStr, args: &[OsString]) -> OsString {
+ pub(super) fn parse(cmd: &OsStr, args: &[Cow]) -> OsString {
if cmd.len() < 2 {
return cmd.to_owned();
}
@@ -120,7 +120,7 @@ mod parser {
fn visit_percent(
mut it: Peekable,
buf: &mut Vec,
- args: &[OsString],
+ args: &[Cow],
quote: bool,
) -> usize {
let Some(c) = it.next().and_then(|c| char::from_u32(c as _)) else {
@@ -186,7 +186,7 @@ mod parser {
fn parse(cmd: &str, args: &[&str]) -> String {
let cmd = OsString::from(cmd);
- let args: Vec<_> = args.iter().map(|&s| OsString::from(s)).collect();
+ let args: Vec<_> = args.iter().map(|&s| OsString::from(s).into()).collect();
super::parse(&cmd, &args).to_str().unwrap().to_owned()
}
diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs
index 73862bfa..35b272dc 100644
--- a/yazi-scheduler/src/scheduler.rs
+++ b/yazi-scheduler/src/scheduler.rs
@@ -6,6 +6,7 @@ use parking_lot::Mutex;
use tokio::{select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle};
use yazi_config::{YAZI, plugin::{Fetcher, Preloader}};
use yazi_dds::Pump;
+use yazi_fs::FsUrl;
use yazi_parser::{app::PluginOpt, tasks::ProcessExecOpt};
use yazi_proxy::TasksProxy;
use yazi_shared::{Id, Throttle, url::UrlBuf};
@@ -209,6 +210,26 @@ impl Scheduler {
self.send_micro(id, LOW, async move { file.trash(FileInTrash { id, target }) })
}
+ pub fn file_download(&self, from: UrlBuf, done: Option>) {
+ let mut ongoing = self.ongoing.lock();
+ let id = self.ongoing.lock().add::(format!("Download {}", from.display()));
+
+ if let Some(tx) = done {
+ ongoing.hooks.add_sync(id, move |canceled| _ = tx.send(canceled));
+ }
+
+ let Some(to) = from.cache().map(UrlBuf::from) else {
+ return self
+ .ops
+ .out(id, FileOutPaste::Deform("Unable to download non-remote file".to_owned()));
+ };
+
+ let file = self.file.clone();
+ self.send_micro(id, LOW, async move {
+ file.paste(FileInPaste { id, from, to, cha: None, cut: false, follow: false, retry: 0 }).await
+ });
+ }
+
pub fn plugin_micro(&self, opt: PluginOpt) {
let id = self.ongoing.lock().add::(format!("Run micro plugin `{}`", opt.id));
@@ -245,6 +266,22 @@ impl Scheduler {
});
}
+ pub async fn fetch_mimetype(&self, targets: Vec) -> bool {
+ let mut wg = vec![];
+ for (fetcher, targets) in YAZI.plugin.mime_fetchers(targets) {
+ let (tx, rx) = oneshot::channel();
+ self.fetch_paged(fetcher, targets, Some(tx));
+ wg.push(rx);
+ }
+
+ for rx in wg {
+ if rx.await != Ok(true) {
+ return false;
+ }
+ }
+ true
+ }
+
pub fn preload_paged(&self, preloader: &'static Preloader, target: &yazi_fs::File) {
let id =
self.ongoing.lock().add::(format!("Run preloader `{}`", preloader.run.name));
diff --git a/yazi-shared/src/chars.rs b/yazi-shared/src/chars.rs
index 319ae580..f6e7a943 100644
--- a/yazi-shared/src/chars.rs
+++ b/yazi-shared/src/chars.rs
@@ -1,8 +1,6 @@
use core::str;
use std::{borrow::Cow, ffi::OsStr};
-pub const MIME_DIR: &str = "inode/directory";
-
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum CharKind {
Space,
diff --git a/yazi-shared/src/data/data.rs b/yazi-shared/src/data/data.rs
new file mode 100644
index 00000000..fc8c28ce
--- /dev/null
+++ b/yazi-shared/src/data/data.rs
@@ -0,0 +1,246 @@
+use std::{any::Any, borrow::Cow};
+
+use anyhow::{Result, bail};
+use hashbrown::HashMap;
+use serde::{Deserialize, Serialize};
+
+use crate::{Id, SStr, data::DataKey, url::{UrlBuf, UrlCow, UrnBuf}};
+
+// --- Data
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum Data {
+ Nil,
+ Boolean(bool),
+ Integer(i64),
+ Number(f64),
+ String(SStr),
+ List(Vec),
+ Dict(HashMap),
+ Id(Id),
+ #[serde(skip_deserializing)]
+ Url(UrlBuf),
+ #[serde(skip_deserializing)]
+ Urn(UrnBuf),
+ #[serde(skip)]
+ Bytes(Vec),
+ #[serde(skip)]
+ Any(Box),
+}
+
+impl Data {
+ pub fn as_str(&self) -> Option<&str> {
+ match self {
+ Self::String(s) => Some(s),
+ _ => None,
+ }
+ }
+
+ pub fn into_string(self) -> Option {
+ match self {
+ Self::String(s) => Some(s),
+ _ => None,
+ }
+ }
+
+ pub fn into_any(self) -> Option {
+ match self {
+ Self::Any(b) => b.downcast::().ok().map(|b| *b),
+ _ => None,
+ }
+ }
+
+ // FIXME: find a better name
+ pub fn into_any2(self) -> Result {
+ if let Self::Any(b) = self
+ && let Ok(t) = b.downcast::()
+ {
+ Ok(*t)
+ } else {
+ bail!("Failed to downcast Data into {}", std::any::type_name::())
+ }
+ }
+}
+
+impl From<()> for Data {
+ fn from(_: ()) -> Self { Self::Nil }
+}
+
+impl From for Data {
+ fn from(value: bool) -> Self { Self::Boolean(value) }
+}
+
+impl From for Data {
+ fn from(value: i32) -> Self { Self::Integer(value as i64) }
+}
+
+impl From for Data {
+ fn from(value: i64) -> Self { Self::Integer(value) }
+}
+
+impl From for Data {
+ fn from(value: f64) -> Self { Self::Number(value) }
+}
+
+impl From for Data {
+ fn from(value: usize) -> Self { Self::Id(value.into()) }
+}
+
+impl From for Data {
+ fn from(value: String) -> Self { Self::String(Cow::Owned(value)) }
+}
+
+impl From for Data {
+ fn from(value: SStr) -> Self { Self::String(value) }
+}
+
+impl From for Data {
+ fn from(value: Id) -> Self { Self::Id(value) }
+}
+
+impl From<&UrlBuf> for Data {
+ fn from(value: &UrlBuf) -> Self { Self::Url(value.clone()) }
+}
+
+impl From<&str> for Data {
+ fn from(value: &str) -> Self { Self::String(Cow::Owned(value.to_owned())) }
+}
+
+impl TryFrom<&Data> for bool {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &Data) -> Result {
+ match value {
+ Data::Boolean(b) => Ok(*b),
+ Data::String(s) if s == "no" => Ok(false),
+ Data::String(s) if s == "yes" => Ok(true),
+ _ => bail!("not a boolean"),
+ }
+ }
+}
+
+impl<'a> TryFrom<&'a Data> for &'a str {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &'a Data) -> Result {
+ match value {
+ Data::String(s) => Ok(s),
+ _ => bail!("not a string"),
+ }
+ }
+}
+
+impl TryFrom for SStr {
+ type Error = anyhow::Error;
+
+ fn try_from(value: Data) -> Result {
+ match value {
+ Data::String(s) => Ok(s),
+ _ => bail!("not a string"),
+ }
+ }
+}
+
+impl<'a> TryFrom<&'a Data> for Cow<'a, str> {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &'a Data) -> Result {
+ match value {
+ Data::String(s) => Ok(s.as_ref().into()),
+ _ => bail!("not a string"),
+ }
+ }
+}
+
+impl TryFrom for HashMap {
+ type Error = anyhow::Error;
+
+ fn try_from(value: Data) -> Result {
+ match value {
+ Data::Dict(d) => Ok(d),
+ _ => bail!("not a dict"),
+ }
+ }
+}
+
+impl TryFrom<&Data> for HashMap {
+ type Error = anyhow::Error;
+
+ fn try_from(_: &Data) -> Result {
+ bail!("cannot take ownership of dict from &Data");
+ }
+}
+
+impl TryFrom for UrlCow<'static> {
+ type Error = anyhow::Error;
+
+ fn try_from(value: Data) -> Result {
+ match value {
+ Data::String(s) => s.try_into(),
+ Data::Url(u) => Ok(u.into()),
+ Data::Bytes(b) => b.try_into(),
+ _ => bail!("not a URL"),
+ }
+ }
+}
+
+impl<'a> TryFrom<&'a Data> for UrlCow<'a> {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &'a Data) -> Result {
+ match value {
+ Data::String(s) => s.as_ref().try_into(),
+ Data::Url(u) => Ok(u.into()),
+ Data::Bytes(b) => b.as_slice().try_into(),
+ _ => bail!("not a URL"),
+ }
+ }
+}
+
+impl PartialEq for Data {
+ fn eq(&self, other: &bool) -> bool { self.try_into().is_ok_and(|b| *other == b) }
+}
+
+// --- Macros
+macro_rules! impl_into_integer {
+ ($t:ty, $name:ident) => {
+ impl TryFrom<&Data> for $t {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &Data) -> Result {
+ Ok(match value {
+ Data::Integer(i) => <$t>::try_from(*i)?,
+ Data::String(s) => s.parse()?,
+ Data::Id(i) => <$t>::try_from(i.get())?,
+ _ => bail!("not an integer"),
+ })
+ }
+ }
+ };
+}
+
+macro_rules! impl_into_number {
+ ($t:ty, $name:ident) => {
+ impl TryFrom<&Data> for $t {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &Data) -> Result {
+ Ok(match value {
+ Data::Integer(i) if *i == (*i as $t as _) => *i as $t,
+ Data::Number(n) => <$t>::try_from(*n)?,
+ Data::String(s) => s.parse()?,
+ Data::Id(i) if i.0 == (i.0 as $t as _) => i.0 as $t,
+ _ => bail!("not a number"),
+ })
+ }
+ }
+ };
+}
+
+impl_into_integer!(usize, as_usize);
+impl_into_integer!(isize, as_isize);
+impl_into_integer!(i16, as_i16);
+impl_into_integer!(i32, as_i32);
+impl_into_integer!(crate::Id, as_id);
+
+impl_into_number!(f64, as_f64);
diff --git a/yazi-shared/src/data/key.rs b/yazi-shared/src/data/key.rs
new file mode 100644
index 00000000..7faa3724
--- /dev/null
+++ b/yazi-shared/src/data/key.rs
@@ -0,0 +1,82 @@
+use std::borrow::Cow;
+
+use ordered_float::OrderedFloat;
+use serde::{Deserialize, Serialize, de};
+
+use crate::{Id, SStr, url::{UrlBuf, UrlCow, UrnBuf}};
+
+#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+#[serde(untagged)]
+pub enum DataKey {
+ Nil,
+ Boolean(bool),
+ #[serde(deserialize_with = "Self::deserialize_integer")]
+ Integer(i64),
+ Number(OrderedFloat),
+ String(SStr),
+ Id(Id),
+ #[serde(skip_deserializing)]
+ Url(UrlBuf),
+ #[serde(skip_deserializing)]
+ Urn(UrnBuf),
+ #[serde(skip)]
+ Bytes(Vec),
+}
+
+impl DataKey {
+ pub fn is_integer(&self) -> bool { matches!(self, Self::Integer(_)) }
+
+ pub fn as_str(&self) -> Option<&str> {
+ match self {
+ Self::String(s) => Some(s),
+ _ => None,
+ }
+ }
+
+ pub fn into_url(self) -> Option> {
+ match self {
+ Self::String(s) => s.try_into().ok(),
+ Self::Url(u) => Some(u.into()),
+ Self::Bytes(b) => b.try_into().ok(),
+ _ => None,
+ }
+ }
+
+ fn deserialize_integer<'de, D>(deserializer: D) -> Result
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl de::Visitor<'_> for Visitor {
+ type Value = i64;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ formatter.write_str("an integer or a string of an integer")
+ }
+
+ fn visit_i64(self, value: i64) -> Result { Ok(value) }
+
+ fn visit_str(self, value: &str) -> Result
+ where
+ E: de::Error,
+ {
+ value.parse().map_err(de::Error::custom)
+ }
+ }
+
+ deserializer.deserialize_any(Visitor)
+ }
+}
+
+impl From for DataKey {
+ fn from(value: usize) -> Self { Self::Integer(value as i64) }
+}
+
+impl From<&'static str> for DataKey {
+ fn from(value: &'static str) -> Self { Self::String(Cow::Borrowed(value)) }
+}
+
+impl From for DataKey {
+ fn from(value: String) -> Self { Self::String(Cow::Owned(value)) }
+}
diff --git a/yazi-shared/src/data/mod.rs b/yazi-shared/src/data/mod.rs
new file mode 100644
index 00000000..ea06534c
--- /dev/null
+++ b/yazi-shared/src/data/mod.rs
@@ -0,0 +1 @@
+yazi_macro::mod_flat!(data key);
diff --git a/yazi-shared/src/event/cmd.rs b/yazi-shared/src/event/cmd.rs
index cfabe7c4..2a35eb3c 100644
--- a/yazi-shared/src/event/cmd.rs
+++ b/yazi-shared/src/event/cmd.rs
@@ -4,8 +4,7 @@ use anyhow::{Result, anyhow, bail};
use hashbrown::HashMap;
use serde::{Deserialize, de};
-use super::{Data, DataKey};
-use crate::{Id, Layer, SStr, Source, url::UrlCow};
+use crate::{Layer, SStr, Source, data::{Data, DataKey}};
#[derive(Debug, Default)]
pub struct Cmd {
@@ -54,23 +53,18 @@ impl Cmd {
cmd
}
- #[inline]
fn null() -> Self { Self { name: Cow::Borrowed("null"), ..Default::default() } }
- #[inline]
pub fn len(&self) -> usize { self.args.len() }
- #[inline]
pub fn is_empty(&self) -> bool { self.args.is_empty() }
// --- With
- #[inline]
pub fn with(mut self, name: impl Into, value: impl Into) -> Self {
self.args.insert(name.into(), value.into());
self
}
- #[inline]
pub fn with_opt(mut self, name: impl Into, value: Option>) -> Self {
if let Some(v) = value {
self.args.insert(name.into(), v.into());
@@ -78,71 +72,103 @@ impl Cmd {
self
}
- #[inline]
pub fn with_any(mut self, name: impl Into, data: impl Any + Send + Sync) -> Self {
self.args.insert(name.into(), Data::Any(Box::new(data)));
self
}
// --- Get
- #[inline]
- pub fn get(&self, name: impl Into) -> Option<&Data> { self.args.get(&name.into()) }
+ pub fn get<'a, T>(&'a self, name: impl Into) -> Result
+ where
+ T: TryFrom<&'a Data>,
+ T::Error: Into,
+ {
+ let name = name.into();
+ match self.args.get(&name) {
+ Some(data) => data.try_into().map_err(Into::into),
+ None => bail!("argument not found: {:?}", name),
+ }
+ }
- #[inline]
- pub fn str(&self, name: impl Into) -> Option<&str> { self.get(name)?.as_str() }
+ pub fn str(&self, name: impl Into) -> &str { self.get(name).unwrap_or_default() }
- #[inline]
- pub fn bool(&self, name: impl Into) -> bool { self.maybe_bool(name).unwrap_or(false) }
+ pub fn bool(&self, name: impl Into) -> bool { self.get(name).unwrap_or(false) }
- #[inline]
- pub fn maybe_bool(&self, name: impl Into) -> Option { self.get(name)?.as_bool() }
+ pub fn first<'a, T>(&'a self) -> Result
+ where
+ T: TryFrom<&'a Data>,
+ T::Error: Into,
+ {
+ self.get(0).map_err(Into::into)
+ }
- #[inline]
- pub fn id(&self, name: impl Into) -> Option { self.get(name)?.as_id() }
+ pub fn second<'a, T>(&'a self) -> Result
+ where
+ T: TryFrom<&'a Data>,
+ T::Error: Into,
+ {
+ self.get(1).map_err(Into::into)
+ }
- #[inline]
- pub fn url(&self, name: impl Into) -> Option> { self.get(name)?.to_url() }
-
- #[inline]
- pub fn first(&self) -> Option<&Data> { self.get(0) }
-
- #[inline]
- pub fn first_str(&self) -> Option<&str> { self.str(0) }
-
- #[inline]
- pub fn second(&self) -> Option<&Data> { self.get(1) }
-
- #[inline]
- pub fn second_str(&self) -> Option<&str> { self.str(1) }
+ pub fn seq<'a, T>(&'a self) -> Vec
+ where
+ T: TryFrom<&'a Data>,
+ {
+ let mut seq = Vec::with_capacity(self.len());
+ for i in 0..self.len() {
+ if let Ok(data) = self.get::<&Data>(i)
+ && let Ok(v) = data.try_into()
+ {
+ seq.push(v);
+ } else {
+ break;
+ }
+ }
+ seq
+ }
// --- Take
- #[inline]
- pub fn take(&mut self, name: impl Into) -> Option {
- self.args.remove(&name.into())
+ pub fn take<'a, T>(&mut self, name: impl Into) -> Result
+ where
+ T: TryFrom,
+ T::Error: Into,
+ {
+ let name = name.into();
+ match self.args.remove(&name) {
+ Some(data) => data.try_into().map_err(Into::into),
+ None => bail!("argument not found: {:?}", name),
+ }
}
- #[inline]
- pub fn take_str(&mut self, name: impl Into) -> Option {
- if let Some(Data::String(s)) = self.take(name) { Some(s) } else { None }
+ pub fn take_first<'a, T>(&mut self) -> Result
+ where
+ T: TryFrom,
+ T::Error: Into,
+ {
+ self.take(0)
}
- #[inline]
- pub fn take_first(&mut self) -> Option { self.take(0) }
-
- #[inline]
- pub fn take_first_str(&mut self) -> Option {
- if let Some(Data::String(s)) = self.take_first() { Some(s) } else { None }
+ pub fn take_seq(&mut self) -> Vec
+ where
+ T: TryFrom,
+ {
+ let mut seq = Vec::with_capacity(self.len());
+ for i in 0..self.len() {
+ if let Ok(data) = self.take::(i)
+ && let Ok(v) = data.try_into()
+ {
+ seq.push(v);
+ } else {
+ break;
+ }
+ }
+ seq
}
- #[inline]
- pub fn take_first_url(&mut self) -> Option> { self.take_first()?.into_url() }
-
- #[inline]
pub fn take_any(&mut self, name: impl Into) -> Option {
self.args.remove(&name.into())?.into_any()
}
- #[inline]
pub fn take_any2(&mut self, name: impl Into) -> Option> {
self.args.remove(&name.into()).map(Data::into_any2)
}
@@ -192,7 +218,7 @@ impl Display for Cmd {
}
}
DataKey::String(k) => {
- if v.as_bool().is_some_and(|b| b) {
+ if v.try_into().is_ok_and(|b| b) {
write!(f, " --{k}")?;
} else if let Some(s) = v.as_str() {
write!(f, " --{k}={s}")?;
diff --git a/yazi-shared/src/event/cow.rs b/yazi-shared/src/event/cow.rs
index 1fb1775c..309b5782 100644
--- a/yazi-shared/src/event/cow.rs
+++ b/yazi-shared/src/event/cow.rs
@@ -1,9 +1,9 @@
-use std::{borrow::Cow, ops::Deref};
+use std::ops::Deref;
use anyhow::Result;
-use super::{Cmd, Data, DataKey};
-use crate::{SStr, url::UrlCow};
+use super::Cmd;
+use crate::data::{Data, DataKey};
#[derive(Debug)]
pub enum CmdCow {
@@ -31,46 +31,42 @@ impl Deref for CmdCow {
}
impl CmdCow {
- #[inline]
- pub fn try_take(&mut self, name: impl Into) -> Option {
+ pub fn take<'a, T>(&mut self, name: impl Into) -> Result
+ where
+ T: TryFrom + TryFrom<&'a Data>,
+ >::Error: Into,
+ >::Error: Into