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]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.20"
|
version = "0.6.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
|
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
|
|
@ -411,9 +411,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.23.2"
|
version = "1.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
|
|
@ -459,9 +459,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.39"
|
version = "1.2.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f"
|
checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"jobserver",
|
"jobserver",
|
||||||
|
|
@ -1243,9 +1243,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
|
checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
|
|
@ -1874,11 +1874,10 @@ checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.13"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2442,9 +2441,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.4"
|
version = "0.12.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
|
|
@ -2452,15 +2451,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.11"
|
version = "0.9.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.52.6",
|
"windows-link 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ lru = "0.16.1"
|
||||||
mlua = { version = "0.11.4", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
|
mlua = { version = "0.11.4", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
|
||||||
objc = "0.2.7"
|
objc = "0.2.7"
|
||||||
ordered-float = { version = "5.1.0", features = [ "serde" ] }
|
ordered-float = { version = "5.1.0", features = [ "serde" ] }
|
||||||
parking_lot = "0.12.4"
|
parking_lot = "0.12.5"
|
||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info", "unstable-widget-ref" ] }
|
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info", "unstable-widget-ref" ] }
|
||||||
regex = "1.11.3"
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use yazi_shared::url::Urn;
|
use yazi_shared::url::{UrlLike, Urn};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Valu
|
||||||
use yazi_binding::Style;
|
use yazi_binding::Style;
|
||||||
use yazi_config::THEME;
|
use yazi_config::THEME;
|
||||||
use yazi_plugin::bindings::Range;
|
use yazi_plugin::bindings::Range;
|
||||||
|
use yazi_shared::url::UrlLike;
|
||||||
|
|
||||||
use super::Lives;
|
use super::Lives;
|
||||||
use crate::lives::PtrCell;
|
use crate::lives::PtrCell;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use std::ops::Deref;
|
||||||
|
|
||||||
use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods, Value};
|
use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods, Value};
|
||||||
use yazi_binding::{Id, UrlRef, cached_field};
|
use yazi_binding::{Id, UrlRef, cached_field};
|
||||||
|
use yazi_shared::url::UrlLike;
|
||||||
|
|
||||||
use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected};
|
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 anyhow::{Result, anyhow};
|
||||||
use crossterm::{execute, style::Print};
|
use crossterm::{execute, style::Print};
|
||||||
|
|
@ -7,11 +7,11 @@ use scopeguard::defer;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use yazi_config::{YAZI, opener::OpenerRule};
|
use yazi_config::{YAZI, opener::OpenerRule};
|
||||||
use yazi_dds::Pubsub;
|
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_macro::{err, succ};
|
||||||
use yazi_parser::VoidOpt;
|
use yazi_parser::VoidOpt;
|
||||||
use yazi_proxy::{AppProxy, HIDER, TasksProxy};
|
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_term::tty::TTY;
|
||||||
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
||||||
use yazi_watcher::WATCHER;
|
use yazi_watcher::WATCHER;
|
||||||
|
|
@ -51,10 +51,13 @@ impl Actor for BulkRename {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
defer! { tokio::spawn(Local.remove_file(tmp.clone())); }
|
defer! { tokio::spawn(Local.remove_file(tmp.clone())); }
|
||||||
TasksProxy::process_exec(Cow::Borrowed(opener), cwd, vec![
|
TasksProxy::process_exec(
|
||||||
OsStr::new("").into(),
|
cwd,
|
||||||
tmp.as_os_str().to_owned().into(),
|
Splatter::new(&[UrlCow::default(), Url::regular(&tmp).into()]).splat(&opener.run),
|
||||||
])
|
vec![UrlCow::default(), UrlBuf::from(&tmp).into()],
|
||||||
|
opener.block,
|
||||||
|
opener.orphan,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let _permit = HIDER.acquire().await.unwrap();
|
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_macro::{act, err, render, succ};
|
||||||
use yazi_parser::mgr::CdOpt;
|
use yazi_parser::mgr::CdOpt;
|
||||||
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy};
|
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 yazi_vfs::VfsFile;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::ffi::OsString;
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use yazi_macro::{act, succ};
|
use yazi_macro::{act, succ};
|
||||||
use yazi_parser::mgr::CopyOpt;
|
use yazi_parser::mgr::CopyOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
use yazi_widgets::CLIPBOARD;
|
use yazi_widgets::CLIPBOARD;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use yazi_fs::{File, FilesOp};
|
||||||
use yazi_macro::{ok_or_not_found, succ};
|
use yazi_macro::{ok_or_not_found, succ};
|
||||||
use yazi_parser::mgr::CreateOpt;
|
use yazi_parser::mgr::CreateOpt;
|
||||||
use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy};
|
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_vfs::{VfsFile, maybe_exists, provider};
|
||||||
use yazi_watcher::WATCHER;
|
use yazi_watcher::WATCHER;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use yazi_fs::path::clean_url;
|
use yazi_fs::path::clean_url;
|
||||||
use yazi_macro::{act, succ};
|
use yazi_macro::{act, succ};
|
||||||
use yazi_parser::VoidOpt;
|
use yazi_parser::VoidOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use yazi_dds::Pubsub;
|
use yazi_dds::Pubsub;
|
||||||
use yazi_macro::{err, render, succ, tab};
|
use yazi_macro::{err, render, succ, tab};
|
||||||
use yazi_parser::mgr::HoverOpt;
|
use yazi_parser::mgr::HoverOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use yazi_macro::{act, succ};
|
use yazi_macro::{act, succ};
|
||||||
use yazi_parser::{VoidOpt, mgr::CdSource};
|
use yazi_parser::{VoidOpt, mgr::CdSource};
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use hashbrown::HashMap;
|
||||||
use yazi_config::{YAZI, popup::PickCfg};
|
use yazi_config::{YAZI, popup::PickCfg};
|
||||||
|
use yazi_fs::Splatter;
|
||||||
use yazi_macro::succ;
|
use yazi_macro::succ;
|
||||||
use yazi_parser::mgr::OpenDoOpt;
|
use yazi_parser::{mgr::OpenDoOpt, tasks::ProcessOpenOpt};
|
||||||
use yazi_proxy::{PickProxy, TasksProxy};
|
use yazi_proxy::{PickProxy, TasksProxy};
|
||||||
use yazi_shared::{data::Data, url::UrlCow};
|
use yazi_shared::{data::Data, url::{UrlBuf, UrlCow}};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
@ -30,21 +30,53 @@ impl Actor for OpenDo {
|
||||||
if targets.is_empty() {
|
if targets.is_empty() {
|
||||||
succ!();
|
succ!();
|
||||||
} else if !opt.interactive {
|
} 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() {
|
if openers.is_empty() {
|
||||||
succ!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
||||||
let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect()));
|
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 {
|
tokio::spawn(async move {
|
||||||
if let Ok(choice) = pick.await {
|
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!();
|
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_macro::{emit, succ};
|
||||||
use yazi_parser::mgr::QuitOpt;
|
use yazi_parser::mgr::QuitOpt;
|
||||||
use yazi_proxy::ConfirmProxy;
|
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};
|
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_macro::{act, err, ok_or_not_found, succ};
|
||||||
use yazi_parser::mgr::RenameOpt;
|
use yazi_parser::mgr::RenameOpt;
|
||||||
use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy};
|
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_vfs::{VfsFile, maybe_exists, provider};
|
||||||
use yazi_watcher::WATCHER;
|
use yazi_watcher::WATCHER;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use yazi_fs::{File, FilesOp};
|
use yazi_fs::{File, FilesOp};
|
||||||
use yazi_macro::{act, render, succ};
|
use yazi_macro::{act, render, succ};
|
||||||
use yazi_parser::mgr::RevealOpt;
|
use yazi_parser::mgr::RevealOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use anyhow::Result;
|
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_macro::{act, succ};
|
||||||
use yazi_parser::mgr::ShellOpt;
|
use yazi_parser::{mgr::ShellOpt, tasks::ProcessOpenOpt};
|
||||||
use yazi_proxy::{InputProxy, TasksProxy};
|
use yazi_proxy::{InputProxy, TasksProxy};
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
|
|
@ -20,7 +21,7 @@ impl Actor for Shell {
|
||||||
act!(mgr:escape_visual, cx)?;
|
act!(mgr:escape_visual, cx)?;
|
||||||
|
|
||||||
let cwd = opt.cwd.take().unwrap_or(cx.cwd().into()).into_owned();
|
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(|| {
|
let input = opt.interactive.then(|| {
|
||||||
InputProxy::show(InputCfg::shell(opt.block).with_value(&*opt.run).with_cursor(opt.cursor))
|
InputProxy::show(InputCfg::shell(opt.block).with_value(&*opt.run).with_cursor(opt.cursor))
|
||||||
|
|
@ -37,18 +38,15 @@ impl Actor for Shell {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TasksProxy::file_open(
|
TasksProxy::open_shell_compat(ProcessOpenOpt {
|
||||||
Cow::Owned(OpenerRule {
|
|
||||||
run: opt.run.into_owned(),
|
|
||||||
block: opt.block,
|
|
||||||
orphan: opt.orphan,
|
|
||||||
desc: Default::default(),
|
|
||||||
r#for: None,
|
|
||||||
spread: true,
|
|
||||||
}),
|
|
||||||
cwd,
|
cwd,
|
||||||
selected,
|
cmd: Splatter::new(&selected).splat(opt.run.as_ref()),
|
||||||
);
|
args: selected,
|
||||||
|
block: opt.block,
|
||||||
|
orphan: opt.orphan,
|
||||||
|
done: None,
|
||||||
|
spread: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
succ!();
|
succ!();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use yazi_core::tab::Folder;
|
||||||
use yazi_fs::FilesOp;
|
use yazi_fs::FilesOp;
|
||||||
use yazi_macro::{act, render, succ};
|
use yazi_macro::{act, render, succ};
|
||||||
use yazi_parser::mgr::UpdateFilesOpt;
|
use yazi_parser::mgr::UpdateFilesOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
use yazi_watcher::LINKED;
|
use yazi_watcher::LINKED;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use yazi_macro::{act, render, succ};
|
use yazi_macro::{act, render, succ};
|
||||||
use yazi_parser::mgr::UpdateMimesOpt;
|
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 yazi_watcher::LINKED;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
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 std::ops::Deref;
|
||||||
|
|
||||||
use mlua::{AnyUserData, ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
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};
|
use crate::{Urn, cached_field, deprecate};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use futures::executor::block_on;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use yazi_fs::{CWD, Xdg, path::expand_url};
|
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;
|
use yazi_vfs::provider;
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
|
|
||||||
|
|
@ -30,40 +30,37 @@ ueberzug_offset = [ 0, 0, 0, 0 ]
|
||||||
|
|
||||||
[opener]
|
[opener]
|
||||||
edit = [
|
edit = [
|
||||||
{ run = '${EDITOR:-vi} "$@"', desc = "$EDITOR", block = true, for = "unix" },
|
{ run = "${EDITOR:-vi} %s", desc = "$EDITOR", for = "unix", block = true },
|
||||||
{ run = 'code %*', orphan = true, desc = "code", for = "windows" },
|
{ run = "code %s", desc = "code", for = "windows", orphan = true },
|
||||||
{ run = 'code -w %*', block = true, desc = "code (block)", for = "windows" },
|
{ run = "code -w %s", desc = "code (block)", for = "windows", block = true },
|
||||||
]
|
]
|
||||||
play = [
|
play = [
|
||||||
{ run = 'xdg-open "$1"', desc = "Play", for = "linux" },
|
{ run = "xdg-open %s1", desc = "Play", for = "linux" },
|
||||||
{ run = 'open "$@"', desc = "Play", for = "macos" },
|
{ run = "open %s", desc = "Play", for = "macos" },
|
||||||
{ run = 'start "" "%1"', orphan = true, desc = "Play", for = "windows" },
|
{ run = 'start "" %s1', orphan = true, desc = "Play", for = "windows" },
|
||||||
{ run = 'termux-open "$1"', desc = "Play", for = "android" },
|
{ run = "termux-open %s1", desc = "Play", for = "android" },
|
||||||
{ run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" },
|
{ run = "mediainfo %s1; 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 = "mediainfo %s1 & pause", block = true, desc = "Show media info", for = "windows" },
|
||||||
]
|
]
|
||||||
open = [
|
open = [
|
||||||
{ run = 'xdg-open "$1"', desc = "Open", for = "linux" },
|
{ run = "xdg-open %s1", desc = "Open", for = "linux" },
|
||||||
{ run = 'open "$@"', desc = "Open", for = "macos" },
|
{ run = "open %s", desc = "Open", for = "macos" },
|
||||||
{ run = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" },
|
{ run = 'start "" %s1', desc = "Open", for = "windows", orphan = true },
|
||||||
{ run = 'termux-open "$1"', desc = "Open", for = "android" },
|
{ run = "termux-open %s1", desc = "Open", for = "android" },
|
||||||
]
|
]
|
||||||
reveal = [
|
reveal = [
|
||||||
{ run = 'xdg-open "$(dirname "$1")"', desc = "Reveal", for = "linux" },
|
{ run = "xdg-open %d1", desc = "Reveal", for = "linux" },
|
||||||
{ run = 'open -R "$1"', desc = "Reveal", for = "macos" },
|
{ run = "open -R %s1", desc = "Reveal", for = "macos" },
|
||||||
{ run = 'explorer /select,"%1"', orphan = true, desc = "Reveal", for = "windows" },
|
{ run = "explorer /select,%s1", desc = "Reveal", for = "windows", orphan = true },
|
||||||
{ run = 'termux-open "$(dirname "$1")"', desc = "Reveal", for = "android" },
|
{ run = "termux-open %d1", desc = "Reveal", for = "android" },
|
||||||
{ run = '''clear; exiftool "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show EXIF", for = "unix" },
|
{ run = "clear; exiftool %s1; echo 'Press enter to exit'; read _", desc = "Show EXIF", for = "unix", block = true },
|
||||||
]
|
]
|
||||||
extract = [
|
extract = [
|
||||||
{ run = 'ya pub extract --list "$@"', desc = "Extract here", for = "unix" },
|
{ run = "ya pub extract --list %s", desc = "Extract here" },
|
||||||
{ run = 'ya pub extract --list %*', desc = "Extract here", for = "windows" },
|
|
||||||
]
|
]
|
||||||
download = [
|
download = [
|
||||||
{ run = 'ya emit download --open "$@"', desc = "Download and open", for = "unix" },
|
{ run = "ya emit download --open %S", desc = "Download and open" },
|
||||||
{ run = 'ya emit download --open %*', desc = "Download and open", for = "windows" },
|
{ run = "ya emit download %S", desc = "Download" },
|
||||||
{ run = 'ya emit download "$@"', desc = "Download", for = "unix" },
|
|
||||||
{ run = 'ya emit download %*', desc = "Download", for = "windows" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[open]
|
[open]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::{Result, bail};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::DeserializeOver2;
|
||||||
use yazi_fs::{CWD, SortBy};
|
use yazi_fs::{CWD, SortBy};
|
||||||
use yazi_shared::{SyncCell, url::UrlBuf};
|
use yazi_shared::{SyncCell, url::{UrlBuf, UrlLike}};
|
||||||
|
|
||||||
use super::{MgrRatio, MouseEvents};
|
use super::{MgrRatio, MouseEvents};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,21 +18,21 @@ impl Deref for Opener {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
where
|
||||||
I: Iterator<Item = &'a str>,
|
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
|
where
|
||||||
I: Iterator<Item = &'b str>,
|
I: Iterator<Item = &'a str>,
|
||||||
{
|
{
|
||||||
uses.flat_map(|use_| self.get(use_)).flatten().next()
|
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
|
where
|
||||||
I: Iterator<Item = &'a str>,
|
I: Iterator<Item = &'a str>,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use yazi_fs::Splatter;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct OpenerRule {
|
pub struct OpenerRule {
|
||||||
|
|
@ -35,11 +36,12 @@ impl OpenerRule {
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
self.spread = self.run.contains("$@") || self.run.contains("$*");
|
self.spread =
|
||||||
|
Splatter::<()>::spread(&self.run) || self.run.contains("$@") || self.run.contains("$*");
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
self.spread = self.run.contains("%*");
|
self.spread = Splatter::<()>::spread(&self.run) || self.run.contains("%*");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use yazi_codegen::DeserializeOver2;
|
use yazi_codegen::DeserializeOver2;
|
||||||
use yazi_fs::File;
|
use yazi_fs::File;
|
||||||
use yazi_shared::Condition;
|
use yazi_shared::{Condition, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Color, Icon as I, Pattern, Style};
|
use crate::{Color, Icon as I, Pattern, Style};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use yazi_adapter::Dimension;
|
use yazi_adapter::Dimension;
|
||||||
use yazi_config::popup::{Origin, Position};
|
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 yazi_watcher::Watcher;
|
||||||
|
|
||||||
use super::{Mimetype, Tabs, Yanked};
|
use super::{Mimetype, Tabs, Yanked};
|
||||||
|
|
@ -53,3 +56,29 @@ impl Mgr {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent_mut(&mut self) -> Option<&mut Folder> { self.active_mut().parent.as_mut() }
|
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_dds::Pubsub;
|
||||||
use yazi_fs::FilesOp;
|
use yazi_fs::FilesOp;
|
||||||
use yazi_macro::err;
|
use yazi_macro::err;
|
||||||
use yazi_shared::url::{Url, UrlBuf, UrlBufCov, UrlCov};
|
use yazi_shared::url::{Url, UrlBuf, UrlBufCov, UrlCov, UrlLike};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Yanked {
|
pub struct Yanked {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use yazi_shared::url::{UrlBuf, UrlBufCov};
|
use yazi_shared::url::{UrlBuf, UrlBufCov, UrlLike};
|
||||||
|
|
||||||
use super::Tasks;
|
use super::Tasks;
|
||||||
use crate::mgr::Yanked;
|
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_parser::tasks::ProcessOpenOpt;
|
||||||
use yazi_shared::url::{UrlBuf, UrlCow};
|
|
||||||
|
|
||||||
use super::Tasks;
|
use super::Tasks;
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
pub fn process_with_selected(&self, cwd: UrlBuf, targets: Vec<(UrlCow<'static>, &str)>) {
|
// TODO: remove
|
||||||
let mut openers = HashMap::new();
|
pub fn open_shell_compat(&self, mut opt: ProcessOpenOpt) {
|
||||||
for (url, mime) in targets {
|
if opt.spread {
|
||||||
if let Some(opener) = YAZI.opener.first(YAZI.open.all(&url, mime)) {
|
self.scheduler.process_open(opt);
|
||||||
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 });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if args.is_empty() {
|
if opt.args.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if args.len() == 2 {
|
if opt.args.len() == 2 {
|
||||||
self.scheduler.process_open(ProcessOpenOpt { cwd, opener, args, done: None });
|
self.scheduler.process_open(opt);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let hovered = mem::take(&mut args[0]);
|
let hovered = mem::take(&mut opt.args[0]);
|
||||||
for target in args.into_iter().skip(1) {
|
for target in opt.args.into_iter().skip(1) {
|
||||||
self.scheduler.process_open(ProcessOpenOpt {
|
self.scheduler.process_open(ProcessOpenOpt {
|
||||||
cwd: cwd.clone(),
|
cwd: opt.cwd.clone(),
|
||||||
opener: opener.clone(),
|
cmd: opt.cmd.clone(),
|
||||||
args: vec![hovered.clone(), target],
|
args: vec![hovered.clone(), target],
|
||||||
|
block: opt.block,
|
||||||
|
orphan: opt.orphan,
|
||||||
done: None,
|
done: None,
|
||||||
|
spread: opt.spread,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ pub enum Spark<'a> {
|
||||||
Linemode(yazi_parser::mgr::LinemodeOpt),
|
Linemode(yazi_parser::mgr::LinemodeOpt),
|
||||||
Link(yazi_parser::mgr::LinkOpt),
|
Link(yazi_parser::mgr::LinkOpt),
|
||||||
Open(yazi_parser::mgr::OpenOpt),
|
Open(yazi_parser::mgr::OpenOpt),
|
||||||
OpenWith(yazi_parser::mgr::OpenWithOpt),
|
|
||||||
OpenDo(yazi_parser::mgr::OpenDoOpt),
|
OpenDo(yazi_parser::mgr::OpenDoOpt),
|
||||||
Paste(yazi_parser::mgr::PasteOpt),
|
Paste(yazi_parser::mgr::PasteOpt),
|
||||||
Peek(yazi_parser::mgr::PeekOpt),
|
Peek(yazi_parser::mgr::PeekOpt),
|
||||||
|
|
@ -160,7 +159,6 @@ impl<'a> IntoLua for Spark<'a> {
|
||||||
Self::Linemode(b) => b.into_lua(lua),
|
Self::Linemode(b) => b.into_lua(lua),
|
||||||
Self::Link(b) => b.into_lua(lua),
|
Self::Link(b) => b.into_lua(lua),
|
||||||
Self::Open(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::OpenDo(b) => b.into_lua(lua),
|
||||||
Self::Paste(b) => b.into_lua(lua),
|
Self::Paste(b) => b.into_lua(lua),
|
||||||
Self::Peek(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::LinkOpt, mgr:link);
|
||||||
try_from_spark!(mgr::OpenDoOpt, mgr:open_do);
|
try_from_spark!(mgr::OpenDoOpt, mgr:open_do);
|
||||||
try_from_spark!(mgr::OpenOpt, mgr:open);
|
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::PasteOpt, mgr:paste);
|
||||||
try_from_spark!(mgr::PeekOpt, mgr:peek);
|
try_from_spark!(mgr::PeekOpt, mgr:peek);
|
||||||
try_from_spark!(mgr::QuitOpt, mgr:quit);
|
try_from_spark!(mgr::QuitOpt, mgr:quit);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use yazi_actor::Ctx;
|
||||||
use yazi_boot::BOOT;
|
use yazi_boot::BOOT;
|
||||||
use yazi_macro::act;
|
use yazi_macro::act;
|
||||||
use yazi_parser::{VoidOpt, mgr::CdSource};
|
use yazi_parser::{VoidOpt, mgr::CdSource};
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::ffi::OsString;
|
||||||
|
|
||||||
use yazi_boot::ARGS;
|
use yazi_boot::ARGS;
|
||||||
use yazi_fs::provider::{Provider, local::Local};
|
use yazi_fs::provider::{Provider, local::Local};
|
||||||
use yazi_shared::event::EventQuit;
|
use yazi_shared::{event::EventQuit, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Term, app::App};
|
use crate::{Term, app::App};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -164,8 +164,8 @@ impl<'a> Executor<'a> {
|
||||||
on!(arrow);
|
on!(arrow);
|
||||||
on!(inspect);
|
on!(inspect);
|
||||||
on!(cancel);
|
on!(cancel);
|
||||||
on!(file_open);
|
|
||||||
on!(process_open);
|
on!(process_open);
|
||||||
|
on!(open_shell_compat);
|
||||||
|
|
||||||
match cmd.name.as_ref() {
|
match cmd.name.as_ref() {
|
||||||
// Help
|
// Help
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{env::{current_dir, set_current_dir}, ops::Deref, path::PathBuf, sync::{Arc, atomic::{AtomicBool, Ordering}}};
|
use std::{env::{current_dir, set_current_dir}, ops::Deref, path::PathBuf, sync::{Arc, atomic::{AtomicBool, Ordering}}};
|
||||||
|
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
use yazi_shared::{RoCell, url::UrlBuf};
|
use yazi_shared::{RoCell, url::{UrlBuf, UrlLike}};
|
||||||
|
|
||||||
use crate::FsUrl;
|
use crate::FsUrl;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{ffi::OsStr, hash::{BuildHasher, Hash, Hasher}, ops::Deref, path::{Path, PathBuf}};
|
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};
|
use crate::cha::{Cha, ChaType};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use tokio::io;
|
use tokio::io;
|
||||||
use yazi_shared::url::{Component, UrlBuf};
|
use yazi_shared::url::{Component, UrlBuf, UrlLike};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ok_or_not_found<T: Default>(result: io::Result<T>) -> io::Result<T> {
|
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_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() {
|
pub fn init() {
|
||||||
CWD.init(<_>::default());
|
CWD.init(<_>::default());
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use yazi_macro::relay;
|
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 super::File;
|
||||||
use crate::{cha::Cha, error::Error};
|
use crate::{cha::Cha, error::Error};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{borrow::Cow, ffi::{OsStr, OsString}, path::{Path, PathBuf}};
|
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};
|
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");
|
.expect("Failed to create Loc from drive letter");
|
||||||
UrlBuf { loc, scheme: url.scheme.into() }.into()
|
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()
|
&& let Some(home) = dirs::home_dir()
|
||||||
&& home.is_absolute()
|
&& home.is_absolute()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{borrow::Cow, ffi::OsStr};
|
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> {
|
pub fn skip_url(url: &UrlBuf, n: usize) -> Cow<'_, OsStr> {
|
||||||
let mut it = url.components();
|
let mut it = url.components();
|
||||||
|
|
@ -36,7 +36,7 @@ pub fn backslash_to_slash(p: &std::path::Path) -> Cow<'_, std::path::Path> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use yazi_shared::url::UrlCow;
|
use yazi_shared::url::{AsUrl, UrlCow};
|
||||||
|
|
||||||
use crate::path::url_relative_to;
|
use crate::path::url_relative_to;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{borrow::Cow, path::{Path, PathBuf}};
|
use std::{borrow::Cow, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
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>(
|
pub fn path_relative_to<'a>(
|
||||||
from: impl AsRef<Path>,
|
from: impl AsRef<Path>,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
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};
|
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 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;
|
use crate::Xdg;
|
||||||
|
|
||||||
pub trait FsUrl {
|
pub trait FsUrl<'a> {
|
||||||
fn cache(&self) -> Option<PathBuf>;
|
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> {
|
fn cache(&self) -> Option<PathBuf> {
|
||||||
match self.scheme {
|
match self.scheme {
|
||||||
SchemeRef::Regular | SchemeRef::Search(_) => None,
|
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 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 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;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::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
|
link
|
||||||
open
|
open
|
||||||
open_do
|
open_do
|
||||||
open_with
|
|
||||||
paste
|
paste
|
||||||
peek
|
peek
|
||||||
quit
|
quit
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::bail;
|
||||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
||||||
|
|
||||||
|
|
@ -8,8 +9,16 @@ pub struct OpenDoOpt {
|
||||||
pub interactive: bool,
|
pub interactive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CmdCow> for OpenDoOpt {
|
impl TryFrom<CmdCow> for OpenDoOpt {
|
||||||
fn from(mut c: CmdCow) -> Self { c.take_any("option").unwrap_or_default() }
|
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 {
|
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 anyhow::anyhow;
|
||||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use yazi_config::opener::OpenerRule;
|
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
||||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
|
||||||
|
|
||||||
// --- Exec
|
// --- Exec
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProcessOpenOpt {
|
pub struct ProcessOpenOpt {
|
||||||
pub cwd: UrlBuf,
|
pub cwd: UrlBuf,
|
||||||
pub opener: Cow<'static, OpenerRule>,
|
pub cmd: OsString,
|
||||||
pub args: Vec<Cow<'static, OsStr>>,
|
pub args: Vec<UrlCow<'static>>,
|
||||||
|
pub block: bool,
|
||||||
|
pub orphan: bool,
|
||||||
pub done: Option<oneshot::Sender<()>>,
|
pub done: Option<oneshot::Sender<()>>,
|
||||||
|
|
||||||
|
pub spread: bool, // TODO: remove
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<CmdCow> for ProcessOpenOpt {
|
impl TryFrom<CmdCow> for ProcessOpenOpt {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::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 anyhow::Result;
|
||||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::{Child, Command}, sync::mpsc::{self, UnboundedReceiver}};
|
use tokio::{io::{AsyncBufReadExt, BufReader}, process::{Child, Command}, sync::mpsc::{self, UnboundedReceiver}};
|
||||||
use yazi_fs::File;
|
use yazi_fs::File;
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||||
use yazi_vfs::VfsFile;
|
use yazi_vfs::VfsFile;
|
||||||
|
|
||||||
pub struct FdOpt {
|
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 anyhow::{Result, bail};
|
||||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
||||||
use yazi_fs::File;
|
use yazi_fs::File;
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||||
use yazi_vfs::VfsFile;
|
use yazi_vfs::VfsFile;
|
||||||
|
|
||||||
pub struct RgOpt {
|
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 anyhow::{Result, bail};
|
||||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
||||||
use yazi_fs::File;
|
use yazi_fs::File;
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||||
use yazi_vfs::VfsFile;
|
use yazi_vfs::VfsFile;
|
||||||
|
|
||||||
pub struct RgaOpt {
|
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_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, Url, UrlRef};
|
||||||
use yazi_config::Pattern;
|
use yazi_config::Pattern;
|
||||||
use yazi_fs::{mounts::PARTITIONS, provider::{DirReader, FileHolder}};
|
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 yazi_vfs::{VfsFile, provider};
|
||||||
|
|
||||||
use crate::bindings::SizeCalculator;
|
use crate::bindings::SizeCalculator;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use std::hash::Hash;
|
||||||
use mlua::{Function, Lua, Table};
|
use mlua::{Function, Lua, Table};
|
||||||
use yazi_binding::{FileRef, Url};
|
use yazi_binding::{FileRef, Url};
|
||||||
use yazi_config::YAZI;
|
use yazi_config::YAZI;
|
||||||
|
use yazi_shared::url::UrlLike;
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
use crate::Twox128;
|
use crate::Twox128;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use mlua::{Function, IntoLuaMulti, Lua, Value};
|
use mlua::{Function, IntoLuaMulti, Lua, Value};
|
||||||
use yazi_adapter::{ADAPTOR, Image};
|
use yazi_adapter::{ADAPTOR, Image};
|
||||||
use yazi_binding::{Error, UrlRef, elements::Rect};
|
use yazi_binding::{Error, UrlRef, elements::Rect};
|
||||||
|
use yazi_shared::url::UrlLike;
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
use crate::bindings::ImageInfo;
|
use crate::bindings::ImageInfo;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use yazi_binding::{Error, elements::{Area, Renderable, Text}};
|
||||||
use yazi_config::YAZI;
|
use yazi_config::YAZI;
|
||||||
use yazi_parser::mgr::{PreviewLock, UpdatePeekedOpt};
|
use yazi_parser::mgr::{PreviewLock, UpdatePeekedOpt};
|
||||||
use yazi_proxy::MgrProxy;
|
use yazi_proxy::MgrProxy;
|
||||||
use yazi_shared::errors::PeekError;
|
use yazi_shared::{errors::PeekError, url::UrlLike};
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
use crate::external::Highlighter;
|
use crate::external::Highlighter;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ impl AppProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify(opt: NotifyOpt) {
|
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) {
|
pub fn update_notify(dur: Duration) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ impl MgrProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_do(opt: OpenDoOpt) {
|
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) {
|
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 tokio::sync::oneshot;
|
||||||
use yazi_config::opener::OpenerRule;
|
|
||||||
use yazi_macro::{emit, relay};
|
use yazi_macro::{emit, relay};
|
||||||
use yazi_parser::{mgr::OpenWithOpt, tasks::ProcessOpenOpt};
|
use yazi_parser::tasks::ProcessOpenOpt;
|
||||||
use yazi_shared::url::{UrlBuf, UrlCow};
|
use yazi_shared::url::{UrlBuf, UrlCow};
|
||||||
|
|
||||||
pub struct TasksProxy;
|
pub struct TasksProxy;
|
||||||
|
|
||||||
impl TasksProxy {
|
impl TasksProxy {
|
||||||
pub fn file_open(opener: Cow<'static, OpenerRule>, cwd: UrlBuf, targets: Vec<UrlCow<'static>>) {
|
// TODO: remove
|
||||||
emit!(Call(relay!(tasks:file_open).with_any("option", OpenWithOpt { opener, cwd, targets })));
|
pub fn open_shell_compat(opt: ProcessOpenOpt) {
|
||||||
|
emit!(Call(relay!(tasks:open_shell_compat).with_any("opt", opt)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn process_exec(
|
pub async fn process_exec(
|
||||||
opener: Cow<'static, OpenerRule>,
|
|
||||||
cwd: UrlBuf,
|
cwd: UrlBuf,
|
||||||
args: Vec<Cow<'static, OsStr>>,
|
cmd: OsString,
|
||||||
|
args: Vec<UrlCow<'static>>,
|
||||||
|
block: bool,
|
||||||
|
orphan: bool,
|
||||||
) {
|
) {
|
||||||
let (tx, rx) = oneshot::channel();
|
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,
|
cwd,
|
||||||
opener,
|
cmd,
|
||||||
args,
|
args,
|
||||||
done: Some(tx)
|
block,
|
||||||
|
orphan,
|
||||||
|
done: Some(tx),
|
||||||
|
spread: false
|
||||||
})));
|
})));
|
||||||
rx.await.ok();
|
rx.await.ok();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use tracing::warn;
|
||||||
use yazi_config::YAZI;
|
use yazi_config::YAZI;
|
||||||
use yazi_fs::{cha::Cha, ok_or_not_found, path::{path_relative_to, skip_url}, provider::{DirReader, FileHolder}};
|
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_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 yazi_vfs::{VfsCha, copy_with_progress, maybe_exists, provider::{self, DirEntry}};
|
||||||
|
|
||||||
use super::{FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash};
|
use super::{FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use tracing::error;
|
||||||
use yazi_config::Priority;
|
use yazi_config::Priority;
|
||||||
use yazi_fs::FilesOp;
|
use yazi_fs::FilesOp;
|
||||||
use yazi_plugin::isolate;
|
use yazi_plugin::isolate;
|
||||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlLike}};
|
||||||
use yazi_vfs::provider;
|
use yazi_vfs::provider;
|
||||||
|
|
||||||
use super::{PreworkInFetch, PreworkInLoad, PreworkInSize};
|
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 tokio::sync::mpsc;
|
||||||
use yazi_shared::{Id, url::UrlBuf};
|
use yazi_shared::{Id, url::{UrlBuf, UrlCow}};
|
||||||
|
|
||||||
use super::ShellOpt;
|
use super::ShellOpt;
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub(crate) struct ProcessInBlock {
|
||||||
pub(crate) id: Id,
|
pub(crate) id: Id,
|
||||||
pub(crate) cwd: UrlBuf,
|
pub(crate) cwd: UrlBuf,
|
||||||
pub(crate) cmd: OsString,
|
pub(crate) cmd: OsString,
|
||||||
pub(crate) args: Vec<Cow<'static, OsStr>>,
|
pub(crate) args: Vec<UrlCow<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ProcessInBlock> for ShellOpt {
|
impl From<ProcessInBlock> for ShellOpt {
|
||||||
|
|
@ -26,7 +26,7 @@ pub(crate) struct ProcessInOrphan {
|
||||||
pub(crate) id: Id,
|
pub(crate) id: Id,
|
||||||
pub(crate) cwd: UrlBuf,
|
pub(crate) cwd: UrlBuf,
|
||||||
pub(crate) cmd: OsString,
|
pub(crate) cmd: OsString,
|
||||||
pub(crate) args: Vec<Cow<'static, OsStr>>,
|
pub(crate) args: Vec<UrlCow<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ProcessInOrphan> for ShellOpt {
|
impl From<ProcessInOrphan> for ShellOpt {
|
||||||
|
|
@ -41,7 +41,7 @@ pub(crate) struct ProcessInBg {
|
||||||
pub(crate) id: Id,
|
pub(crate) id: Id,
|
||||||
pub(crate) cwd: UrlBuf,
|
pub(crate) cwd: UrlBuf,
|
||||||
pub(crate) cmd: OsString,
|
pub(crate) cmd: OsString,
|
||||||
pub(crate) args: Vec<Cow<'static, OsStr>>,
|
pub(crate) args: Vec<UrlCow<'static>>,
|
||||||
pub(crate) cancel: mpsc::Receiver<()>,
|
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 anyhow::{Result, bail};
|
||||||
use tokio::process::{Child, Command};
|
use tokio::process::{Child, Command};
|
||||||
use yazi_fs::FsUrl;
|
use yazi_fs::FsUrl;
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::{UrlBuf, UrlCow, UrlLike};
|
||||||
|
|
||||||
pub(crate) struct ShellOpt {
|
pub(crate) struct ShellOpt {
|
||||||
pub(crate) cwd: UrlBuf,
|
pub(crate) cwd: UrlBuf,
|
||||||
pub(crate) cmd: OsString,
|
pub(crate) cmd: OsString,
|
||||||
pub(crate) args: Vec<Cow<'static, OsStr>>,
|
pub(crate) args: Vec<UrlCow<'static>>,
|
||||||
pub(crate) piped: bool,
|
pub(crate) piped: bool,
|
||||||
pub(crate) orphan: bool,
|
pub(crate) orphan: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -39,13 +39,17 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result<Child> {
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
return Ok(unsafe {
|
return Ok(unsafe {
|
||||||
|
use yazi_shared::url::AsUrl;
|
||||||
|
|
||||||
Command::new("sh")
|
Command::new("sh")
|
||||||
.arg("-c")
|
|
||||||
.stdin(opt.stdio())
|
.stdin(opt.stdio())
|
||||||
.stdout(opt.stdio())
|
.stdout(opt.stdio())
|
||||||
.stderr(opt.stdio())
|
.stderr(opt.stdio())
|
||||||
|
.arg("-c")
|
||||||
.arg(opt.cmd)
|
.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)
|
.current_dir(cwd)
|
||||||
.kill_on_drop(!opt.orphan)
|
.kill_on_drop(!opt.orphan)
|
||||||
.pre_exec(move || {
|
.pre_exec(move || {
|
||||||
|
|
@ -60,11 +64,11 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result<Child> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
return Ok(
|
return Ok(
|
||||||
Command::new("cmd.exe")
|
Command::new("cmd.exe")
|
||||||
.raw_arg("/C")
|
|
||||||
.raw_arg(parser::parse(&opt.cmd, &opt.args))
|
|
||||||
.stdin(opt.stdio())
|
.stdin(opt.stdio())
|
||||||
.stdout(opt.stdio())
|
.stdout(opt.stdio())
|
||||||
.stderr(opt.stdio())
|
.stderr(opt.stdio())
|
||||||
|
.raw_arg("/C")
|
||||||
|
.raw_arg(opt.cmd)
|
||||||
.current_dir(cwd)
|
.current_dir(cwd)
|
||||||
.kill_on_drop(!opt.orphan)
|
.kill_on_drop(!opt.orphan)
|
||||||
.spawn()?,
|
.spawn()?,
|
||||||
|
|
@ -72,176 +76,3 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result<Child> {
|
||||||
})
|
})
|
||||||
.await?
|
.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 anyhow::Result;
|
||||||
use futures::{FutureExt, future::BoxFuture};
|
use futures::{FutureExt, future::BoxFuture};
|
||||||
|
|
@ -9,7 +9,7 @@ use yazi_dds::Pump;
|
||||||
use yazi_fs::FsUrl;
|
use yazi_fs::FsUrl;
|
||||||
use yazi_parser::{app::PluginOpt, tasks::ProcessOpenOpt};
|
use yazi_parser::{app::PluginOpt, tasks::ProcessOpenOpt};
|
||||||
use yazi_proxy::TasksProxy;
|
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 yazi_vfs::{must_be_dir, provider, unique_name};
|
||||||
|
|
||||||
use super::{Ongoing, TaskOp};
|
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 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() {
|
if args.is_empty() {
|
||||||
format!("Run {:?}", opener.run)
|
format!("Run {:?}", opt.cmd)
|
||||||
} else {
|
} else {
|
||||||
format!("Run {:?} with `{args}`", opener.run)
|
format!("Run {:?} with `{args}`", opt.cmd)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ongoing = self.ongoing.lock();
|
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())
|
(ongoing.add::<ProcessProgBlock>(name), ProcessOutBlock::Clean.into())
|
||||||
} else if opener.orphan {
|
} else if opt.orphan {
|
||||||
(ongoing.add::<ProcessProgOrphan>(name), ProcessOutOrphan::Clean.into())
|
(ongoing.add::<ProcessProgOrphan>(name), ProcessOutOrphan::Clean.into())
|
||||||
} else {
|
} else {
|
||||||
(ongoing.add::<ProcessProgBg>(name), ProcessOutBg::Clean.into())
|
(ongoing.add::<ProcessProgBg>(name), ProcessOutBg::Clean.into())
|
||||||
|
|
@ -332,21 +332,22 @@ impl Scheduler {
|
||||||
cancel_tx.send(()).await.ok();
|
cancel_tx.send(()).await.ok();
|
||||||
cancel_tx.closed().await;
|
cancel_tx.closed().await;
|
||||||
}
|
}
|
||||||
if let Some(tx) = done {
|
if let Some(tx) = opt.done {
|
||||||
tx.send(()).ok();
|
tx.send(()).ok();
|
||||||
}
|
}
|
||||||
ops.out(id, clean);
|
ops.out(id, clean);
|
||||||
});
|
});
|
||||||
|
|
||||||
let cmd = OsString::from(&opener.run);
|
|
||||||
let process = self.process.clone();
|
let process = self.process.clone();
|
||||||
self.send_micro::<_, TaskOut>(id, NORMAL, async move {
|
self.send_micro::<_, TaskOut>(id, NORMAL, async move {
|
||||||
if opener.block {
|
if opt.block {
|
||||||
process.block(ProcessInBlock { id, cwd, cmd, args }).await?;
|
process.block(ProcessInBlock { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }).await?;
|
||||||
} else if opener.orphan {
|
} else if opt.orphan {
|
||||||
process.orphan(ProcessInOrphan { id, cwd, cmd, args }).await?;
|
process.orphan(ProcessInOrphan { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }).await?;
|
||||||
} else {
|
} 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(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ impl LocBuf {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::url::UrlBuf;
|
use crate::url::{UrlBuf, UrlLike};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new() {
|
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]
|
#[inline]
|
||||||
pub fn bytes(self) -> &'a [u8] { self.inner.as_os_str().as_encoded_bytes() }
|
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 std::{borrow::Cow, ffi::{OsStr, OsString}};
|
||||||
|
|
||||||
use crate::url::UrlBuf;
|
use crate::url::{UrlBuf, UrlLike};
|
||||||
|
|
||||||
pub trait IntoStringLossy {
|
pub trait IntoStringLossy {
|
||||||
fn into_string_lossy(self) -> String;
|
fn into_string_lossy(self) -> String;
|
||||||
|
|
@ -33,5 +33,6 @@ impl IntoStringLossy for Cow<'_, OsStr> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoStringLossy for &UrlBuf {
|
impl IntoStringLossy for &UrlBuf {
|
||||||
|
// FIXME: remove
|
||||||
fn into_string_lossy(self) -> String { self.os_str().into_string_lossy() }
|
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 anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
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)]
|
#[derive(Clone, Default, Eq, Hash, PartialEq)]
|
||||||
pub struct UrlBuf {
|
pub struct UrlBuf {
|
||||||
|
|
@ -89,67 +89,6 @@ impl UrlBuf {
|
||||||
// }; &U
|
// }; &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]
|
#[inline]
|
||||||
pub fn into_path(self) -> Option<PathBuf> {
|
pub fn into_path(self) -> Option<PathBuf> {
|
||||||
Some(self.loc.into_path()).filter(|_| !self.scheme.is_virtual())
|
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() }
|
Self { loc: self.loc.rebase(base), scheme: self.scheme.clone() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn pair(&self) -> Option<(Url<'_>, &Urn)> { self.as_url().pair() }
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) }
|
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 {
|
impl UrlBuf {
|
||||||
// --- Regular
|
// --- Regular
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -225,34 +153,6 @@ impl UrlBuf {
|
||||||
Scheme::Archive(_) => false,
|
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 {
|
impl Debug for UrlBuf {
|
||||||
|
|
@ -286,6 +186,7 @@ mod tests {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::url::UrlLike;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_join() -> anyhow::Result<()> {
|
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 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)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum Component<'a> {
|
pub enum Component<'a> {
|
||||||
|
|
@ -60,8 +60,7 @@ impl<'a> FromIterator<Component<'a>> for PathBuf {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Components<'a> {
|
pub struct Components<'a> {
|
||||||
inner: path::Components<'a>,
|
inner: path::Components<'a>,
|
||||||
loc: Loc<'a>,
|
url: Url<'a>,
|
||||||
scheme: SchemeRef<'a>,
|
|
||||||
scheme_yielded: bool,
|
scheme_yielded: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,36 +68,20 @@ impl<'a> From<Url<'a>> for Components<'a> {
|
||||||
fn from(value: Url<'a>) -> Self {
|
fn from(value: Url<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: value.loc.as_path().components(),
|
inner: value.loc.as_path().components(),
|
||||||
loc: value.loc,
|
url: value,
|
||||||
scheme: value.scheme,
|
|
||||||
scheme_yielded: false,
|
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> {
|
impl<'a> Components<'a> {
|
||||||
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
pub fn os_str(&self) -> Cow<'a, OsStr> {
|
||||||
let path = self.inner.as_path();
|
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();
|
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.reserve_exact(path.as_os_str().len());
|
||||||
s.push(path);
|
s.push(path);
|
||||||
s.into()
|
s.into()
|
||||||
|
|
@ -107,7 +90,7 @@ impl<'a> Components<'a> {
|
||||||
pub fn covariant(&self, other: &Self) -> bool {
|
pub fn covariant(&self, other: &Self) -> bool {
|
||||||
match (self.scheme_yielded, other.scheme_yielded) {
|
match (self.scheme_yielded, other.scheme_yielded) {
|
||||||
(false, false) => {}
|
(false, false) => {}
|
||||||
(true, true) if self.scheme.covariant(other.scheme) => {}
|
(true, true) if self.url.scheme.covariant(other.url.scheme) => {}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
self.inner == other.inner
|
self.inner == other.inner
|
||||||
|
|
@ -120,7 +103,7 @@ impl<'a> Iterator for Components<'a> {
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if !self.scheme_yielded {
|
if !self.scheme_yielded {
|
||||||
self.scheme_yielded = true;
|
self.scheme_yielded = true;
|
||||||
Some(Component::Scheme(self.scheme))
|
Some(Component::Scheme(self.url.scheme))
|
||||||
} else {
|
} else {
|
||||||
self.inner.next().map(Into::into)
|
self.inner.next().map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +123,7 @@ impl<'a> DoubleEndedIterator for Components<'a> {
|
||||||
Some(comp.into())
|
Some(comp.into())
|
||||||
} else if !self.scheme_yielded {
|
} else if !self.scheme_yielded {
|
||||||
self.scheme_yielded = true;
|
self.scheme_yielded = true;
|
||||||
Some(Component::Scheme(self.scheme))
|
Some(Component::Scheme(self.url.scheme))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -151,8 +134,8 @@ impl<'a> FusedIterator for Components<'a> {}
|
||||||
|
|
||||||
impl<'a> PartialEq for Components<'a> {
|
impl<'a> PartialEq for Components<'a> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
Some(self.scheme).filter(|_| !self.scheme_yielded)
|
Some(self.url.scheme).filter(|_| !self.scheme_yielded)
|
||||||
== Some(other.scheme).filter(|_| !other.scheme_yielded)
|
== Some(other.url.scheme).filter(|_| !other.scheme_yielded)
|
||||||
&& self.inner == other.inner
|
&& self.inner == other.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +146,7 @@ mod tests {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::pool::InternStr;
|
use crate::{pool::InternStr, url::UrlLike};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collect() {
|
fn test_collect() {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{hash::{Hash, Hasher}, ops::Deref, path::PathBuf};
|
||||||
use hashbrown::Equivalent;
|
use hashbrown::Equivalent;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::url::{Url, UrlBuf, UrlCow};
|
use crate::url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct UrlCov<'a>(Url<'a>);
|
pub struct UrlCov<'a>(Url<'a>);
|
||||||
|
|
@ -19,7 +19,7 @@ impl<'a> From<&'a UrlBufCov> for UrlCov<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq<UrlBufCov> for UrlCov<'_> {
|
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<'_> {
|
impl Hash for UrlCov<'_> {
|
||||||
|
|
@ -79,21 +79,13 @@ impl<'a> From<&'a UrlBufCov> for Url<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for UrlBufCov {
|
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 {
|
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 {
|
impl PartialEq<UrlBuf> for UrlBufCov {
|
||||||
fn eq(&self, other: &UrlBuf) -> bool { self.covariant(other) }
|
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 anyhow::Result;
|
||||||
use percent_encoding::percent_decode;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum UrlCow<'a> {
|
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]
|
#[inline]
|
||||||
pub fn into_owned(self) -> UrlBuf {
|
pub fn into_owned(self) -> UrlBuf {
|
||||||
match self {
|
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]
|
#[inline]
|
||||||
pub fn to_owned(&self) -> UrlBuf { self.as_url().into() }
|
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)>)> {
|
pub fn parse(bytes: &[u8]) -> Result<(SchemeCow<'_>, Cow<'_, Path>, Option<(usize, usize)>)> {
|
||||||
let mut skip = 0;
|
let mut skip = 0;
|
||||||
let (scheme, tilde, uri, urn) = SchemeCow::parse(bytes, &mut skip)?;
|
let (scheme, tilde, uri, urn) = SchemeCow::parse(bytes, &mut skip)?;
|
||||||
|
|
@ -179,13 +156,4 @@ impl<'a> UrlCow<'a> {
|
||||||
impl UrlCow<'_> {
|
impl UrlCow<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_regular(&self) -> bool { self.as_url().is_regular() }
|
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> {
|
pub struct Display<'a> {
|
||||||
inner: &'a UrlBuf,
|
inner: Url<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Display<'a> {
|
impl<'a> From<Url<'a>> for Display<'a> {
|
||||||
#[inline]
|
fn from(value: Url<'a>) -> Self { Self { inner: value } }
|
||||||
pub fn new(inner: &'a UrlBuf) -> Self { Self { inner } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::fmt::Display for Display<'a> {
|
impl<'a> std::fmt::Display for Display<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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() {
|
if scheme.is_virtual() {
|
||||||
Encode::from(self.inner).fmt(f)?;
|
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 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> {
|
#[derive(Clone, Copy)]
|
||||||
loc: Loc<'a>,
|
pub struct Encode<'a>(Url<'a>);
|
||||||
scheme: SchemeRef<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Url<'a>> for Encode<'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> {
|
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> {
|
impl<'a> Encode<'a> {
|
||||||
#[inline]
|
|
||||||
pub(super) fn new(loc: Loc<'a>, scheme: SchemeRef<'a>) -> Self { Self { loc, scheme } }
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn domain<'s>(s: &'s str) -> PercentEncode<'s> {
|
pub fn domain<'s>(s: &'s str) -> PercentEncode<'s> {
|
||||||
const SET: &AsciiSet = &CONTROLS.add(b'/').add(b':');
|
const SET: &AsciiSet = &CONTROLS.add(b'/').add(b':');
|
||||||
percent_encode(s.as_bytes(), SET)
|
percent_encode(s.as_bytes(), SET)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ports(&self) -> impl Display {
|
fn ports(self) -> impl Display {
|
||||||
struct D<'a>(&'a Encode<'a>);
|
struct D<'a>(Encode<'a>);
|
||||||
|
|
||||||
impl Display for D<'_> {
|
impl Display for D<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
macro_rules! w {
|
macro_rules! w {
|
||||||
($default_uri:expr, $default_urn:expr) => {{
|
($default_uri:expr, $default_urn:expr) => {{
|
||||||
let uri = self.0.loc.uri().count();
|
let uri = self.0.0.loc.uri().count();
|
||||||
let urn = self.0.loc.urn().count();
|
let urn = self.0.0.loc.urn().count();
|
||||||
match (uri != $default_uri, urn != $default_urn) {
|
match (uri != $default_uri, urn != $default_urn) {
|
||||||
(true, true) => write!(f, ":{uri}:{urn}"),
|
(true, true) => write!(f, ":{uri}:{urn}"),
|
||||||
(true, false) => write!(f, ":{uri}"),
|
(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::Regular => Ok(()),
|
||||||
SchemeRef::Search(_) | SchemeRef::Archive(_) => w!(0, 0),
|
SchemeRef::Search(_) | SchemeRef::Archive(_) => w!(0, 0),
|
||||||
SchemeRef::Sftp(_) => w!(
|
SchemeRef::Sftp(_) => w!(
|
||||||
self.0.loc.as_os_str().is_empty().not() as usize,
|
self.0.0.loc.as_os_str().is_empty().not() as usize,
|
||||||
self.0.loc.file_name().is_some() as usize
|
self.0.0.loc.file_name().is_some() as usize
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +58,7 @@ impl<'a> Encode<'a> {
|
||||||
impl Display for Encode<'_> {
|
impl Display for Encode<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use SchemeRef as S;
|
use SchemeRef as S;
|
||||||
match self.scheme {
|
match self.0.scheme {
|
||||||
S::Regular => write!(f, "regular://"),
|
S::Regular => write!(f, "regular://"),
|
||||||
S::Search(d) => write!(f, "search://{}{}/", Self::domain(d), self.ports()),
|
S::Search(d) => write!(f, "search://{}{}/", Self::domain(d), self.ports()),
|
||||||
S::Archive(d) => write!(f, "archive://{}{}/", 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
|
// --- Tilded
|
||||||
pub struct EncodeTilded<'a> {
|
#[derive(Clone, Copy)]
|
||||||
loc: Loc<'a>,
|
pub struct EncodeTilded<'a>(Url<'a>);
|
||||||
scheme: SchemeRef<'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> {
|
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> {
|
impl<'a> From<EncodeTilded<'a>> for Encode<'a> {
|
||||||
fn from(value: &EncodeTilded<'a>) -> Self { Self::new(value.loc, value.scheme) }
|
fn from(value: EncodeTilded<'a>) -> Self { Self(value.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for EncodeTilded<'_> {
|
impl Display for EncodeTilded<'_> {
|
||||||
|
|
@ -91,14 +88,14 @@ impl Display for EncodeTilded<'_> {
|
||||||
use Encode as E;
|
use Encode as E;
|
||||||
use SchemeRef as S;
|
use SchemeRef as S;
|
||||||
|
|
||||||
let loc = percent_encode(self.loc.as_os_str().as_encoded_bytes(), CONTROLS);
|
let loc = percent_encode(self.0.loc.as_os_str().as_encoded_bytes(), CONTROLS);
|
||||||
match self.scheme {
|
match self.0.scheme {
|
||||||
S::Regular => write!(f, "regular~://{loc}"),
|
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) => {
|
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 {
|
pub trait AsUrl {
|
||||||
fn as_url(&self) -> Url<'_>;
|
fn as_url(&self) -> Url<'_>;
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +14,7 @@ impl AsUrl for Url<'_> {
|
||||||
|
|
||||||
impl AsUrl for UrlBuf {
|
impl AsUrl for UrlBuf {
|
||||||
#[inline]
|
#[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 {
|
impl AsUrl for &UrlBuf {
|
||||||
|
|
@ -26,7 +29,12 @@ impl AsUrl for &mut UrlBuf {
|
||||||
|
|
||||||
impl AsUrl for UrlCow<'_> {
|
impl AsUrl for UrlCow<'_> {
|
||||||
#[inline]
|
#[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<'_> {
|
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> {
|
impl<'a, T: AsUrl> From<&'a mut T> for Url<'a> {
|
||||||
fn from(value: &'a mut T) -> Self { value.as_url() }
|
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() }
|
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]
|
#[inline]
|
||||||
pub fn uri(self) -> &'a Uri { self.loc.uri() }
|
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 tokio::{select, sync::{mpsc, oneshot}};
|
||||||
use yazi_fs::cha::Cha;
|
use yazi_fs::cha::Cha;
|
||||||
use yazi_macro::ok_or_not_found;
|
use yazi_macro::ok_or_not_found;
|
||||||
use yazi_shared::url::{AsUrl, UrlBuf};
|
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
||||||
|
|
||||||
use crate::provider;
|
use crate::provider;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use yazi_fs::FilesOp;
|
use yazi_fs::FilesOp;
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||||
|
|
||||||
use crate::maybe_exists;
|
use crate::maybe_exists;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{borrow::Cow, ffi::OsStr, io, sync::Arc};
|
use std::{borrow::Cow, ffi::OsStr, io, sync::Arc};
|
||||||
|
|
||||||
use yazi_fs::{cha::{Cha, ChaType}, provider::FileHolder};
|
use yazi_fs::{cha::{Cha, ChaType}, provider::FileHolder};
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||||
|
|
||||||
pub enum DirEntry {
|
pub enum DirEntry {
|
||||||
Regular(yazi_fs::provider::local::DirEntry),
|
Regular(yazi_fs::provider::local::DirEntry),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use yazi_shared::url::{AsUrl, Url, UrlBuf};
|
use yazi_shared::url::{AsUrl, Url, UrlBuf, UrlLike};
|
||||||
|
|
||||||
use crate::{LINKED, WATCHED, backend};
|
use crate::{LINKED, WATCHED, backend};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use yazi_shared::url::{AsUrl, UrlBuf};
|
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Watched(HashSet<UrlBuf>);
|
pub struct Watched(HashSet<UrlBuf>);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use hashbrown::HashSet;
|
||||||
use tokio::{pin, sync::{mpsc::{self, UnboundedReceiver}, watch}};
|
use tokio::{pin, sync::{mpsc::{self, UnboundedReceiver}, watch}};
|
||||||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||||
use yazi_fs::{File, FilesOp, provider::local};
|
use yazi_fs::{File, FilesOp, provider::local};
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||||
use yazi_vfs::VfsFile;
|
use yazi_vfs::VfsFile;
|
||||||
|
|
||||||
use crate::{LINKED, Linked, WATCHED, WATCHER, backend::Backend};
|
use crate::{LINKED, Linked, WATCHED, WATCHER, backend::Backend};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue