mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: shell formatting (#3232)
This commit is contained in:
parent
cd6881c9fe
commit
554cb52cc5
86 changed files with 832 additions and 656 deletions
31
Cargo.lock
generated
31
Cargo.lock
generated
|
|
@ -100,9 +100,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.20"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
|
|
@ -411,9 +411,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.2"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
|
@ -459,9 +459,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.39"
|
||||
version = "1.2.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f"
|
||||
checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
|
|
@ -1243,9 +1243,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
|
||||
checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
|
|
@ -1874,11 +1874,10 @@ checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
|||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
|
|
@ -2442,9 +2441,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.4"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
|
|
@ -2452,15 +2451,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-link 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ lru = "0.16.1"
|
|||
mlua = { version = "0.11.4", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
|
||||
objc = "0.2.7"
|
||||
ordered-float = { version = "5.1.0", features = [ "serde" ] }
|
||||
parking_lot = "0.12.4"
|
||||
parking_lot = "0.12.5"
|
||||
paste = "1.0.15"
|
||||
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info", "unstable-widget-ref" ] }
|
||||
regex = "1.11.3"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"version":"0.2","language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes"]}
|
||||
{"version":"0.2","flagWords":[],"language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable"]}
|
||||
|
|
@ -91,7 +91,7 @@ impl Trigger {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use yazi_shared::url::Urn;
|
||||
use yazi_shared::url::{UrlLike, Urn};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Valu
|
|||
use yazi_binding::Style;
|
||||
use yazi_config::THEME;
|
||||
use yazi_plugin::bindings::Range;
|
||||
use yazi_shared::url::UrlLike;
|
||||
|
||||
use super::Lives;
|
||||
use crate::lives::PtrCell;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::ops::Deref;
|
|||
|
||||
use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods, Value};
|
||||
use yazi_binding::{Id, UrlRef, cached_field};
|
||||
use yazi_shared::url::UrlLike;
|
||||
|
||||
use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, hash::Hash, io::{Read, Write}, ops::Deref};
|
||||
use std::{ffi::{OsStr, OsString}, hash::Hash, io::{Read, Write}, ops::Deref};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use crossterm::{execute, style::Print};
|
||||
|
|
@ -7,11 +7,11 @@ use scopeguard::defer;
|
|||
use tokio::io::AsyncWriteExt;
|
||||
use yazi_config::{YAZI, opener::OpenerRule};
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::{File, FilesOp, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}};
|
||||
use yazi_fs::{File, FilesOp, Splatter, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}};
|
||||
use yazi_macro::{err, succ};
|
||||
use yazi_parser::VoidOpt;
|
||||
use yazi_proxy::{AppProxy, HIDER, TasksProxy};
|
||||
use yazi_shared::{OsStrJoin, data::Data, terminal_clear, url::{Component, Url, UrlBuf}};
|
||||
use yazi_shared::{OsStrJoin, data::Data, terminal_clear, url::{Component, Url, UrlBuf, UrlCow, UrlLike}};
|
||||
use yazi_term::tty::TTY;
|
||||
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
||||
use yazi_watcher::WATCHER;
|
||||
|
|
@ -51,10 +51,13 @@ impl Actor for BulkRename {
|
|||
.await?;
|
||||
|
||||
defer! { tokio::spawn(Local.remove_file(tmp.clone())); }
|
||||
TasksProxy::process_exec(Cow::Borrowed(opener), cwd, vec![
|
||||
OsStr::new("").into(),
|
||||
tmp.as_os_str().to_owned().into(),
|
||||
])
|
||||
TasksProxy::process_exec(
|
||||
cwd,
|
||||
Splatter::new(&[UrlCow::default(), Url::regular(&tmp).into()]).splat(&opener.run),
|
||||
vec![UrlCow::default(), UrlBuf::from(&tmp).into()],
|
||||
opener.block,
|
||||
opener.orphan,
|
||||
)
|
||||
.await;
|
||||
|
||||
let _permit = HIDER.acquire().await.unwrap();
|
||||
|
|
|
|||
|
|
@ -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, data::Data, errors::InputError, url::UrlBuf};
|
||||
use yazi_shared::{Debounce, data::Data, errors::InputError, url::{AsUrl, UrlBuf, UrlLike}};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
|
|
|||
|
|
@ -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::data::Data;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
use yazi_widgets::CLIPBOARD;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ 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::{data::Data, url::UrlBuf};
|
||||
use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}};
|
||||
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
||||
use yazi_watcher::WATCHER;
|
||||
|
||||
|
|
|
|||
|
|
@ -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::data::Data;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
|
|||
|
|
@ -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::data::Data;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::{VoidOpt, mgr::CdSource};
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use anyhow::Result;
|
||||
use hashbrown::HashMap;
|
||||
use yazi_config::{YAZI, popup::PickCfg};
|
||||
use yazi_fs::Splatter;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::mgr::OpenDoOpt;
|
||||
use yazi_parser::{mgr::OpenDoOpt, tasks::ProcessOpenOpt};
|
||||
use yazi_proxy::{PickProxy, TasksProxy};
|
||||
use yazi_shared::{data::Data, url::UrlCow};
|
||||
use yazi_shared::{data::Data, url::{UrlBuf, UrlCow}};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -30,21 +30,53 @@ impl Actor for OpenDo {
|
|||
if targets.is_empty() {
|
||||
succ!();
|
||||
} else if !opt.interactive {
|
||||
succ!(cx.tasks.process_with_selected(opt.cwd, targets));
|
||||
succ!(Self::match_and_open(cx, opt.cwd, targets));
|
||||
}
|
||||
|
||||
let openers: Vec<_> = YAZI.opener.all(YAZI.open.common(&targets).into_iter());
|
||||
let openers: Vec<_> = YAZI.opener.all(YAZI.open.common(&targets).into_iter()).collect();
|
||||
if openers.is_empty() {
|
||||
succ!();
|
||||
}
|
||||
|
||||
let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
|
||||
let urls = [UrlCow::default()].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
|
||||
let urls: Vec<_> =
|
||||
[UrlCow::default()].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(choice) = pick.await {
|
||||
TasksProxy::file_open(Cow::Borrowed(openers[choice]), opt.cwd, urls);
|
||||
TasksProxy::open_shell_compat(ProcessOpenOpt {
|
||||
cwd: opt.cwd,
|
||||
cmd: Splatter::new(&urls).splat(&openers[choice].run),
|
||||
args: urls,
|
||||
block: openers[choice].block,
|
||||
orphan: openers[choice].orphan,
|
||||
done: None,
|
||||
spread: openers[choice].spread,
|
||||
});
|
||||
}
|
||||
});
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenDo {
|
||||
// TODO: remove
|
||||
fn match_and_open(cx: &Ctx, cwd: UrlBuf, 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)) {
|
||||
openers.entry(opener).or_insert_with(|| vec![UrlCow::default()]).push(url);
|
||||
}
|
||||
}
|
||||
for (opener, args) in openers {
|
||||
cx.tasks.open_shell_compat(ProcessOpenOpt {
|
||||
cwd: cwd.clone(),
|
||||
cmd: Splatter::new(&args).splat(&opener.run),
|
||||
args,
|
||||
block: opener.block,
|
||||
orphan: opener.orphan,
|
||||
done: None,
|
||||
spread: opener.spread,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use yazi_dds::spark::SparkKind;
|
|||
use yazi_macro::{emit, succ};
|
||||
use yazi_parser::mgr::QuitOpt;
|
||||
use yazi_proxy::ConfirmProxy;
|
||||
use yazi_shared::{data::Data, event::EventQuit, url::UrlCow};
|
||||
use yazi_shared::{data::Data, event::EventQuit, url::{AsUrl, UrlCow}};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ 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, data::Data, url::UrlBuf};
|
||||
use yazi_shared::{Id, data::Data, url::{UrlBuf, UrlLike}};
|
||||
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
||||
use yazi_watcher::WATCHER;
|
||||
|
||||
|
|
|
|||
|
|
@ -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::data::Data;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use anyhow::Result;
|
||||
use yazi_config::{opener::OpenerRule, popup::InputCfg};
|
||||
use yazi_config::popup::InputCfg;
|
||||
use yazi_fs::Splatter;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::mgr::ShellOpt;
|
||||
use yazi_parser::{mgr::ShellOpt, tasks::ProcessOpenOpt};
|
||||
use yazi_proxy::{InputProxy, TasksProxy};
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
|
|
@ -20,7 +21,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().map(Into::into).collect();
|
||||
let selected: Vec<_> = 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))
|
||||
|
|
@ -37,18 +38,15 @@ impl Actor for Shell {
|
|||
return;
|
||||
}
|
||||
|
||||
TasksProxy::file_open(
|
||||
Cow::Owned(OpenerRule {
|
||||
run: opt.run.into_owned(),
|
||||
TasksProxy::open_shell_compat(ProcessOpenOpt {
|
||||
cwd,
|
||||
cmd: Splatter::new(&selected).splat(opt.run.as_ref()),
|
||||
args: selected,
|
||||
block: opt.block,
|
||||
orphan: opt.orphan,
|
||||
desc: Default::default(),
|
||||
r#for: None,
|
||||
done: None,
|
||||
spread: true,
|
||||
}),
|
||||
cwd,
|
||||
selected,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
succ!();
|
||||
|
|
|
|||
|
|
@ -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::data::Data;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
use yazi_watcher::LINKED;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use hashbrown::HashMap;
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::mgr::UpdateMimesOpt;
|
||||
use yazi_shared::{data::Data, pool::InternStr, url::UrlCov};
|
||||
use yazi_shared::{data::Data, pool::InternStr, url::{AsUrl, UrlCov}};
|
||||
use yazi_watcher::LINKED;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::mgr::OpenWithOpt;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct FileOpen;
|
||||
|
||||
impl Actor for FileOpen {
|
||||
type Options = OpenWithOpt;
|
||||
|
||||
const NAME: &str = "file_open";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
succ!(cx.tasks.process_with_opener(
|
||||
opt.cwd,
|
||||
opt.opener,
|
||||
opt.targets.into_iter().map(|u| u.into_os_str2()).collect(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(arrow cancel close file_open inspect process_open show update_succeed);
|
||||
yazi_macro::mod_flat!(arrow cancel close open_shell_compat inspect process_open show update_succeed);
|
||||
|
|
|
|||
19
yazi-actor/src/tasks/open_shell_compat.rs
Normal file
19
yazi-actor/src/tasks/open_shell_compat.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::tasks::ProcessOpenOpt;
|
||||
use yazi_shared::data::Data;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct OpenShellCompat;
|
||||
|
||||
// TODO: remove
|
||||
impl Actor for OpenShellCompat {
|
||||
type Options = ProcessOpenOpt;
|
||||
|
||||
const NAME: &str = "open_shell_compat";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
succ!(cx.tasks.open_shell_compat(opt));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use yazi_shared::{IntoOsStr, url::{AsUrl, UrlCow}};
|
||||
use yazi_shared::{IntoOsStr, url::{AsUrl, UrlCow, UrlLike}};
|
||||
|
||||
use crate::{Urn, cached_field, deprecate};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use futures::executor::block_on;
|
|||
use hashbrown::HashSet;
|
||||
use serde::Serialize;
|
||||
use yazi_fs::{CWD, Xdg, path::expand_url};
|
||||
use yazi_shared::url::{UrlBuf, UrlCow, UrnBuf};
|
||||
use yazi_shared::url::{UrlBuf, UrlCow, UrlLike, UrnBuf};
|
||||
use yazi_vfs::provider;
|
||||
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
|
|
|
|||
|
|
@ -30,40 +30,37 @@ ueberzug_offset = [ 0, 0, 0, 0 ]
|
|||
|
||||
[opener]
|
||||
edit = [
|
||||
{ run = '${EDITOR:-vi} "$@"', desc = "$EDITOR", block = true, for = "unix" },
|
||||
{ run = 'code %*', orphan = true, desc = "code", for = "windows" },
|
||||
{ run = 'code -w %*', block = true, desc = "code (block)", for = "windows" },
|
||||
{ run = "${EDITOR:-vi} %s", desc = "$EDITOR", for = "unix", block = true },
|
||||
{ run = "code %s", desc = "code", for = "windows", orphan = true },
|
||||
{ run = "code -w %s", desc = "code (block)", for = "windows", block = true },
|
||||
]
|
||||
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" },
|
||||
{ run = "xdg-open %s1", desc = "Play", for = "linux" },
|
||||
{ run = "open %s", desc = "Play", for = "macos" },
|
||||
{ run = 'start "" %s1', orphan = true, desc = "Play", for = "windows" },
|
||||
{ run = "termux-open %s1", desc = "Play", for = "android" },
|
||||
{ run = "mediainfo %s1; echo 'Press enter to exit'; read _", block = true, desc = "Show media info", for = "unix" },
|
||||
{ run = "mediainfo %s1 & pause", block = true, desc = "Show media info", for = "windows" },
|
||||
]
|
||||
open = [
|
||||
{ run = 'xdg-open "$1"', desc = "Open", for = "linux" },
|
||||
{ run = 'open "$@"', desc = "Open", for = "macos" },
|
||||
{ run = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" },
|
||||
{ run = 'termux-open "$1"', desc = "Open", for = "android" },
|
||||
{ run = "xdg-open %s1", desc = "Open", for = "linux" },
|
||||
{ run = "open %s", desc = "Open", for = "macos" },
|
||||
{ run = 'start "" %s1', desc = "Open", for = "windows", orphan = true },
|
||||
{ run = "termux-open %s1", desc = "Open", for = "android" },
|
||||
]
|
||||
reveal = [
|
||||
{ run = 'xdg-open "$(dirname "$1")"', desc = "Reveal", for = "linux" },
|
||||
{ run = 'open -R "$1"', desc = "Reveal", for = "macos" },
|
||||
{ run = 'explorer /select,"%1"', orphan = true, desc = "Reveal", for = "windows" },
|
||||
{ run = 'termux-open "$(dirname "$1")"', desc = "Reveal", for = "android" },
|
||||
{ run = '''clear; exiftool "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show EXIF", for = "unix" },
|
||||
{ run = "xdg-open %d1", desc = "Reveal", for = "linux" },
|
||||
{ run = "open -R %s1", desc = "Reveal", for = "macos" },
|
||||
{ run = "explorer /select,%s1", desc = "Reveal", for = "windows", orphan = true },
|
||||
{ run = "termux-open %d1", desc = "Reveal", for = "android" },
|
||||
{ run = "clear; exiftool %s1; echo 'Press enter to exit'; read _", desc = "Show EXIF", for = "unix", block = true },
|
||||
]
|
||||
extract = [
|
||||
{ run = 'ya pub extract --list "$@"', desc = "Extract here", for = "unix" },
|
||||
{ run = 'ya pub extract --list %*', desc = "Extract here", for = "windows" },
|
||||
{ run = "ya pub extract --list %s", desc = "Extract here" },
|
||||
]
|
||||
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" },
|
||||
{ run = "ya emit download --open %S", desc = "Download and open" },
|
||||
{ run = "ya emit download %S", desc = "Download" },
|
||||
]
|
||||
|
||||
[open]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::{Result, bail};
|
|||
use serde::Deserialize;
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::{CWD, SortBy};
|
||||
use yazi_shared::{SyncCell, url::UrlBuf};
|
||||
use yazi_shared::{SyncCell, url::{UrlBuf, UrlLike}};
|
||||
|
||||
use super::{MgrRatio, MouseEvents};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,21 +18,21 @@ impl Deref for Opener {
|
|||
}
|
||||
|
||||
impl Opener {
|
||||
pub fn all<'a, I>(&'a self, uses: I) -> Vec<&'a OpenerRule>
|
||||
pub fn all<'a, I>(&self, uses: I) -> impl Iterator<Item = &OpenerRule>
|
||||
where
|
||||
I: Iterator<Item = &'a str>,
|
||||
{
|
||||
uses.flat_map(|use_| self.get(use_)).flatten().collect()
|
||||
uses.flat_map(|use_| self.get(use_)).flatten()
|
||||
}
|
||||
|
||||
pub fn first<'a, 'b, I>(&'a self, uses: I) -> Option<&'a OpenerRule>
|
||||
pub fn first<'a, I>(&self, uses: I) -> Option<&OpenerRule>
|
||||
where
|
||||
I: Iterator<Item = &'b str>,
|
||||
I: Iterator<Item = &'a str>,
|
||||
{
|
||||
uses.flat_map(|use_| self.get(use_)).flatten().next()
|
||||
}
|
||||
|
||||
pub fn block<'a, I>(&'a self, uses: I) -> Option<&'a OpenerRule>
|
||||
pub fn block<'a, I>(&self, uses: I) -> Option<&OpenerRule>
|
||||
where
|
||||
I: Iterator<Item = &'a str>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use anyhow::{Result, bail};
|
||||
use serde::Deserialize;
|
||||
use yazi_fs::Splatter;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct OpenerRule {
|
||||
|
|
@ -35,11 +36,12 @@ impl OpenerRule {
|
|||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
self.spread = self.run.contains("$@") || self.run.contains("$*");
|
||||
self.spread =
|
||||
Splatter::<()>::spread(&self.run) || self.run.contains("$@") || self.run.contains("$*");
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.spread = self.run.contains("%*");
|
||||
self.spread = Splatter::<()>::spread(&self.run) || self.run.contains("%*");
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use hashbrown::HashMap;
|
|||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_codegen::DeserializeOver2;
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::Condition;
|
||||
use yazi_shared::{Condition, url::UrlLike};
|
||||
|
||||
use crate::{Color, Icon as I, Pattern, Style};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use std::iter;
|
||||
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_adapter::Dimension;
|
||||
use yazi_config::popup::{Origin, Position};
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_fs::Splatable;
|
||||
use yazi_shared::url::{AsUrl, Url, UrlBuf};
|
||||
use yazi_watcher::Watcher;
|
||||
|
||||
use super::{Mimetype, Tabs, Yanked};
|
||||
|
|
@ -53,3 +56,29 @@ impl Mgr {
|
|||
#[inline]
|
||||
pub fn parent_mut(&mut self) -> Option<&mut Folder> { self.active_mut().parent.as_mut() }
|
||||
}
|
||||
|
||||
impl Splatable for Mgr {
|
||||
fn tab(&self) -> usize { self.tabs.cursor }
|
||||
|
||||
fn selected(&self, tab: usize, mut idx: Option<usize>) -> impl Iterator<Item = Url<'_>> {
|
||||
idx = idx.and_then(|i| i.checked_sub(1));
|
||||
tab
|
||||
.checked_sub(1)
|
||||
.and_then(|tab| self.tabs.get(tab))
|
||||
.map(|tab| tab.selected_or_hovered())
|
||||
.unwrap_or_else(|| Box::new(iter::empty()))
|
||||
.skip(idx.unwrap_or(0))
|
||||
.take(if idx.is_some() { 1 } else { usize::MAX })
|
||||
.map(|u| u.as_url())
|
||||
}
|
||||
|
||||
fn hovered(&self, tab: usize) -> Option<Url<'_>> {
|
||||
tab
|
||||
.checked_sub(1)
|
||||
.and_then(|tab| self.tabs.get(tab))
|
||||
.and_then(|tab| tab.hovered())
|
||||
.map(|h| h.url.as_url())
|
||||
}
|
||||
|
||||
fn yanked(&self) -> impl Iterator<Item = Url<'_>> { self.yanked.iter().map(|u| u.as_url()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use hashbrown::HashSet;
|
|||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::FilesOp;
|
||||
use yazi_macro::err;
|
||||
use yazi_shared::url::{Url, UrlBuf, UrlBufCov, UrlCov};
|
||||
use yazi_shared::url::{Url, UrlBuf, UrlBufCov, UrlCov, UrlLike};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Yanked {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use hashbrown::HashSet;
|
||||
use tracing::debug;
|
||||
use yazi_shared::url::{UrlBuf, UrlBufCov};
|
||||
use yazi_shared::url::{UrlBuf, UrlBufCov, UrlLike};
|
||||
|
||||
use super::Tasks;
|
||||
use crate::mgr::Yanked;
|
||||
|
|
|
|||
|
|
@ -1,52 +1,33 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, mem};
|
||||
use std::mem;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use yazi_config::{YAZI, opener::OpenerRule};
|
||||
use yazi_parser::tasks::ProcessOpenOpt;
|
||||
use yazi_shared::url::{UrlBuf, UrlCow};
|
||||
|
||||
use super::Tasks;
|
||||
|
||||
impl Tasks {
|
||||
pub fn process_with_selected(&self, cwd: UrlBuf, 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)) {
|
||||
openers
|
||||
.entry(opener)
|
||||
.or_insert_with(|| vec![OsStr::new("").into()])
|
||||
.push(url.into_os_str2());
|
||||
}
|
||||
}
|
||||
for (opener, args) in openers {
|
||||
self.process_with_opener(cwd.clone(), Cow::Borrowed(opener), args);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_with_opener(
|
||||
&self,
|
||||
cwd: UrlBuf,
|
||||
opener: Cow<'static, OpenerRule>,
|
||||
mut args: Vec<Cow<'static, OsStr>>,
|
||||
) {
|
||||
if opener.spread {
|
||||
self.scheduler.process_open(ProcessOpenOpt { cwd, opener, args, done: None });
|
||||
// TODO: remove
|
||||
pub fn open_shell_compat(&self, mut opt: ProcessOpenOpt) {
|
||||
if opt.spread {
|
||||
self.scheduler.process_open(opt);
|
||||
return;
|
||||
}
|
||||
if args.is_empty() {
|
||||
if opt.args.is_empty() {
|
||||
return;
|
||||
}
|
||||
if args.len() == 2 {
|
||||
self.scheduler.process_open(ProcessOpenOpt { cwd, opener, args, done: None });
|
||||
if opt.args.len() == 2 {
|
||||
self.scheduler.process_open(opt);
|
||||
return;
|
||||
}
|
||||
let hovered = mem::take(&mut args[0]);
|
||||
for target in args.into_iter().skip(1) {
|
||||
let hovered = mem::take(&mut opt.args[0]);
|
||||
for target in opt.args.into_iter().skip(1) {
|
||||
self.scheduler.process_open(ProcessOpenOpt {
|
||||
cwd: cwd.clone(),
|
||||
opener: opener.clone(),
|
||||
cwd: opt.cwd.clone(),
|
||||
cmd: opt.cmd.clone(),
|
||||
args: vec![hovered.clone(), target],
|
||||
block: opt.block,
|
||||
orphan: opt.orphan,
|
||||
done: None,
|
||||
spread: opt.spread,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ pub enum Spark<'a> {
|
|||
Linemode(yazi_parser::mgr::LinemodeOpt),
|
||||
Link(yazi_parser::mgr::LinkOpt),
|
||||
Open(yazi_parser::mgr::OpenOpt),
|
||||
OpenWith(yazi_parser::mgr::OpenWithOpt),
|
||||
OpenDo(yazi_parser::mgr::OpenDoOpt),
|
||||
Paste(yazi_parser::mgr::PasteOpt),
|
||||
Peek(yazi_parser::mgr::PeekOpt),
|
||||
|
|
@ -160,7 +159,6 @@ impl<'a> IntoLua for Spark<'a> {
|
|||
Self::Linemode(b) => b.into_lua(lua),
|
||||
Self::Link(b) => b.into_lua(lua),
|
||||
Self::Open(b) => b.into_lua(lua),
|
||||
Self::OpenWith(b) => b.into_lua(lua),
|
||||
Self::OpenDo(b) => b.into_lua(lua),
|
||||
Self::Paste(b) => b.into_lua(lua),
|
||||
Self::Peek(b) => b.into_lua(lua),
|
||||
|
|
@ -295,7 +293,6 @@ try_from_spark!(mgr::LinemodeOpt, mgr:linemode);
|
|||
try_from_spark!(mgr::LinkOpt, mgr:link);
|
||||
try_from_spark!(mgr::OpenDoOpt, mgr:open_do);
|
||||
try_from_spark!(mgr::OpenOpt, mgr:open);
|
||||
try_from_spark!(mgr::OpenWithOpt, mgr:open_with);
|
||||
try_from_spark!(mgr::PasteOpt, mgr:paste);
|
||||
try_from_spark!(mgr::PeekOpt, mgr:peek);
|
||||
try_from_spark!(mgr::QuitOpt, mgr:quit);
|
||||
|
|
|
|||
|
|
@ -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::data::Data;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
|
||||
use crate::app::App;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::ffi::OsString;
|
|||
|
||||
use yazi_boot::ARGS;
|
||||
use yazi_fs::provider::{Provider, local::Local};
|
||||
use yazi_shared::event::EventQuit;
|
||||
use yazi_shared::{event::EventQuit, url::UrlLike};
|
||||
|
||||
use crate::{Term, app::App};
|
||||
|
||||
|
|
|
|||
|
|
@ -164,8 +164,8 @@ impl<'a> Executor<'a> {
|
|||
on!(arrow);
|
||||
on!(inspect);
|
||||
on!(cancel);
|
||||
on!(file_open);
|
||||
on!(process_open);
|
||||
on!(open_shell_compat);
|
||||
|
||||
match cmd.name.as_ref() {
|
||||
// Help
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{env::{current_dir, set_current_dir}, ops::Deref, path::PathBuf, sync::{Arc, atomic::{AtomicBool, Ordering}}};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use yazi_shared::{RoCell, url::UrlBuf};
|
||||
use yazi_shared::{RoCell, url::{UrlBuf, UrlLike}};
|
||||
|
||||
use crate::FsUrl;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{ffi::OsStr, hash::{BuildHasher, Hash, Hasher}, ops::Deref, path::{Path, PathBuf}};
|
||||
|
||||
use yazi_shared::url::{Uri, UrlBuf, Urn};
|
||||
use yazi_shared::url::{Uri, UrlBuf, UrlLike, Urn};
|
||||
|
||||
use crate::cha::{Cha, ChaType};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use tokio::io;
|
||||
use yazi_shared::url::{Component, UrlBuf};
|
||||
use yazi_shared::url::{Component, UrlBuf, UrlLike};
|
||||
|
||||
#[inline]
|
||||
pub fn ok_or_not_found<T: Default>(result: io::Result<T>) -> io::Result<T> {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
yazi_macro::mod_pub!(cha error mounts path provider);
|
||||
|
||||
yazi_macro::mod_flat!(cwd file files filter fns op sorter sorting stage url xdg);
|
||||
yazi_macro::mod_flat!(cwd file files filter fns op sorter sorting splatter stage url xdg);
|
||||
|
||||
pub fn init() {
|
||||
CWD.init(<_>::default());
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
|||
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use yazi_macro::relay;
|
||||
use yazi_shared::{Id, Ids, url::{UrlBuf, UrnBuf}};
|
||||
use yazi_shared::{Id, Ids, url::{UrlBuf, UrlLike, UrnBuf}};
|
||||
|
||||
use super::File;
|
||||
use crate::{cha::Cha, error::Error};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, path::{Path, PathBuf}};
|
||||
|
||||
use yazi_shared::{loc::LocBuf, url::{Url, UrlBuf, UrlCow}};
|
||||
use yazi_shared::{loc::LocBuf, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
||||
|
||||
use crate::{CWD, path::clean_url};
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ pub fn absolute_url<'a>(url: Url<'a>) -> UrlCow<'a> {
|
|||
)
|
||||
.expect("Failed to create Loc from drive letter");
|
||||
UrlBuf { loc, scheme: url.scheme.into() }.into()
|
||||
} else if let Ok(rest) = url.loc.strip_prefix("~/")
|
||||
} else if let Some(rest) = url.loc.strip_prefix("~/")
|
||||
&& let Some(home) = dirs::home_dir()
|
||||
&& home.is_absolute()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::OsStr};
|
||||
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
|
||||
pub fn skip_url(url: &UrlBuf, n: usize) -> Cow<'_, OsStr> {
|
||||
let mut it = url.components();
|
||||
|
|
@ -36,7 +36,7 @@ pub fn backslash_to_slash(p: &std::path::Path) -> Cow<'_, std::path::Path> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use yazi_shared::url::UrlCow;
|
||||
use yazi_shared::url::{AsUrl, UrlCow};
|
||||
|
||||
use crate::path::url_relative_to;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, path::{Path, PathBuf}};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use yazi_shared::{loc::LocBuf, url::{Url, UrlBuf, UrlCow}};
|
||||
use yazi_shared::{loc::LocBuf, url::{Url, UrlBuf, UrlCow, UrlLike}};
|
||||
|
||||
pub fn path_relative_to<'a>(
|
||||
from: impl AsRef<Path>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use yazi_shared::{LcgRng, natsort, translit::Transliterator, url::UrnBuf};
|
||||
use yazi_shared::{LcgRng, natsort, translit::Transliterator, url::{UrlLike, UrnBuf}};
|
||||
|
||||
use crate::{File, SortBy};
|
||||
|
||||
|
|
|
|||
355
yazi-fs/src/splatter.rs
Normal file
355
yazi-fs/src/splatter.rs
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
#[cfg(unix)]
|
||||
use std::os::unix::ffi::{OsStrExt, OsStringExt};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
use std::{cell::Cell, ffi::{OsStr, OsString}, iter::{self, Peekable}, mem};
|
||||
|
||||
use yazi_shared::url::{AsUrl, Url, UrlCow};
|
||||
|
||||
use crate::FsUrl;
|
||||
|
||||
#[cfg(unix)]
|
||||
type Iter<'a> = Peekable<std::iter::Copied<std::slice::Iter<'a, u8>>>;
|
||||
#[cfg(windows)]
|
||||
type Iter<'a> = Peekable<std::os::windows::ffi::EncodeWide<'a>>;
|
||||
|
||||
#[cfg(unix)]
|
||||
type Buf = Vec<u8>;
|
||||
#[cfg(windows)]
|
||||
type Buf = Vec<u16>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Splatter<T> {
|
||||
src: T,
|
||||
tab: usize,
|
||||
}
|
||||
|
||||
pub trait Splatable {
|
||||
fn tab(&self) -> usize;
|
||||
|
||||
fn selected(&self, tab: usize, idx: Option<usize>) -> impl Iterator<Item = Url<'_>>;
|
||||
|
||||
fn hovered(&self, tab: usize) -> Option<Url<'_>>;
|
||||
|
||||
fn yanked(&self) -> impl Iterator<Item = Url<'_>>;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn b2c(b: u8) -> Option<char> { Some(b as char) }
|
||||
#[cfg(windows)]
|
||||
fn b2c(b: u16) -> Option<char> { char::from_u32(b as u32) }
|
||||
|
||||
fn cue(buf: &mut Buf, s: impl AsRef<OsStr>) {
|
||||
#[cfg(unix)]
|
||||
buf.extend(yazi_shared::shell::escape_os_str(s.as_ref()).as_bytes());
|
||||
#[cfg(windows)]
|
||||
buf.extend(yazi_shared::shell::escape_os_str(s.as_ref()).encode_wide());
|
||||
}
|
||||
|
||||
impl<T> Splatter<T>
|
||||
where
|
||||
T: Splatable,
|
||||
{
|
||||
pub fn new(src: T) -> Self { Self { tab: src.tab() + 1, src } }
|
||||
|
||||
pub fn splat(mut self, cmd: impl AsRef<OsStr>) -> OsString {
|
||||
#[cfg(unix)]
|
||||
let mut it = cmd.as_ref().as_bytes().iter().copied().peekable();
|
||||
#[cfg(windows)]
|
||||
let mut it = cmd.as_ref().encode_wide().peekable();
|
||||
|
||||
let mut buf = vec![];
|
||||
while let Some(cur) = it.next() {
|
||||
if b2c(cur) == Some('%') && it.peek().is_some() {
|
||||
self.visit(&mut it, &mut buf);
|
||||
} else {
|
||||
buf.push(cur);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
return OsString::from_vec(buf);
|
||||
#[cfg(windows)]
|
||||
return OsString::from_wide(&buf);
|
||||
}
|
||||
|
||||
fn visit(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
let c = it.peek().copied().and_then(b2c);
|
||||
match c {
|
||||
Some('s') | Some('S') => self.visit_selected(it, buf),
|
||||
Some('h') | Some('H') => self.visit_hovered(it, buf),
|
||||
Some('d') | Some('D') => self.visit_dirname(it, buf),
|
||||
Some('t') | Some('T') => self.visit_tab(it, buf),
|
||||
Some('y') | Some('Y') => self.visit_yanked(it, buf),
|
||||
Some('%') => self.visit_escape(it, buf),
|
||||
Some('*') => self.visit_selected(it, buf), // TODO: remove this
|
||||
Some(c) if c.is_ascii_digit() => self.visit_digit(it, buf),
|
||||
_ => self.visit_unknown(it, buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_selected(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
let c = it.next().and_then(b2c);
|
||||
let idx = self.consume_digit(it);
|
||||
|
||||
let mut first = true;
|
||||
for url in self.src.selected(self.tab, idx) {
|
||||
if !mem::replace(&mut first, false) {
|
||||
buf.push(b' ' as _);
|
||||
}
|
||||
|
||||
if c == Some('S') {
|
||||
cue(buf, url.os_str());
|
||||
} else {
|
||||
cue(buf, url.unified_path_str());
|
||||
}
|
||||
}
|
||||
if first && idx.is_some() {
|
||||
cue(buf, "");
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_hovered(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
match it.next().and_then(b2c) {
|
||||
Some('h') => {
|
||||
cue(buf, self.src.hovered(self.tab).map(|u| u.unified_path_str()).unwrap_or_default());
|
||||
}
|
||||
Some('H') => {
|
||||
cue(buf, self.src.hovered(self.tab).map(|u| u.os_str()).unwrap_or_default());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_dirname(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
let c = it.next().and_then(b2c);
|
||||
let idx = self.consume_digit(it);
|
||||
|
||||
let mut first = true;
|
||||
for url in self.src.selected(self.tab, idx) {
|
||||
if !mem::replace(&mut first, false) {
|
||||
buf.push(b' ' as _);
|
||||
}
|
||||
|
||||
if c == Some('D') {
|
||||
cue(buf, url.parent().map(|p| p.os_str()).unwrap_or_default());
|
||||
} else {
|
||||
cue(buf, url.parent().map(|p| p.unified_path_str()).unwrap_or_default());
|
||||
}
|
||||
}
|
||||
if first && idx.is_some() {
|
||||
cue(buf, "");
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_tab(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
let old = self.tab;
|
||||
match it.next().and_then(b2c) {
|
||||
Some('t') => self.tab = self.tab.saturating_add(1),
|
||||
Some('T') => self.tab = self.tab.saturating_sub(1),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
self.visit(it, buf);
|
||||
self.tab = old;
|
||||
}
|
||||
|
||||
fn visit_digit(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
// TODO: remove
|
||||
match self.consume_digit(it) {
|
||||
Some(0) => {
|
||||
cue(buf, self.src.hovered(self.tab).map(|u| u.unified_path_str()).unwrap_or_default());
|
||||
}
|
||||
Some(n) => {
|
||||
cue(
|
||||
buf,
|
||||
self
|
||||
.src
|
||||
.selected(self.tab, Some(n))
|
||||
.next()
|
||||
.map(|u| u.unified_path_str())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_yanked(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
let c = it.next().and_then(b2c);
|
||||
|
||||
let mut first = true;
|
||||
for url in self.src.yanked() {
|
||||
if !mem::replace(&mut first, false) {
|
||||
buf.push(b' ' as _);
|
||||
}
|
||||
|
||||
if c == Some('Y') {
|
||||
cue(buf, url.os_str());
|
||||
} else {
|
||||
cue(buf, url.unified_path_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_escape(&mut self, it: &mut Iter, buf: &mut Buf) { buf.push(it.next().unwrap()); }
|
||||
|
||||
fn visit_unknown(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
buf.push(b'%' as _);
|
||||
if let Some(b) = it.next() {
|
||||
buf.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_digit(&mut self, it: &mut Iter) -> Option<usize> {
|
||||
fn next(it: &mut Iter) -> Option<usize> {
|
||||
let n = b2c(*it.peek()?)?.to_digit(10)? as usize;
|
||||
it.next();
|
||||
Some(n)
|
||||
}
|
||||
|
||||
let mut sum = next(it)?;
|
||||
while let Some(n) = next(it) {
|
||||
sum = sum.checked_mul(10)?.checked_add(n)?;
|
||||
}
|
||||
Some(sum)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Splatter<T> {
|
||||
pub fn spread(cmd: impl AsRef<OsStr>) -> bool {
|
||||
struct Source(Cell<bool>);
|
||||
|
||||
impl Splatable for &Source {
|
||||
fn tab(&self) -> usize { 0 }
|
||||
|
||||
fn selected(&self, _tab: usize, idx: Option<usize>) -> impl Iterator<Item = Url<'_>> {
|
||||
if idx.is_none() {
|
||||
self.0.set(true);
|
||||
}
|
||||
iter::empty()
|
||||
}
|
||||
|
||||
fn hovered(&self, _tab: usize) -> Option<Url<'_>> { None }
|
||||
|
||||
fn yanked(&self) -> impl Iterator<Item = Url<'_>> {
|
||||
self.0.set(true);
|
||||
iter::empty()
|
||||
}
|
||||
}
|
||||
|
||||
let src = Source(Cell::new(false));
|
||||
Splatter { src: &src, tab: 1 }.splat(cmd.as_ref());
|
||||
src.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
impl<'a, T> Splatable for &'a T
|
||||
where
|
||||
T: AsRef<[UrlCow<'a>]>,
|
||||
{
|
||||
fn tab(&self) -> usize { 0 }
|
||||
|
||||
fn selected(&self, tab: usize, idx: Option<usize>) -> impl Iterator<Item = Url<'_>> {
|
||||
self
|
||||
.as_ref()
|
||||
.iter()
|
||||
.filter(move |_| tab == 1)
|
||||
.map(|u| u.as_url())
|
||||
.skip(idx.unwrap_or(1))
|
||||
.take(if idx.is_some() { 1 } else { usize::MAX })
|
||||
}
|
||||
|
||||
fn hovered(&self, tab: usize) -> Option<Url<'_>> {
|
||||
self.as_ref().first().filter(|_| tab == 1).map(|u| u.as_url())
|
||||
}
|
||||
|
||||
fn yanked(&self) -> impl Iterator<Item = Url<'_>> { iter::empty() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
struct Source(usize);
|
||||
|
||||
impl Splatable for Source {
|
||||
fn tab(&self) -> usize { self.0 }
|
||||
|
||||
fn selected(&self, tab: usize, mut idx: Option<usize>) -> impl Iterator<Item = Url<'_>> {
|
||||
let urls = if tab == 1 {
|
||||
vec![Url::regular("t1/s1"), Url::regular("t1/s2")]
|
||||
} else if tab == 2 {
|
||||
vec![Url::regular("t 2/s 1"), Url::regular("t 2/s 2")]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
idx = idx.and_then(|i| i.checked_sub(1));
|
||||
urls.into_iter().skip(idx.unwrap_or(0)).take(if idx.is_some() { 1 } else { usize::MAX })
|
||||
}
|
||||
|
||||
fn hovered(&self, tab: usize) -> Option<Url<'_>> {
|
||||
if tab == 1 {
|
||||
Some(Url::regular("hovered"))
|
||||
} else if tab == 2 {
|
||||
Some(Url::regular("hover ed"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn yanked(&self) -> impl Iterator<Item = Url<'_>> {
|
||||
[Url::regular("y1"), Url::regular("y 2"), Url::regular("y3")].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_unix() {
|
||||
let cases = [
|
||||
// Selected
|
||||
(Source(0), r#"ls %s"#, r#"ls t1/s1 t1/s2"#),
|
||||
(Source(0), r#"ls %s1 %s2 %s3"#, r#"ls t1/s1 t1/s2 ''"#),
|
||||
(Source(0), r#"ls %s %s2 %s"#, r#"ls t1/s1 t1/s2 t1/s2 t1/s1 t1/s2"#),
|
||||
(Source(1), r#"ls %s"#, r#"ls 't 2/s 1' 't 2/s 2'"#),
|
||||
(Source(1), r#"ls %s1 %s3 %s2"#, r#"ls 't 2/s 1' '' 't 2/s 2'"#),
|
||||
(Source(2), r#"ls %s"#, r#"ls "#),
|
||||
(Source(2), r#"ls %s1 %s %s2"#, r#"ls '' ''"#),
|
||||
// Hovered
|
||||
(Source(0), r#"ls %h"#, r#"ls hovered"#),
|
||||
(Source(1), r#"ls %h"#, r#"ls 'hover ed'"#),
|
||||
(Source(2), r#"ls %h"#, r#"ls ''"#),
|
||||
// Dirname
|
||||
(Source(0), r#"cd %d"#, r#"cd t1 t1"#),
|
||||
(Source(1), r#"cd %d"#, r#"cd 't 2' 't 2'"#),
|
||||
(Source(1), r#"cd %d1 %d3 %d2"#, r#"cd 't 2' '' 't 2'"#),
|
||||
(Source(2), r#"cd %d %d1"#, r#"cd ''"#),
|
||||
// Yanked
|
||||
(Source(0), r#"cd %y"#, r#"cd y1 'y 2' y3"#),
|
||||
(Source(1), r#"cd %y"#, r#"cd y1 'y 2' y3"#),
|
||||
(Source(2), r#"cd %y"#, r#"cd y1 'y 2' y3"#),
|
||||
// Tab
|
||||
(Source(0), r#"ls %s %ts %s"#, r#"ls t1/s1 t1/s2 't 2/s 1' 't 2/s 2' t1/s1 t1/s2"#),
|
||||
(Source(1), r#"ls %s1 %ts %s2"#, r#"ls 't 2/s 1' 't 2/s 2'"#),
|
||||
(Source(1), r#"ls %s1 %Ts1 %s2 %Ts2"#, r#"ls 't 2/s 1' t1/s1 't 2/s 2' t1/s2"#),
|
||||
(Source(0), r#"ls %s1 %Ts1 %s2 %Ts2"#, r#"ls t1/s1 '' t1/s2 ''"#),
|
||||
(Source(0), r#"ls %ty"#, r#"ls y1 'y 2' y3"#),
|
||||
(Source(0), r#"ls %Ty"#, r#"ls y1 'y 2' y3"#),
|
||||
// Escape
|
||||
(
|
||||
Source(0),
|
||||
r#"echo % %% %s2 %%h %d %%%y %%%%ts %%%%%ts1"#,
|
||||
r#"echo % % t1/s2 %h t1 t1 %y1 'y 2' y3 %%ts %%'t 2/s 1'"#,
|
||||
),
|
||||
// TODO: remove
|
||||
(Source(0), r#"ls %1 %* %2 %0 %3"#, r#"ls t1/s1 t1/s1 t1/s2 t1/s2 hovered ''"#),
|
||||
];
|
||||
|
||||
for (src, cmd, expected) in cases {
|
||||
let s = Splatter::new(src).splat(OsStr::new(cmd));
|
||||
assert_eq!(s, OsStr::new(expected), "{cmd}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,27 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
|
||||
|
||||
use twox_hash::XxHash3_128;
|
||||
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf, UrlCow}};
|
||||
use yazi_shared::{scheme::SchemeRef, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
||||
|
||||
use crate::Xdg;
|
||||
|
||||
pub trait FsUrl {
|
||||
pub trait FsUrl<'a> {
|
||||
fn cache(&self) -> Option<PathBuf>;
|
||||
|
||||
fn unified_path(self) -> Cow<'a, Path>;
|
||||
|
||||
fn unified_path_str(self) -> Cow<'a, OsStr>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match self.unified_path() {
|
||||
Cow::Borrowed(p) => p.as_os_str().into(),
|
||||
Cow::Owned(p) => p.into_os_string().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FsUrl for Url<'_> {
|
||||
impl<'a> FsUrl<'a> for Url<'a> {
|
||||
fn cache(&self) -> Option<PathBuf> {
|
||||
match self.scheme {
|
||||
SchemeRef::Regular | SchemeRef::Search(_) => None,
|
||||
|
|
@ -25,12 +37,28 @@ impl FsUrl for Url<'_> {
|
|||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn unified_path(self) -> Cow<'a, Path> {
|
||||
self.cache().map(Cow::Owned).unwrap_or_else(|| Cow::Borrowed(self.loc.as_path()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FsUrl for UrlBuf {
|
||||
impl<'a> FsUrl<'a> for UrlBuf {
|
||||
fn cache(&self) -> Option<PathBuf> { self.as_url().cache() }
|
||||
|
||||
fn unified_path(self) -> Cow<'a, Path> {
|
||||
self.cache().unwrap_or_else(|| self.loc.into_path()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FsUrl for UrlCow<'_> {
|
||||
impl<'a> FsUrl<'a> for UrlCow<'a> {
|
||||
fn cache(&self) -> Option<PathBuf> { self.as_url().cache() }
|
||||
|
||||
fn unified_path(self) -> Cow<'a, Path> {
|
||||
match (self.cache(), self) {
|
||||
(None, UrlCow::Borrowed { loc, .. }) => loc.as_path().into(),
|
||||
(None, UrlCow::Owned { loc, .. }) => loc.into_path().into(),
|
||||
(Some(cache), _) => cache.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ impl TryFrom<CmdCow> for NotifyOpt {
|
|||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
||||
c.take_any("option").ok_or_else(|| anyhow!("Invalid 'option' in NotifyOpt"))
|
||||
c.take_any("opt").ok_or_else(|| anyhow!("Invalid 'opt' in NotifyOpt"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ yazi_macro::mod_flat!(
|
|||
link
|
||||
open
|
||||
open_do
|
||||
open_with
|
||||
paste
|
||||
peek
|
||||
quit
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow::bail;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
||||
|
||||
|
|
@ -8,8 +9,16 @@ pub struct OpenDoOpt {
|
|||
pub interactive: bool,
|
||||
}
|
||||
|
||||
impl From<CmdCow> for OpenDoOpt {
|
||||
fn from(mut c: CmdCow) -> Self { c.take_any("option").unwrap_or_default() }
|
||||
impl TryFrom<CmdCow> for OpenDoOpt {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
||||
if let Some(opt) = c.take_any2("opt") {
|
||||
opt
|
||||
} else {
|
||||
bail!("'opt' is required for OpenDoOpt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for OpenDoOpt {
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
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, UrlCow}};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OpenWithOpt {
|
||||
pub opener: Cow<'static, OpenerRule>,
|
||||
pub cwd: UrlBuf,
|
||||
pub targets: Vec<UrlCow<'static>>,
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for OpenWithOpt {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
||||
c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in OpenWithOpt"))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for OpenWithOpt {
|
||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||
}
|
||||
|
||||
impl IntoLua for OpenWithOpt {
|
||||
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||
}
|
||||
|
|
@ -1,25 +1,28 @@
|
|||
use std::{borrow::Cow, ffi::OsStr};
|
||||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_config::opener::OpenerRule;
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
||||
|
||||
// --- Exec
|
||||
#[derive(Debug)]
|
||||
pub struct ProcessOpenOpt {
|
||||
pub cwd: UrlBuf,
|
||||
pub opener: Cow<'static, OpenerRule>,
|
||||
pub args: Vec<Cow<'static, OsStr>>,
|
||||
pub cmd: OsString,
|
||||
pub args: Vec<UrlCow<'static>>,
|
||||
pub block: bool,
|
||||
pub orphan: bool,
|
||||
pub done: Option<oneshot::Sender<()>>,
|
||||
|
||||
pub spread: bool, // TODO: remove
|
||||
}
|
||||
|
||||
impl TryFrom<CmdCow> for ProcessOpenOpt {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
||||
c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in ProcessOpenOpt"))
|
||||
c.take_any("opt").ok_or_else(|| anyhow!("Missing 'opt' in ProcessOpenOpt"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
2
yazi-plugin/src/external/fd.rs
vendored
2
yazi-plugin/src/external/fd.rs
vendored
|
|
@ -3,7 +3,7 @@ use std::process::Stdio;
|
|||
use anyhow::Result;
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::{Child, Command}, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
pub struct FdOpt {
|
||||
|
|
|
|||
2
yazi-plugin/src/external/rg.rs
vendored
2
yazi-plugin/src/external/rg.rs
vendored
|
|
@ -3,7 +3,7 @@ use std::process::Stdio;
|
|||
use anyhow::{Result, bail};
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
pub struct RgOpt {
|
||||
|
|
|
|||
2
yazi-plugin/src/external/rga.rs
vendored
2
yazi-plugin/src/external/rga.rs
vendored
|
|
@ -3,7 +3,7 @@ use std::process::Stdio;
|
|||
use anyhow::{Result, bail};
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
pub struct RgaOpt {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use mlua::{ExternalError, Function, IntoLua, IntoLuaMulti, Lua, Table, Value};
|
|||
use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, Url, UrlRef};
|
||||
use yazi_config::Pattern;
|
||||
use yazi_fs::{mounts::PARTITIONS, provider::{DirReader, FileHolder}};
|
||||
use yazi_shared::url::UrlCow;
|
||||
use yazi_shared::url::{UrlCow, UrlLike};
|
||||
use yazi_vfs::{VfsFile, provider};
|
||||
|
||||
use crate::bindings::SizeCalculator;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::hash::Hash;
|
|||
use mlua::{Function, Lua, Table};
|
||||
use yazi_binding::{FileRef, Url};
|
||||
use yazi_config::YAZI;
|
||||
use yazi_shared::url::UrlLike;
|
||||
|
||||
use super::Utils;
|
||||
use crate::Twox128;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use mlua::{Function, IntoLuaMulti, Lua, Value};
|
||||
use yazi_adapter::{ADAPTOR, Image};
|
||||
use yazi_binding::{Error, UrlRef, elements::Rect};
|
||||
use yazi_shared::url::UrlLike;
|
||||
|
||||
use super::Utils;
|
||||
use crate::bindings::ImageInfo;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use yazi_binding::{Error, elements::{Area, Renderable, Text}};
|
|||
use yazi_config::YAZI;
|
||||
use yazi_parser::mgr::{PreviewLock, UpdatePeekedOpt};
|
||||
use yazi_proxy::MgrProxy;
|
||||
use yazi_shared::errors::PeekError;
|
||||
use yazi_shared::{errors::PeekError, url::UrlLike};
|
||||
|
||||
use super::Utils;
|
||||
use crate::external::Highlighter;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ impl AppProxy {
|
|||
}
|
||||
|
||||
pub fn notify(opt: NotifyOpt) {
|
||||
emit!(Call(relay!(app:notify).with_any("option", opt)));
|
||||
emit!(Call(relay!(app:notify).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub fn update_notify(dur: Duration) {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ impl MgrProxy {
|
|||
}
|
||||
|
||||
pub fn open_do(opt: OpenDoOpt) {
|
||||
emit!(Call(relay!(mgr:open_do).with_any("option", opt)));
|
||||
emit!(Call(relay!(mgr:open_do).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub fn remove_do(targets: Vec<UrlBuf>, permanently: bool) {
|
||||
|
|
|
|||
|
|
@ -1,29 +1,34 @@
|
|||
use std::{borrow::Cow, ffi::OsStr};
|
||||
use std::ffi::OsString;
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_config::opener::OpenerRule;
|
||||
use yazi_macro::{emit, relay};
|
||||
use yazi_parser::{mgr::OpenWithOpt, tasks::ProcessOpenOpt};
|
||||
use yazi_parser::tasks::ProcessOpenOpt;
|
||||
use yazi_shared::url::{UrlBuf, UrlCow};
|
||||
|
||||
pub struct TasksProxy;
|
||||
|
||||
impl TasksProxy {
|
||||
pub fn file_open(opener: Cow<'static, OpenerRule>, cwd: UrlBuf, targets: Vec<UrlCow<'static>>) {
|
||||
emit!(Call(relay!(tasks:file_open).with_any("option", OpenWithOpt { opener, cwd, targets })));
|
||||
// TODO: remove
|
||||
pub fn open_shell_compat(opt: ProcessOpenOpt) {
|
||||
emit!(Call(relay!(tasks:open_shell_compat).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub async fn process_exec(
|
||||
opener: Cow<'static, OpenerRule>,
|
||||
cwd: UrlBuf,
|
||||
args: Vec<Cow<'static, OsStr>>,
|
||||
cmd: OsString,
|
||||
args: Vec<UrlCow<'static>>,
|
||||
block: bool,
|
||||
orphan: bool,
|
||||
) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
emit!(Call(relay!(tasks:process_open).with_any("option", ProcessOpenOpt {
|
||||
emit!(Call(relay!(tasks:process_open).with_any("opt", ProcessOpenOpt {
|
||||
cwd,
|
||||
opener,
|
||||
cmd,
|
||||
args,
|
||||
done: Some(tx)
|
||||
block,
|
||||
orphan,
|
||||
done: Some(tx),
|
||||
spread: false
|
||||
})));
|
||||
rx.await.ok();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ 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::{AsUrl, UrlBuf, UrlCow};
|
||||
use yazi_shared::url::{AsUrl, UrlBuf, UrlCow, UrlLike};
|
||||
use yazi_vfs::{VfsCha, copy_with_progress, maybe_exists, provider::{self, DirEntry}};
|
||||
|
||||
use super::{FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use tracing::error;
|
|||
use yazi_config::Priority;
|
||||
use yazi_fs::FilesOp;
|
||||
use yazi_plugin::isolate;
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlLike}};
|
||||
use yazi_vfs::provider;
|
||||
|
||||
use super::{PreworkInFetch, PreworkInLoad, PreworkInSize};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}};
|
||||
use std::ffi::OsString;
|
||||
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_shared::{Id, url::UrlBuf};
|
||||
use yazi_shared::{Id, url::{UrlBuf, UrlCow}};
|
||||
|
||||
use super::ShellOpt;
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ pub(crate) struct ProcessInBlock {
|
|||
pub(crate) id: Id,
|
||||
pub(crate) cwd: UrlBuf,
|
||||
pub(crate) cmd: OsString,
|
||||
pub(crate) args: Vec<Cow<'static, OsStr>>,
|
||||
pub(crate) args: Vec<UrlCow<'static>>,
|
||||
}
|
||||
|
||||
impl From<ProcessInBlock> 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<Cow<'static, OsStr>>,
|
||||
pub(crate) args: Vec<UrlCow<'static>>,
|
||||
}
|
||||
|
||||
impl From<ProcessInOrphan> 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<Cow<'static, OsStr>>,
|
||||
pub(crate) args: Vec<UrlCow<'static>>,
|
||||
pub(crate) cancel: mpsc::Receiver<()>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, process::Stdio};
|
||||
use std::{borrow::Cow, ffi::OsString, process::Stdio};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use tokio::process::{Child, Command};
|
||||
use yazi_fs::FsUrl;
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{UrlBuf, UrlCow, UrlLike};
|
||||
|
||||
pub(crate) struct ShellOpt {
|
||||
pub(crate) cwd: UrlBuf,
|
||||
pub(crate) cmd: OsString,
|
||||
pub(crate) args: Vec<Cow<'static, OsStr>>,
|
||||
pub(crate) args: Vec<UrlCow<'static>>,
|
||||
pub(crate) piped: bool,
|
||||
pub(crate) orphan: bool,
|
||||
}
|
||||
|
|
@ -39,13 +39,17 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result<Child> {
|
|||
|
||||
#[cfg(unix)]
|
||||
return Ok(unsafe {
|
||||
use yazi_shared::url::AsUrl;
|
||||
|
||||
Command::new("sh")
|
||||
.arg("-c")
|
||||
.stdin(opt.stdio())
|
||||
.stdout(opt.stdio())
|
||||
.stderr(opt.stdio())
|
||||
.arg("-c")
|
||||
.arg(opt.cmd)
|
||||
.args(opt.args)
|
||||
.arg("--")
|
||||
// TODO: remove
|
||||
.args(opt.args.iter().skip(1).map(|u| u.as_url().unified_path_str()))
|
||||
.current_dir(cwd)
|
||||
.kill_on_drop(!opt.orphan)
|
||||
.pre_exec(move || {
|
||||
|
|
@ -60,11 +64,11 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result<Child> {
|
|||
#[cfg(windows)]
|
||||
return Ok(
|
||||
Command::new("cmd.exe")
|
||||
.raw_arg("/C")
|
||||
.raw_arg(parser::parse(&opt.cmd, &opt.args))
|
||||
.stdin(opt.stdio())
|
||||
.stdout(opt.stdio())
|
||||
.stderr(opt.stdio())
|
||||
.raw_arg("/C")
|
||||
.raw_arg(opt.cmd)
|
||||
.current_dir(cwd)
|
||||
.kill_on_drop(!opt.orphan)
|
||||
.spawn()?,
|
||||
|
|
@ -72,176 +76,3 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result<Child> {
|
|||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod parser {
|
||||
use std::{borrow::Cow, ffi::{OsStr, OsString}, iter::Peekable, os::windows::ffi::{EncodeWide, OsStrExt, OsStringExt}};
|
||||
|
||||
macro_rules! w {
|
||||
($c:literal) => {
|
||||
$c as u16
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) fn parse(cmd: &OsStr, args: &[Cow<OsStr>]) -> OsString {
|
||||
if cmd.len() < 2 {
|
||||
return cmd.to_owned();
|
||||
}
|
||||
|
||||
let mut buf = Vec::with_capacity(cmd.len());
|
||||
let mut it = cmd.encode_wide().peekable();
|
||||
|
||||
while let Some(c) = it.next() {
|
||||
if c == w!('%') {
|
||||
let n = visit_percent(it.clone(), &mut buf, &args, false);
|
||||
if n == 0 {
|
||||
buf.push(c);
|
||||
} else {
|
||||
it.by_ref().take(n).for_each(drop);
|
||||
}
|
||||
} else if c == w!('"') && it.peek().is_some_and(|&c| c == w!('%')) {
|
||||
it.next();
|
||||
|
||||
let n = visit_percent(it.clone(), &mut buf, &args, true);
|
||||
if n == 0 {
|
||||
buf.push(c);
|
||||
buf.push(w!('%'));
|
||||
} else {
|
||||
it.by_ref().take(n).for_each(drop);
|
||||
}
|
||||
} else {
|
||||
buf.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
OsString::from_wide(&buf)
|
||||
}
|
||||
|
||||
fn visit_percent(
|
||||
mut it: Peekable<EncodeWide>,
|
||||
buf: &mut Vec<u16>,
|
||||
args: &[Cow<OsStr>],
|
||||
quote: bool,
|
||||
) -> usize {
|
||||
let Some(c) = it.next().and_then(|c| char::from_u32(c as _)) else {
|
||||
return 0;
|
||||
};
|
||||
|
||||
let mut pos = None;
|
||||
if c.is_ascii_digit() {
|
||||
let mut p = c.to_string();
|
||||
while let Some(n) = it.peek().and_then(|&c| char::from_u32(c as _)) {
|
||||
if n.is_ascii_digit() {
|
||||
it.next();
|
||||
p.push(n);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos = Some(p);
|
||||
}
|
||||
|
||||
if quote && !it.next().is_some_and(|e| e == w!('"')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if let Some(p) = pos {
|
||||
if let Some(arg) = args.get(p.parse::<usize>().unwrap()) {
|
||||
if quote {
|
||||
buf.extend(yazi_shared::shell::escape_os_str(arg).encode_wide());
|
||||
} else {
|
||||
buf.extend(arg.encode_wide());
|
||||
}
|
||||
}
|
||||
return p.len() + quote as usize;
|
||||
}
|
||||
|
||||
if c != '*' && c != '@' {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut s = OsString::new();
|
||||
for (i, arg) in args.iter().skip(1).enumerate() {
|
||||
if i > 0 {
|
||||
s.push(" ");
|
||||
}
|
||||
if c == '*' {
|
||||
s.push(yazi_shared::shell::escape_os_str(arg));
|
||||
} else {
|
||||
s.push(arg);
|
||||
}
|
||||
}
|
||||
if quote {
|
||||
buf.extend(yazi_shared::shell::escape_os_str(&s).encode_wide());
|
||||
} else {
|
||||
buf.extend(s.encode_wide());
|
||||
}
|
||||
|
||||
1 + quote as usize
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::OsString;
|
||||
|
||||
fn parse(cmd: &str, args: &[&str]) -> String {
|
||||
let cmd = OsString::from(cmd);
|
||||
let args: Vec<_> = args.iter().map(|&s| OsString::from(s).into()).collect();
|
||||
super::parse(&cmd, &args).to_str().unwrap().to_owned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_quote() {
|
||||
let s = parse("echo abc xyz %0 %2", &["000", "111", "222"]);
|
||||
assert_eq!(s, "echo abc xyz 000 222");
|
||||
|
||||
let s = parse(" echo abc xyz %1 %2 ", &["", "111", "222"]);
|
||||
assert_eq!(s, " echo abc xyz 111 222 ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_quote() {
|
||||
let s = parse("echo 'abc xyz' '%1' %2", &["000", "111", "222"]);
|
||||
assert_eq!(s, "echo 'abc xyz' '111' 222");
|
||||
|
||||
let s = parse(r#"echo 'abc ""xyz' '%1' %2"#, &["", "111", "222"]);
|
||||
assert_eq!(s, r#"echo 'abc ""xyz' '111' 222"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_double_quote() {
|
||||
let s = parse(r#"echo "abc ' 'xyz" "%1" %2 %3"#, &["", "111", "222"]);
|
||||
assert_eq!(s, r#"echo "abc ' 'xyz" 111 222 "#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escaped() {
|
||||
let s = parse(r#"echo "a bc ' 'x\nyz" "\%1" "\"%2"" %3"#, &["", "111", "22 2"]);
|
||||
assert_eq!(s, r#"echo "a bc ' 'x\nyz" "\111" "\"22 2"" "#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_percent_star() {
|
||||
let s = parse("echo %* xyz", &[]);
|
||||
assert_eq!(s, "echo xyz");
|
||||
|
||||
let s = parse("echo %* xyz", &["000", "111", "222"]);
|
||||
assert_eq!(s, "echo 111 222 xyz");
|
||||
|
||||
let s = parse("echo '%*' xyz", &["000", "111", "22 2"]);
|
||||
assert_eq!(s, r#"echo '111 "22 2"' xyz"#);
|
||||
|
||||
let s = parse("echo -C%* xyz", &[]);
|
||||
assert_eq!(s, "echo -C xyz");
|
||||
|
||||
let s = parse("echo -C%* xyz", &["000", " 111", "222"]);
|
||||
assert_eq!(s, r#"echo -C" 111" 222 xyz"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var() {
|
||||
let s = parse(r#"%EDITOR% %@ "%@" %* "%*" xyz"#, &["000", "1 11", "222"]);
|
||||
assert_eq!(s, r#"%EDITOR% 1 11 222 "1 11 222" "1 11" 222 "\"1 11\" 222" xyz"#);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{ffi::OsString, future::Future, sync::Arc, time::Duration};
|
||||
use std::{future::Future, sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use futures::{FutureExt, future::BoxFuture};
|
||||
|
|
@ -9,7 +9,7 @@ use yazi_dds::Pump;
|
|||
use yazi_fs::FsUrl;
|
||||
use yazi_parser::{app::PluginOpt, tasks::ProcessOpenOpt};
|
||||
use yazi_proxy::TasksProxy;
|
||||
use yazi_shared::{Id, Throttle, url::UrlBuf};
|
||||
use yazi_shared::{Id, Throttle, url::{UrlBuf, UrlLike}};
|
||||
use yazi_vfs::{must_be_dir, provider, unique_name};
|
||||
|
||||
use super::{Ongoing, TaskOp};
|
||||
|
|
@ -306,20 +306,20 @@ impl Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn process_open(&self, ProcessOpenOpt { cwd, opener, args, done }: ProcessOpenOpt) {
|
||||
pub fn process_open(&self, opt: ProcessOpenOpt) {
|
||||
let name = {
|
||||
let args = args.iter().map(|a| a.to_string_lossy()).collect::<Vec<_>>().join(" ");
|
||||
let args = opt.args.iter().map(|a| a.display().to_string()).collect::<Vec<_>>().join(" ");
|
||||
if args.is_empty() {
|
||||
format!("Run {:?}", opener.run)
|
||||
format!("Run {:?}", opt.cmd)
|
||||
} else {
|
||||
format!("Run {:?} with `{args}`", opener.run)
|
||||
format!("Run {:?} with `{args}`", opt.cmd)
|
||||
}
|
||||
};
|
||||
|
||||
let mut ongoing = self.ongoing.lock();
|
||||
let (id, clean): (_, TaskOut) = if opener.block {
|
||||
let (id, clean): (_, TaskOut) = if opt.block {
|
||||
(ongoing.add::<ProcessProgBlock>(name), ProcessOutBlock::Clean.into())
|
||||
} else if opener.orphan {
|
||||
} else if opt.orphan {
|
||||
(ongoing.add::<ProcessProgOrphan>(name), ProcessOutOrphan::Clean.into())
|
||||
} else {
|
||||
(ongoing.add::<ProcessProgBg>(name), ProcessOutBg::Clean.into())
|
||||
|
|
@ -332,21 +332,22 @@ impl Scheduler {
|
|||
cancel_tx.send(()).await.ok();
|
||||
cancel_tx.closed().await;
|
||||
}
|
||||
if let Some(tx) = done {
|
||||
if let Some(tx) = opt.done {
|
||||
tx.send(()).ok();
|
||||
}
|
||||
ops.out(id, clean);
|
||||
});
|
||||
|
||||
let cmd = OsString::from(&opener.run);
|
||||
let process = self.process.clone();
|
||||
self.send_micro::<_, TaskOut>(id, NORMAL, async move {
|
||||
if opener.block {
|
||||
process.block(ProcessInBlock { id, cwd, cmd, args }).await?;
|
||||
} else if opener.orphan {
|
||||
process.orphan(ProcessInOrphan { id, cwd, cmd, args }).await?;
|
||||
if opt.block {
|
||||
process.block(ProcessInBlock { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }).await?;
|
||||
} else if opt.orphan {
|
||||
process.orphan(ProcessInOrphan { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }).await?;
|
||||
} else {
|
||||
process.bg(ProcessInBg { id, cwd, cmd, args, cancel: cancel_rx }).await?;
|
||||
process
|
||||
.bg(ProcessInBg { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args, cancel: cancel_rx })
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ impl LocBuf {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::url::UrlBuf;
|
||||
use crate::url::{UrlBuf, UrlLike};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
|
|
|
|||
|
|
@ -204,6 +204,11 @@ impl<'a> Loc<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn strip_prefix(self, base: impl AsRef<Path>) -> Option<&'a Path> {
|
||||
self.inner.strip_prefix(base).ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bytes(self) -> &'a [u8] { self.inner.as_os_str().as_encoded_bytes() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}};
|
||||
|
||||
use crate::url::UrlBuf;
|
||||
use crate::url::{UrlBuf, UrlLike};
|
||||
|
||||
pub trait IntoStringLossy {
|
||||
fn into_string_lossy(self) -> String;
|
||||
|
|
@ -33,5 +33,6 @@ impl IntoStringLossy for Cow<'_, OsStr> {
|
|||
}
|
||||
|
||||
impl IntoStringLossy for &UrlBuf {
|
||||
// FIXME: remove
|
||||
fn into_string_lossy(self) -> String { self.os_str().into_string_lossy() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, hash::BuildHasher, p
|
|||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{loc::LocBuf, pool::Pool, scheme::{Scheme, SchemeRef}, url::{AsUrl, Components, Display, Encode, EncodeTilded, Uri, Url, UrlCow, Urn}};
|
||||
use crate::{loc::LocBuf, pool::Pool, scheme::Scheme, url::{AsUrl, Encode, EncodeTilded, Url, UrlCow}};
|
||||
|
||||
#[derive(Clone, Default, Eq, Hash, PartialEq)]
|
||||
pub struct UrlBuf {
|
||||
|
|
@ -89,67 +89,6 @@ impl UrlBuf {
|
|||
// }; &U
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn join(&self, path: impl AsRef<Path>) -> Self { self.as_url().join(path) }
|
||||
|
||||
#[inline]
|
||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
||||
|
||||
#[inline]
|
||||
pub fn display(&self) -> Display<'_> { Display::new(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other) }
|
||||
|
||||
#[inline]
|
||||
pub fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
|
||||
|
||||
#[inline]
|
||||
pub fn starts_with(&self, base: impl AsUrl) -> bool { self.as_url().starts_with(base) }
|
||||
|
||||
#[inline]
|
||||
pub fn ends_with(&self, child: impl AsUrl) -> bool { self.as_url().ends_with(child) }
|
||||
|
||||
pub fn strip_prefix(&self, base: impl AsUrl) -> Option<&Urn> {
|
||||
use Scheme as S;
|
||||
use SchemeRef as T;
|
||||
|
||||
let base = base.as_url();
|
||||
let prefix = self.loc.strip_prefix(base.loc).ok()?;
|
||||
|
||||
Some(Urn::new(match (&self.scheme, base.scheme) {
|
||||
// Same scheme
|
||||
(S::Regular, T::Regular) => Some(prefix),
|
||||
(S::Search(_), T::Search(_)) => Some(prefix),
|
||||
(S::Archive(a), T::Archive(b)) => Some(prefix).filter(|_| a == b),
|
||||
(S::Sftp(a), T::Sftp(b)) => Some(prefix).filter(|_| a == b),
|
||||
|
||||
// Both are local files
|
||||
(S::Regular, T::Search(_)) => Some(prefix),
|
||||
(S::Search(_), T::Regular) => Some(prefix),
|
||||
|
||||
// Only the entry of archives is a local file
|
||||
(S::Regular, T::Archive(_)) => Some(prefix).filter(|_| base.uri().is_empty()),
|
||||
(S::Search(_), T::Archive(_)) => Some(prefix).filter(|_| base.uri().is_empty()),
|
||||
(S::Archive(_), T::Regular) => Some(prefix).filter(|_| self.uri().is_empty()),
|
||||
(S::Archive(_), T::Search(_)) => Some(prefix).filter(|_| self.uri().is_empty()),
|
||||
|
||||
// Independent virtual file space
|
||||
(S::Regular, T::Sftp(_)) => None,
|
||||
(S::Search(_), T::Sftp(_)) => None,
|
||||
(S::Archive(_), T::Sftp(_)) => None,
|
||||
(S::Sftp(_), T::Regular) => None,
|
||||
(S::Sftp(_), T::Search(_)) => None,
|
||||
(S::Sftp(_), T::Archive(_)) => None,
|
||||
}?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_path(&self) -> Option<&Path> { self.as_url().as_path() }
|
||||
|
||||
#[inline]
|
||||
pub fn into_path(self) -> Option<PathBuf> {
|
||||
Some(self.loc.into_path()).filter(|_| !self.scheme.is_virtual())
|
||||
|
|
@ -163,21 +102,10 @@ impl UrlBuf {
|
|||
Self { loc: self.loc.rebase(base), scheme: self.scheme.clone() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pair(&self) -> Option<(Url<'_>, &Urn)> { self.as_url().pair() }
|
||||
|
||||
#[inline]
|
||||
pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) }
|
||||
}
|
||||
|
||||
impl UrlBuf {
|
||||
#[inline]
|
||||
pub fn as_url(&self) -> Url<'_> { Url { loc: self.loc.as_loc(), scheme: self.scheme.as_ref() } }
|
||||
|
||||
#[inline]
|
||||
pub fn base(&self) -> Option<Url<'_>> { self.as_url().base() }
|
||||
}
|
||||
|
||||
impl UrlBuf {
|
||||
// --- Regular
|
||||
#[inline]
|
||||
|
|
@ -225,34 +153,6 @@ impl UrlBuf {
|
|||
Scheme::Archive(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: remove
|
||||
#[inline]
|
||||
pub fn into_path2(self) -> PathBuf { self.loc.into_path() }
|
||||
|
||||
#[inline]
|
||||
pub fn name(&self) -> Option<&OsStr> { self.as_url().name() }
|
||||
|
||||
#[inline]
|
||||
pub fn stem(&self) -> Option<&OsStr> { self.as_url().stem() }
|
||||
|
||||
#[inline]
|
||||
pub fn ext(&self) -> Option<&OsStr> { self.as_url().ext() }
|
||||
|
||||
#[inline]
|
||||
pub fn uri(&self) -> &Uri { self.as_url().uri() }
|
||||
|
||||
#[inline]
|
||||
pub fn urn(&self) -> &Urn { self.as_url().urn() }
|
||||
|
||||
#[inline]
|
||||
pub fn is_absolute(&self) -> bool { self.as_url().is_absolute() }
|
||||
|
||||
#[inline]
|
||||
pub fn has_root(&self) -> bool { self.as_url().has_root() }
|
||||
|
||||
#[inline]
|
||||
pub fn has_trail(&self) -> bool { self.as_url().has_trail() }
|
||||
}
|
||||
|
||||
impl Debug for UrlBuf {
|
||||
|
|
@ -286,6 +186,7 @@ mod tests {
|
|||
use anyhow::Result;
|
||||
|
||||
use super::*;
|
||||
use crate::url::UrlLike;
|
||||
|
||||
#[test]
|
||||
fn test_join() -> anyhow::Result<()> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, iter::FusedIterator, ops::Not, path::{self, PathBuf, PrefixComponent}};
|
||||
|
||||
use crate::{loc::Loc, scheme::{Scheme, SchemeRef}, url::{Encode, Url, UrlBuf, UrlCow}};
|
||||
use crate::{scheme::{Scheme, SchemeRef}, url::{Encode, Url, UrlBuf}};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Component<'a> {
|
||||
|
|
@ -60,8 +60,7 @@ impl<'a> FromIterator<Component<'a>> for PathBuf {
|
|||
#[derive(Clone)]
|
||||
pub struct Components<'a> {
|
||||
inner: path::Components<'a>,
|
||||
loc: Loc<'a>,
|
||||
scheme: SchemeRef<'a>,
|
||||
url: Url<'a>,
|
||||
scheme_yielded: bool,
|
||||
}
|
||||
|
||||
|
|
@ -69,36 +68,20 @@ impl<'a> From<Url<'a>> for Components<'a> {
|
|||
fn from(value: Url<'a>) -> Self {
|
||||
Self {
|
||||
inner: value.loc.as_path().components(),
|
||||
loc: value.loc,
|
||||
scheme: value.scheme,
|
||||
url: value,
|
||||
scheme_yielded: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBuf> for Components<'a> {
|
||||
fn from(value: &'a UrlBuf) -> Self {
|
||||
Self {
|
||||
inner: value.loc.components(),
|
||||
loc: value.loc.as_loc(),
|
||||
scheme: value.scheme.as_ref(),
|
||||
scheme_yielded: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlCow<'a>> for Components<'a> {
|
||||
fn from(value: &'a UrlCow<'a>) -> Self { Self::from(value.as_url()) }
|
||||
}
|
||||
|
||||
impl<'a> Components<'a> {
|
||||
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
||||
let path = self.inner.as_path();
|
||||
if !self.scheme.is_virtual() || self.scheme_yielded {
|
||||
if !self.url.scheme.is_virtual() || self.scheme_yielded {
|
||||
return path.as_os_str().into();
|
||||
}
|
||||
|
||||
let mut s = OsString::from(Encode::new(self.loc, self.scheme).to_string());
|
||||
let mut s = OsString::from(Encode::from(self.url).to_string());
|
||||
s.reserve_exact(path.as_os_str().len());
|
||||
s.push(path);
|
||||
s.into()
|
||||
|
|
@ -107,7 +90,7 @@ impl<'a> Components<'a> {
|
|||
pub fn covariant(&self, other: &Self) -> bool {
|
||||
match (self.scheme_yielded, other.scheme_yielded) {
|
||||
(false, false) => {}
|
||||
(true, true) if self.scheme.covariant(other.scheme) => {}
|
||||
(true, true) if self.url.scheme.covariant(other.url.scheme) => {}
|
||||
_ => return false,
|
||||
}
|
||||
self.inner == other.inner
|
||||
|
|
@ -120,7 +103,7 @@ impl<'a> Iterator for Components<'a> {
|
|||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if !self.scheme_yielded {
|
||||
self.scheme_yielded = true;
|
||||
Some(Component::Scheme(self.scheme))
|
||||
Some(Component::Scheme(self.url.scheme))
|
||||
} else {
|
||||
self.inner.next().map(Into::into)
|
||||
}
|
||||
|
|
@ -140,7 +123,7 @@ impl<'a> DoubleEndedIterator for Components<'a> {
|
|||
Some(comp.into())
|
||||
} else if !self.scheme_yielded {
|
||||
self.scheme_yielded = true;
|
||||
Some(Component::Scheme(self.scheme))
|
||||
Some(Component::Scheme(self.url.scheme))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -151,8 +134,8 @@ impl<'a> FusedIterator for Components<'a> {}
|
|||
|
||||
impl<'a> PartialEq for Components<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Some(self.scheme).filter(|_| !self.scheme_yielded)
|
||||
== Some(other.scheme).filter(|_| !other.scheme_yielded)
|
||||
Some(self.url.scheme).filter(|_| !self.scheme_yielded)
|
||||
== Some(other.url.scheme).filter(|_| !other.scheme_yielded)
|
||||
&& self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +146,7 @@ mod tests {
|
|||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
use crate::pool::InternStr;
|
||||
use crate::{pool::InternStr, url::UrlLike};
|
||||
|
||||
#[test]
|
||||
fn test_collect() {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{hash::{Hash, Hasher}, ops::Deref, path::PathBuf};
|
|||
use hashbrown::Equivalent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::url::{Url, UrlBuf, UrlCow};
|
||||
use crate::url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UrlCov<'a>(Url<'a>);
|
||||
|
|
@ -19,7 +19,7 @@ impl<'a> From<&'a UrlBufCov> for UrlCov<'a> {
|
|||
}
|
||||
|
||||
impl PartialEq<UrlBufCov> for UrlCov<'_> {
|
||||
fn eq(&self, other: &UrlBufCov) -> bool { self.0.covariant(other.0.as_url()) }
|
||||
fn eq(&self, other: &UrlBufCov) -> bool { self.0.covariant(&other.0) }
|
||||
}
|
||||
|
||||
impl Hash for UrlCov<'_> {
|
||||
|
|
@ -79,21 +79,13 @@ impl<'a> From<&'a UrlBufCov> for Url<'a> {
|
|||
}
|
||||
|
||||
impl Hash for UrlBufCov {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.as_url().hash(state) }
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { UrlCov::from(self).hash(state) }
|
||||
}
|
||||
|
||||
impl PartialEq for UrlBufCov {
|
||||
fn eq(&self, other: &Self) -> bool { self.covariant(other) }
|
||||
fn eq(&self, other: &Self) -> bool { self.covariant(&other.0) }
|
||||
}
|
||||
|
||||
impl PartialEq<UrlBuf> for UrlBufCov {
|
||||
fn eq(&self, other: &UrlBuf) -> bool { self.covariant(other) }
|
||||
}
|
||||
|
||||
impl UrlBufCov {
|
||||
#[inline]
|
||||
pub fn as_url(&self) -> UrlCov<'_> { UrlCov::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn parent(&self) -> Option<Self> { self.0.parent().map(Into::into) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}};
|
||||
use std::{borrow::Cow, path::{Path, PathBuf}};
|
||||
|
||||
use anyhow::Result;
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
use crate::{IntoOsStr, loc::{Loc, LocBuf}, scheme::{SchemeCow, SchemeRef}, url::{Components, Url, UrlBuf, Urn}};
|
||||
use crate::{IntoOsStr, loc::{Loc, LocBuf}, scheme::{SchemeCow, SchemeRef}, url::{AsUrl, Url, UrlBuf}};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum UrlCow<'a> {
|
||||
|
|
@ -113,14 +113,6 @@ impl<'a> UrlCow<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_url(&self) -> Url<'_> {
|
||||
match self {
|
||||
UrlCow::Borrowed { loc, scheme } => Url { loc: *loc, scheme: scheme.as_ref() },
|
||||
UrlCow::Owned { loc, scheme } => Url { loc: loc.as_loc(), scheme: scheme.as_ref() },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> UrlBuf {
|
||||
match self {
|
||||
|
|
@ -137,24 +129,9 @@ impl<'a> UrlCow<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: remove
|
||||
#[inline]
|
||||
pub fn into_os_str2(self) -> Cow<'a, OsStr> {
|
||||
match self {
|
||||
UrlCow::Borrowed { loc, .. } => loc.as_path().as_os_str().into(),
|
||||
UrlCow::Owned { loc, .. } => loc.into_path().into_os_string().into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_owned(&self) -> UrlBuf { self.as_url().into() }
|
||||
|
||||
#[inline]
|
||||
pub fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
|
||||
|
||||
#[inline]
|
||||
pub fn pair(&self) -> Option<(Url<'_>, &Urn)> { self.as_url().pair() }
|
||||
|
||||
pub fn parse(bytes: &[u8]) -> Result<(SchemeCow<'_>, Cow<'_, Path>, Option<(usize, usize)>)> {
|
||||
let mut skip = 0;
|
||||
let (scheme, tilde, uri, urn) = SchemeCow::parse(bytes, &mut skip)?;
|
||||
|
|
@ -179,13 +156,4 @@ impl<'a> UrlCow<'a> {
|
|||
impl UrlCow<'_> {
|
||||
#[inline]
|
||||
pub fn is_regular(&self) -> bool { self.as_url().is_regular() }
|
||||
|
||||
#[inline]
|
||||
pub fn is_absolute(&self) -> bool { self.as_url().is_absolute() }
|
||||
|
||||
#[inline]
|
||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
use crate::url::{Encode, UrlBuf};
|
||||
use crate::url::{Encode, Url};
|
||||
|
||||
pub struct Display<'a> {
|
||||
inner: &'a UrlBuf,
|
||||
inner: Url<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Display<'a> {
|
||||
#[inline]
|
||||
pub fn new(inner: &'a UrlBuf) -> Self { Self { inner } }
|
||||
impl<'a> From<Url<'a>> for Display<'a> {
|
||||
fn from(value: Url<'a>) -> Self { Self { inner: value } }
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Display for Display<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let UrlBuf { loc, scheme } = self.inner;
|
||||
let Url { loc, scheme } = self.inner;
|
||||
if scheme.is_virtual() {
|
||||
Encode::from(self.inner).fmt(f)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,40 +2,35 @@ use std::{fmt::{self, Display}, ops::Not};
|
|||
|
||||
use percent_encoding::{AsciiSet, CONTROLS, PercentEncode, percent_encode};
|
||||
|
||||
use crate::{loc::Loc, scheme::SchemeRef, url::{Url, UrlBuf}};
|
||||
use crate::{scheme::SchemeRef, url::{AsUrl, Url, UrlBuf}};
|
||||
|
||||
pub struct Encode<'a> {
|
||||
loc: Loc<'a>,
|
||||
scheme: SchemeRef<'a>,
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Encode<'a>(Url<'a>);
|
||||
|
||||
impl<'a> From<Url<'a>> for Encode<'a> {
|
||||
fn from(url: Url<'a>) -> Self { Self::new(url.loc, url.scheme) }
|
||||
fn from(value: Url<'a>) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBuf> for Encode<'a> {
|
||||
fn from(url: &'a UrlBuf) -> Self { Self::new(url.loc.as_loc(), url.scheme.as_ref()) }
|
||||
fn from(value: &'a UrlBuf) -> Self { Self(value.as_url()) }
|
||||
}
|
||||
|
||||
impl<'a> Encode<'a> {
|
||||
#[inline]
|
||||
pub(super) fn new(loc: Loc<'a>, scheme: SchemeRef<'a>) -> Self { Self { loc, scheme } }
|
||||
|
||||
#[inline]
|
||||
pub fn domain<'s>(s: &'s str) -> PercentEncode<'s> {
|
||||
const SET: &AsciiSet = &CONTROLS.add(b'/').add(b':');
|
||||
percent_encode(s.as_bytes(), SET)
|
||||
}
|
||||
|
||||
fn ports(&self) -> impl Display {
|
||||
struct D<'a>(&'a Encode<'a>);
|
||||
fn ports(self) -> impl Display {
|
||||
struct D<'a>(Encode<'a>);
|
||||
|
||||
impl Display for D<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
macro_rules! w {
|
||||
($default_uri:expr, $default_urn:expr) => {{
|
||||
let uri = self.0.loc.uri().count();
|
||||
let urn = self.0.loc.urn().count();
|
||||
let uri = self.0.0.loc.uri().count();
|
||||
let urn = self.0.0.loc.urn().count();
|
||||
match (uri != $default_uri, urn != $default_urn) {
|
||||
(true, true) => write!(f, ":{uri}:{urn}"),
|
||||
(true, false) => write!(f, ":{uri}"),
|
||||
|
|
@ -45,12 +40,12 @@ impl<'a> Encode<'a> {
|
|||
}};
|
||||
}
|
||||
|
||||
match self.0.scheme {
|
||||
match self.0.0.scheme {
|
||||
SchemeRef::Regular => Ok(()),
|
||||
SchemeRef::Search(_) | SchemeRef::Archive(_) => w!(0, 0),
|
||||
SchemeRef::Sftp(_) => w!(
|
||||
self.0.loc.as_os_str().is_empty().not() as usize,
|
||||
self.0.loc.file_name().is_some() as usize
|
||||
self.0.0.loc.as_os_str().is_empty().not() as usize,
|
||||
self.0.0.loc.file_name().is_some() as usize
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +58,7 @@ impl<'a> Encode<'a> {
|
|||
impl Display for Encode<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use SchemeRef as S;
|
||||
match self.scheme {
|
||||
match self.0.scheme {
|
||||
S::Regular => write!(f, "regular://"),
|
||||
S::Search(d) => write!(f, "search://{}{}/", Self::domain(d), self.ports()),
|
||||
S::Archive(d) => write!(f, "archive://{}{}/", Self::domain(d), self.ports()),
|
||||
|
|
@ -73,17 +68,19 @@ impl Display for Encode<'_> {
|
|||
}
|
||||
|
||||
// --- Tilded
|
||||
pub struct EncodeTilded<'a> {
|
||||
loc: Loc<'a>,
|
||||
scheme: SchemeRef<'a>,
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct EncodeTilded<'a>(Url<'a>);
|
||||
|
||||
impl<'a> From<Url<'a>> for EncodeTilded<'a> {
|
||||
fn from(value: Url<'a>) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UrlBuf> for EncodeTilded<'a> {
|
||||
fn from(url: &'a UrlBuf) -> Self { Self { loc: url.loc.as_loc(), scheme: url.scheme.as_ref() } }
|
||||
fn from(value: &'a UrlBuf) -> Self { Self(value.as_url()) }
|
||||
}
|
||||
|
||||
impl<'a> From<&EncodeTilded<'a>> for Encode<'a> {
|
||||
fn from(value: &EncodeTilded<'a>) -> Self { Self::new(value.loc, value.scheme) }
|
||||
impl<'a> From<EncodeTilded<'a>> for Encode<'a> {
|
||||
fn from(value: EncodeTilded<'a>) -> Self { Self(value.0) }
|
||||
}
|
||||
|
||||
impl Display for EncodeTilded<'_> {
|
||||
|
|
@ -91,14 +88,14 @@ impl Display for EncodeTilded<'_> {
|
|||
use Encode as E;
|
||||
use SchemeRef as S;
|
||||
|
||||
let loc = percent_encode(self.loc.as_os_str().as_encoded_bytes(), CONTROLS);
|
||||
match self.scheme {
|
||||
let loc = percent_encode(self.0.loc.as_os_str().as_encoded_bytes(), CONTROLS);
|
||||
match self.0.scheme {
|
||||
S::Regular => write!(f, "regular~://{loc}"),
|
||||
S::Search(d) => write!(f, "search~://{}{}/{loc}", E::domain(d), E::ports(&self.into())),
|
||||
S::Search(d) => write!(f, "search~://{}{}/{loc}", E::domain(d), E::ports((*self).into())),
|
||||
S::Archive(d) => {
|
||||
write!(f, "archive~://{}{}/{loc}", E::domain(d), E::ports(&self.into()))
|
||||
write!(f, "archive~://{}{}/{loc}", E::domain(d), E::ports((*self).into()))
|
||||
}
|
||||
S::Sftp(d) => write!(f, "sftp~://{}{}/{loc}", E::domain(d), E::ports(&self.into())),
|
||||
S::Sftp(d) => write!(f, "sftp~://{}{}/{loc}", E::domain(d), E::ports((*self).into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use crate::url::{Url, UrlBuf, UrlCow};
|
||||
use std::{borrow::Cow, ffi::OsStr, path::Path};
|
||||
|
||||
use crate::url::{Components, Display, Uri, Url, UrlBuf, UrlCow, Urn};
|
||||
|
||||
// --- AsUrl
|
||||
pub trait AsUrl {
|
||||
fn as_url(&self) -> Url<'_>;
|
||||
}
|
||||
|
|
@ -11,7 +14,7 @@ impl AsUrl for Url<'_> {
|
|||
|
||||
impl AsUrl for UrlBuf {
|
||||
#[inline]
|
||||
fn as_url(&self) -> Url<'_> { self.as_url() }
|
||||
fn as_url(&self) -> Url<'_> { Url { loc: self.loc.as_loc(), scheme: self.scheme.as_ref() } }
|
||||
}
|
||||
|
||||
impl AsUrl for &UrlBuf {
|
||||
|
|
@ -26,7 +29,12 @@ impl AsUrl for &mut UrlBuf {
|
|||
|
||||
impl AsUrl for UrlCow<'_> {
|
||||
#[inline]
|
||||
fn as_url(&self) -> Url<'_> { self.as_url() }
|
||||
fn as_url(&self) -> Url<'_> {
|
||||
match self {
|
||||
UrlCow::Borrowed { loc, scheme } => Url { loc: *loc, scheme: scheme.as_ref() },
|
||||
UrlCow::Owned { loc, scheme } => Url { loc: loc.as_loc(), scheme: scheme.as_ref() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsUrl for &UrlCow<'_> {
|
||||
|
|
@ -41,3 +49,51 @@ impl<'a, T: AsUrl> From<&'a T> for Url<'a> {
|
|||
impl<'a, T: AsUrl> From<&'a mut T> for Url<'a> {
|
||||
fn from(value: &'a mut T) -> Self { value.as_url() }
|
||||
}
|
||||
|
||||
// UrlLike
|
||||
pub trait UrlLike
|
||||
where
|
||||
Self: AsUrl + Sized,
|
||||
{
|
||||
fn as_path(&self) -> Option<&Path> { self.as_url().as_path() }
|
||||
|
||||
fn base(&self) -> Option<Url<'_>> { self.as_url().base() }
|
||||
|
||||
fn components(&self) -> Components<'_> { self.as_url().into() }
|
||||
|
||||
fn covariant(&self, other: impl AsUrl) -> bool { self.as_url().covariant(other) }
|
||||
|
||||
fn display(&self) -> Display<'_> { self.as_url().into() }
|
||||
|
||||
fn ends_with(&self, child: impl AsUrl) -> bool { self.as_url().ends_with(child) }
|
||||
|
||||
fn ext(&self) -> Option<&OsStr> { self.as_url().ext() }
|
||||
|
||||
fn has_root(&self) -> bool { self.as_url().has_root() }
|
||||
|
||||
fn has_trail(&self) -> bool { self.as_url().has_trail() }
|
||||
|
||||
fn is_absolute(&self) -> bool { self.as_url().is_absolute() }
|
||||
|
||||
fn join(&self, path: impl AsRef<Path>) -> UrlBuf { self.as_url().join(path) }
|
||||
|
||||
fn name(&self) -> Option<&OsStr> { self.as_url().name() }
|
||||
|
||||
fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
||||
|
||||
fn pair(&self) -> Option<(Url<'_>, &Urn)> { self.as_url().pair() }
|
||||
|
||||
fn parent(&self) -> Option<Url<'_>> { self.as_url().parent() }
|
||||
|
||||
fn starts_with(&self, base: impl AsUrl) -> bool { self.as_url().starts_with(base) }
|
||||
|
||||
fn stem(&self) -> Option<&OsStr> { self.as_url().stem() }
|
||||
|
||||
fn strip_prefix(&self, base: impl AsUrl) -> Option<&Urn> { self.as_url().strip_prefix(base) }
|
||||
|
||||
fn uri(&self) -> &Uri { self.as_url().uri() }
|
||||
|
||||
fn urn(&self) -> &Urn { self.as_url().urn() }
|
||||
}
|
||||
|
||||
impl<T: AsUrl> UrlLike for T {}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,39 @@ impl<'a> Url<'a> {
|
|||
UrlBuf { loc, scheme: self.scheme.into() }
|
||||
}
|
||||
|
||||
pub fn strip_prefix(self, base: impl AsUrl) -> Option<&'a Urn> {
|
||||
use SchemeRef as S;
|
||||
|
||||
let base = base.as_url();
|
||||
let prefix = self.loc.strip_prefix(base.loc)?;
|
||||
|
||||
Some(Urn::new(match (self.scheme, base.scheme) {
|
||||
// Same scheme
|
||||
(S::Regular, S::Regular) => Some(prefix),
|
||||
(S::Search(_), S::Search(_)) => Some(prefix),
|
||||
(S::Archive(a), S::Archive(b)) => Some(prefix).filter(|_| a == b),
|
||||
(S::Sftp(a), S::Sftp(b)) => Some(prefix).filter(|_| a == b),
|
||||
|
||||
// Both are local files
|
||||
(S::Regular, S::Search(_)) => Some(prefix),
|
||||
(S::Search(_), S::Regular) => Some(prefix),
|
||||
|
||||
// Only the entry of archives is a local file
|
||||
(S::Regular, S::Archive(_)) => Some(prefix).filter(|_| base.uri().is_empty()),
|
||||
(S::Search(_), S::Archive(_)) => Some(prefix).filter(|_| base.uri().is_empty()),
|
||||
(S::Archive(_), S::Regular) => Some(prefix).filter(|_| self.uri().is_empty()),
|
||||
(S::Archive(_), S::Search(_)) => Some(prefix).filter(|_| self.uri().is_empty()),
|
||||
|
||||
// Independent virtual file space
|
||||
(S::Regular, S::Sftp(_)) => None,
|
||||
(S::Search(_), S::Sftp(_)) => None,
|
||||
(S::Archive(_), S::Sftp(_)) => None,
|
||||
(S::Sftp(_), S::Regular) => None,
|
||||
(S::Sftp(_), S::Search(_)) => None,
|
||||
(S::Sftp(_), S::Archive(_)) => None,
|
||||
}?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn uri(self) -> &'a Uri { self.loc.uri() }
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{ffi::OsString, io};
|
|||
use tokio::{select, sync::{mpsc, oneshot}};
|
||||
use yazi_fs::cha::Cha;
|
||||
use yazi_macro::ok_or_not_found;
|
||||
use yazi_shared::url::{AsUrl, UrlBuf};
|
||||
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
||||
|
||||
use crate::provider;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use yazi_fs::FilesOp;
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
|
||||
use crate::maybe_exists;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, io, sync::Arc};
|
||||
|
||||
use yazi_fs::{cha::{Cha, ChaType}, provider::FileHolder};
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
|
||||
pub enum DirEntry {
|
||||
Regular(yazi_fs::provider::local::DirEntry),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use hashbrown::HashSet;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::error;
|
||||
use yazi_shared::url::{AsUrl, Url, UrlBuf};
|
||||
use yazi_shared::url::{AsUrl, Url, UrlBuf, UrlLike};
|
||||
|
||||
use crate::{LINKED, WATCHED, backend};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::path::Path;
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use yazi_shared::url::{AsUrl, UrlBuf};
|
||||
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Watched(HashSet<UrlBuf>);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use hashbrown::HashSet;
|
|||
use tokio::{pin, sync::{mpsc::{self, UnboundedReceiver}, watch}};
|
||||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||
use yazi_fs::{File, FilesOp, provider::local};
|
||||
use yazi_shared::url::UrlBuf;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
use crate::{LINKED, Linked, WATCHED, WATCHER, backend::Backend};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue