mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: remote filesystem support for interactive cd (#3398)
This commit is contained in:
parent
c7739c5941
commit
c6e03e91d2
20 changed files with 148 additions and 123 deletions
|
|
@ -68,8 +68,8 @@ impl Actor for Trigger {
|
|||
impl Trigger {
|
||||
fn split_url(s: &str) -> Option<(UrlBuf, PathBufDyn)> {
|
||||
let (scheme, path) = SchemeCow::parse(s.as_bytes()).ok()?;
|
||||
let scheme = scheme.zeroed();
|
||||
|
||||
tracing::debug!(?scheme, ?path);
|
||||
if scheme.is_local() && path.as_strand() == "~" {
|
||||
return None; // We don't autocomplete a `~`, but `~/`
|
||||
}
|
||||
|
|
@ -86,7 +86,11 @@ impl Trigger {
|
|||
(UrlCow::try_from((scheme, root)).ok()?.into_owned(), c.into())
|
||||
}
|
||||
Some((p, c)) => (expand_url(UrlCow::try_from((scheme, p)).ok()?), c.into()),
|
||||
None => (CWD.load().as_ref().clone(), path.into()),
|
||||
None if CWD.load().scheme().covariant(&scheme) => (CWD.load().as_ref().clone(), path.into()),
|
||||
None => {
|
||||
let empty = PathDyn::with_str(scheme.kind(), "");
|
||||
(UrlCow::try_from((scheme, empty)).ok()?.into_owned(), path.into())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -108,6 +112,7 @@ mod tests {
|
|||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
yazi_shared::init_tests();
|
||||
yazi_fs::init();
|
||||
compare("", "", "");
|
||||
compare(" ", "", " ");
|
||||
|
|
@ -124,6 +129,12 @@ mod tests {
|
|||
compare("//foo/", "/foo/", "");
|
||||
compare("/foo/bar", "/foo/", "bar");
|
||||
compare("///foo/bar", "/foo/", "bar");
|
||||
|
||||
CWD.set(&"sftp://test/".parse::<UrlBuf>().unwrap(), || {});
|
||||
compare("sftp://test/a", "sftp://test/", "a");
|
||||
compare("sftp://test//a", "sftp://test:0//", "a");
|
||||
compare("sftp://test2/a", "sftp://test2/", "a");
|
||||
compare("sftp://test2//a", "sftp://test2:0//", "a");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
|
@ -131,9 +142,11 @@ mod tests {
|
|||
fn test_split() {
|
||||
yazi_fs::init();
|
||||
compare("foo", "", "foo");
|
||||
|
||||
compare(r"foo\", r"foo\", "");
|
||||
compare(r"foo\bar", r"foo\", "bar");
|
||||
compare(r"foo\bar\", r"foo\bar\", "");
|
||||
|
||||
compare(r"C:\", r"C:\", "");
|
||||
compare(r"C:\foo", r"C:\", "foo");
|
||||
compare(r"C:\foo\", r"C:\foo\", "");
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use yazi_fs::{File, FilesOp, path::expand_url};
|
|||
use yazi_macro::{act, err, render, succ};
|
||||
use yazi_parser::mgr::CdOpt;
|
||||
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy};
|
||||
use yazi_shared::{Debounce, data::Data, errors::InputError, url::{UrlBuf, UrlLike}};
|
||||
use yazi_vfs::VfsFile;
|
||||
use yazi_shared::{Debounce, data::Data, errors::InputError, url::{AsUrl, UrlBuf, UrlLike}};
|
||||
use yazi_vfs::{VfsFile, provider};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ impl Actor for Cd {
|
|||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
if opt.interactive {
|
||||
return Self::cd_interactive();
|
||||
return Self::cd_interactive(cx);
|
||||
}
|
||||
|
||||
let tab = cx.tab_mut();
|
||||
|
|
@ -59,8 +59,8 @@ impl Actor for Cd {
|
|||
}
|
||||
|
||||
impl Cd {
|
||||
fn cd_interactive() -> Result<Data> {
|
||||
let input = InputProxy::show(InputCfg::cd());
|
||||
fn cd_interactive(cx: &Ctx) -> Result<Data> {
|
||||
let input = InputProxy::show(InputCfg::cd(cx.cwd().as_url()));
|
||||
|
||||
tokio::spawn(async move {
|
||||
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));
|
||||
|
|
@ -70,6 +70,7 @@ impl Cd {
|
|||
match result {
|
||||
Ok(s) => {
|
||||
let Ok(url) = UrlBuf::try_from(s).map(expand_url) else { return };
|
||||
let Ok(url) = provider::absolute(&url).await else { return };
|
||||
|
||||
let Ok(file) = File::new(&url).await else { return };
|
||||
if file.is_dir() {
|
||||
|
|
@ -79,10 +80,10 @@ impl Cd {
|
|||
if let Some(p) = url.parent() {
|
||||
FilesOp::Upserting(p.into(), [(url.urn().into(), file)].into()).emit();
|
||||
}
|
||||
MgrProxy::reveal(&url);
|
||||
MgrProxy::reveal(url);
|
||||
}
|
||||
Err(InputError::Completed(before, ticket)) => {
|
||||
CmpProxy::trigger(&before, ticket);
|
||||
CmpProxy::trigger(before, ticket);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use anyhow::{Result, bail};
|
||||
use yazi_fs::FilesOp;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::{VoidOpt, mgr::{CdSource, DisplaceDoOpt}};
|
||||
use anyhow::Result;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::{VoidOpt, mgr::DisplaceDoOpt};
|
||||
use yazi_proxy::MgrProxy;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
use yazi_vfs::provider;
|
||||
|
|
@ -23,42 +22,9 @@ impl Actor for Displace {
|
|||
let tab = cx.tab().id;
|
||||
let from = cx.cwd().to_owned();
|
||||
tokio::spawn(async move {
|
||||
MgrProxy::displace_do(tab, DisplaceDoOpt {
|
||||
to: provider::absolute(&from).await.map(|u| u.into_owned()),
|
||||
from,
|
||||
});
|
||||
MgrProxy::displace_do(tab, DisplaceDoOpt { to: provider::canonicalize(&from).await, from });
|
||||
});
|
||||
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Do
|
||||
pub struct DisplaceDo;
|
||||
|
||||
impl Actor for DisplaceDo {
|
||||
type Options = DisplaceDoOpt;
|
||||
|
||||
const NAME: &str = "displace_do";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
if cx.cwd() != opt.from {
|
||||
succ!()
|
||||
}
|
||||
|
||||
let to = match opt.to {
|
||||
Ok(url) => url,
|
||||
Err(e) => return act!(mgr:update_files, cx, FilesOp::IOErr(opt.from, e.into())),
|
||||
};
|
||||
|
||||
if !to.is_absolute() {
|
||||
bail!("Target URL must be absolute");
|
||||
} else if let Some(hovered) = cx.hovered()
|
||||
&& let Ok(url) = to.try_join(hovered.urn())
|
||||
{
|
||||
act!(mgr:reveal, cx, (url, CdSource::Displace))
|
||||
} else {
|
||||
act!(mgr:cd, cx, (to, CdSource::Displace))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
yazi-actor/src/mgr/displace_do.rs
Normal file
36
yazi-actor/src/mgr/displace_do.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use anyhow::{Result, bail};
|
||||
use yazi_fs::FilesOp;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::mgr::{CdSource, DisplaceDoOpt};
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
pub struct DisplaceDo;
|
||||
|
||||
impl Actor for DisplaceDo {
|
||||
type Options = DisplaceDoOpt;
|
||||
|
||||
const NAME: &str = "displace_do";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
if cx.cwd() != opt.from {
|
||||
succ!()
|
||||
}
|
||||
|
||||
let to = match opt.to {
|
||||
Ok(url) => url,
|
||||
Err(e) => return act!(mgr:update_files, cx, FilesOp::IOErr(opt.from, e.into())),
|
||||
};
|
||||
|
||||
if !to.is_absolute() {
|
||||
bail!("Target URL must be absolute");
|
||||
} else if let Some(hovered) = cx.hovered()
|
||||
&& let Ok(url) = to.try_join(hovered.urn())
|
||||
{
|
||||
act!(mgr:reveal, cx, (url, CdSource::Displace))
|
||||
} else {
|
||||
act!(mgr:cd, cx, (to, CdSource::Displace))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ yazi_macro::mod_flat!(
|
|||
copy
|
||||
create
|
||||
displace
|
||||
displace_do
|
||||
download
|
||||
enter
|
||||
escape
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use yazi_fs::{FilesOp, cha::Cha};
|
|||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::{VoidOpt, mgr::{CdSource, SearchOpt, SearchOptVia}};
|
||||
use yazi_plugin::external;
|
||||
use yazi_proxy::{InputProxy, MgrProxy};
|
||||
use yazi_proxy::{AppProxy, InputProxy, MgrProxy};
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
|
@ -52,8 +52,10 @@ impl Actor for SearchDo {
|
|||
handle.abort();
|
||||
}
|
||||
|
||||
let cwd = tab.cwd().to_search(&opt.subject)?;
|
||||
let hidden = tab.pref.show_hidden;
|
||||
let Ok(cwd) = tab.cwd().to_search(&opt.subject) else {
|
||||
succ!(AppProxy::notify_warn("Search", "Only local filesystem searches are supported"));
|
||||
};
|
||||
|
||||
tab.search = Some(tokio::spawn(async move {
|
||||
let rx = match opt.via {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ impl Actor for Stash {
|
|||
const NAME: &str = "stash";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
if opt.target.is_internal() {
|
||||
if opt.target.is_absolute() && opt.target.is_internal() {
|
||||
cx.tab_mut().backstack.push(opt.target.as_url());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}};
|
||||
use yazi_shared::{strand::ToStrand, url::UrlBuf};
|
||||
use yazi_shared::{scheme::Encode as EncodeScheme, strand::ToStrand, url::{Url, UrlBuf}};
|
||||
|
||||
use super::{Offset, Position};
|
||||
use crate::YAZI;
|
||||
|
|
@ -31,9 +31,10 @@ pub struct ConfirmCfg {
|
|||
}
|
||||
|
||||
impl InputCfg {
|
||||
pub fn cd() -> Self {
|
||||
pub fn cd(cwd: Url) -> Self {
|
||||
Self {
|
||||
title: YAZI.input.cd_title.clone(),
|
||||
value: if cwd.kind().is_local() { String::new() } else { EncodeScheme(cwd).to_string() },
|
||||
position: Position::new(YAZI.input.cd_origin, YAZI.input.cd_offset),
|
||||
completion: true,
|
||||
..Default::default()
|
||||
|
|
|
|||
13
yazi-plugin/src/external/fd.rs
vendored
13
yazi-plugin/src/external/fd.rs
vendored
|
|
@ -2,8 +2,8 @@ use std::process::Stdio;
|
|||
|
||||
use anyhow::Result;
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::{Child, Command}, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
use yazi_fs::{File, FsUrl};
|
||||
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
pub struct FdOpt {
|
||||
|
|
@ -34,16 +34,9 @@ pub fn fd(opt: FdOpt) -> Result<UnboundedReceiver<File>> {
|
|||
}
|
||||
|
||||
fn spawn(program: &str, opt: &FdOpt) -> std::io::Result<Child> {
|
||||
let Some(path) = opt.cwd.as_local() else {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"fd can only search local filesystem",
|
||||
));
|
||||
};
|
||||
|
||||
Command::new(program)
|
||||
.arg("--base-directory")
|
||||
.arg(path)
|
||||
.arg(&*opt.cwd.as_url().unified_path())
|
||||
.arg("--regex")
|
||||
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
|
||||
.args(&opt.args)
|
||||
|
|
|
|||
12
yazi-plugin/src/external/rg.rs
vendored
12
yazi-plugin/src/external/rg.rs
vendored
|
|
@ -1,9 +1,9 @@
|
|||
use std::process::Stdio;
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use anyhow::Result;
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
use yazi_fs::{File, FsUrl};
|
||||
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
pub struct RgOpt {
|
||||
|
|
@ -14,16 +14,12 @@ pub struct RgOpt {
|
|||
}
|
||||
|
||||
pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
|
||||
let Some(path) = opt.cwd.as_local() else {
|
||||
bail!("rg can only search local filesystem");
|
||||
};
|
||||
|
||||
let mut child = Command::new("rg")
|
||||
.args(["--color=never", "--files-with-matches", "--smart-case"])
|
||||
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
|
||||
.args(opt.args)
|
||||
.arg(opt.subject)
|
||||
.arg(path)
|
||||
.arg(&*opt.cwd.as_url().unified_path())
|
||||
.kill_on_drop(true)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null())
|
||||
|
|
|
|||
12
yazi-plugin/src/external/rga.rs
vendored
12
yazi-plugin/src/external/rga.rs
vendored
|
|
@ -1,9 +1,9 @@
|
|||
use std::process::Stdio;
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use anyhow::Result;
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_fs::File;
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
use yazi_fs::{File, FsUrl};
|
||||
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
pub struct RgaOpt {
|
||||
|
|
@ -14,16 +14,12 @@ pub struct RgaOpt {
|
|||
}
|
||||
|
||||
pub fn rga(opt: RgaOpt) -> Result<UnboundedReceiver<File>> {
|
||||
let Some(path) = opt.cwd.as_local() else {
|
||||
bail!("rga can only search local filesystem");
|
||||
};
|
||||
|
||||
let mut child = Command::new("rga")
|
||||
.args(["--color=never", "--files-with-matches", "--smart-case"])
|
||||
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
|
||||
.args(opt.args)
|
||||
.arg(opt.subject)
|
||||
.arg(path)
|
||||
.arg(&*opt.cwd.as_url().unified_path())
|
||||
.kill_on_drop(true)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null())
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ impl CmpProxy {
|
|||
emit!(Call(relay!(cmp:show).with_any("opt", opt)));
|
||||
}
|
||||
|
||||
pub fn trigger(word: &str, ticket: Id) {
|
||||
emit!(Call(relay!(cmp:trigger, [word]).with("ticket", ticket)));
|
||||
pub fn trigger(word: impl Into<String>, ticket: Id) {
|
||||
emit!(Call(relay!(cmp:trigger, [word.into()]).with("ticket", ticket)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ impl MgrProxy {
|
|||
emit!(Call(relay!(mgr:arrow, [step.into()])));
|
||||
}
|
||||
|
||||
pub fn cd(target: &UrlBuf) {
|
||||
emit!(Call(relay!(mgr:cd, [target]).with("raw", true)));
|
||||
pub fn cd(target: impl Into<UrlBuf>) {
|
||||
emit!(Call(relay!(mgr:cd, [target.into()]).with("raw", true)));
|
||||
}
|
||||
|
||||
pub fn displace_do(tab: Id, opt: DisplaceDoOpt) {
|
||||
|
|
@ -41,8 +41,8 @@ impl MgrProxy {
|
|||
));
|
||||
}
|
||||
|
||||
pub fn reveal(target: &UrlBuf) {
|
||||
emit!(Call(relay!(mgr:reveal, [target]).with("raw", true).with("no-dummy", true)));
|
||||
pub fn reveal(target: impl Into<UrlBuf>) {
|
||||
emit!(Call(relay!(mgr:reveal, [target.into()]).with("raw", true).with("no-dummy", true)));
|
||||
}
|
||||
|
||||
pub fn search_do(opt: SearchOpt) {
|
||||
|
|
|
|||
|
|
@ -168,6 +168,14 @@ impl<'a> SchemeCow<'a> {
|
|||
}
|
||||
|
||||
impl<'a> SchemeCow<'a> {
|
||||
#[inline]
|
||||
pub fn into_domain(self) -> Option<SymbolCow<'a, str>> {
|
||||
Some(match self {
|
||||
SchemeCow::Borrowed(s) => s.domain()?.into(),
|
||||
SchemeCow::Owned(s) => s.into_domain()?.into(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> Scheme {
|
||||
match self {
|
||||
|
|
@ -176,15 +184,17 @@ impl<'a> SchemeCow<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_domain(self) -> Option<SymbolCow<'a, str>> {
|
||||
Some(match self {
|
||||
SchemeCow::Borrowed(s) => s.domain()?.into(),
|
||||
SchemeCow::Owned(s) => s.into_domain()?.into(),
|
||||
})
|
||||
pub fn with_ports(self, uri: usize, urn: usize) -> Self {
|
||||
match self {
|
||||
Self::Borrowed(s) => s.with_ports(uri, urn).into(),
|
||||
Self::Owned(s) => s.with_ports(uri, urn).into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn zeroed(self) -> Self { self.with_ports(0, 0) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -43,13 +43,9 @@ impl From<SchemeRef<'_>> for Scheme {
|
|||
|
||||
impl<'a> SchemeRef<'a> {
|
||||
#[inline]
|
||||
pub const fn kind(self) -> SchemeKind {
|
||||
match self {
|
||||
Self::Regular { .. } => SchemeKind::Regular,
|
||||
Self::Search { .. } => SchemeKind::Search,
|
||||
Self::Archive { .. } => SchemeKind::Archive,
|
||||
Self::Sftp { .. } => SchemeKind::Sftp,
|
||||
}
|
||||
pub fn covariant(self, other: impl AsScheme) -> bool {
|
||||
let other = other.as_scheme();
|
||||
if self.is_virtual() || other.is_virtual() { self == other } else { true }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -62,6 +58,16 @@ impl<'a> SchemeRef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn kind(self) -> SchemeKind {
|
||||
match self {
|
||||
Self::Regular { .. } => SchemeKind::Regular,
|
||||
Self::Search { .. } => SchemeKind::Search,
|
||||
Self::Archive { .. } => SchemeKind::Archive,
|
||||
Self::Sftp { .. } => SchemeKind::Sftp,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn ports(self) -> (usize, usize) {
|
||||
match self {
|
||||
|
|
@ -72,22 +78,6 @@ impl<'a> SchemeRef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn zeroed(self) -> Self {
|
||||
match self {
|
||||
Self::Regular { .. } => Self::Regular { uri: 0, urn: 0 },
|
||||
Self::Search { domain, .. } => Self::Search { domain, uri: 0, urn: 0 },
|
||||
Self::Archive { domain, .. } => Self::Archive { domain, uri: 0, urn: 0 },
|
||||
Self::Sftp { domain, .. } => Self::Sftp { domain, uri: 0, urn: 0 },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(self, other: impl AsScheme) -> bool {
|
||||
let other = other.as_scheme();
|
||||
if self.is_virtual() || other.is_virtual() { self == other } else { true }
|
||||
}
|
||||
|
||||
pub fn to_owned(self) -> Scheme {
|
||||
match self {
|
||||
Self::Regular { uri, urn } => Scheme::Regular { uri, urn },
|
||||
|
|
@ -96,4 +86,16 @@ impl<'a> SchemeRef<'a> {
|
|||
Self::Sftp { domain, uri, urn } => Scheme::Sftp { domain: domain.intern(), uri, urn },
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn with_ports(self, uri: usize, urn: usize) -> Self {
|
||||
match self {
|
||||
Self::Regular { .. } => Self::Regular { uri, urn },
|
||||
Self::Search { domain, .. } => Self::Search { domain, uri, urn },
|
||||
Self::Archive { domain, .. } => Self::Archive { domain, uri, urn },
|
||||
Self::Sftp { domain, .. } => Self::Sftp { domain, uri, urn },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn zeroed(self) -> Self { self.with_ports(0, 0) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,4 +38,7 @@ impl Scheme {
|
|||
Self::Sftp { domain, .. } => Self::Sftp { domain, uri, urn },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn zeroed(self) -> Self { self.with_ports(0, 0) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,15 +44,15 @@ pub(super) fn copy_with_progress_impl(
|
|||
Some(f)
|
||||
};
|
||||
|
||||
let chunks = (cha.len + 10485760 - 1) / 10485760;
|
||||
let chunks = (cha.len + 5242880 - 1) / 5242880;
|
||||
let mut result = futures::stream::iter(0..chunks)
|
||||
.map(|i| {
|
||||
let acc_ = acc_.clone();
|
||||
let (from, to) = (from.clone(), to.clone());
|
||||
let (src, dist) = (src.take(), dist.take());
|
||||
async move {
|
||||
let offset = i * 10485760;
|
||||
let take = cha.len.saturating_sub(offset).min(10485760);
|
||||
let offset = i * 5242880;
|
||||
let take = cha.len.saturating_sub(offset).min(5242880);
|
||||
|
||||
let mut src = BufReader::with_capacity(524288, match src {
|
||||
Some(f) => f,
|
||||
|
|
@ -93,7 +93,7 @@ pub(super) fn copy_with_progress_impl(
|
|||
}
|
||||
}
|
||||
})
|
||||
.buffer_unordered(3)
|
||||
.buffer_unordered(4)
|
||||
.try_fold(None, |first, file| async { Ok(first.or(file)) })
|
||||
.await;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{io, sync::Arc};
|
|||
|
||||
use tokio::{io::{AsyncWriteExt, BufReader, BufWriter}, sync::mpsc::Receiver};
|
||||
use yazi_config::vfs::{ProviderSftp, Vfs};
|
||||
use yazi_fs::provider::{DirReader, FileHolder, Provider};
|
||||
use yazi_fs::{CWD, provider::{DirReader, FileHolder, Provider}};
|
||||
use yazi_sftp::fs::{Attrs, Flags};
|
||||
use yazi_shared::{loc::LocBuf, path::{AsPath, PathBufDyn}, pool::InternStr, scheme::SchemeKind, url::{Url, UrlBuf, UrlCow, UrlLike}};
|
||||
|
||||
|
|
@ -26,7 +26,14 @@ impl<'a> Provider for Sftp<'a> {
|
|||
type UrlCow = UrlCow<'a>;
|
||||
|
||||
async fn absolute(&self) -> io::Result<Self::UrlCow> {
|
||||
Ok(if self.url.is_absolute() { self.url.into() } else { self.canonicalize().await?.into() })
|
||||
let cwd = CWD.load();
|
||||
Ok(if self.url.is_absolute() {
|
||||
self.url.into()
|
||||
} else if cwd.scheme().covariant(self.url.scheme()) {
|
||||
cwd.try_join(self.path)?.into()
|
||||
} else {
|
||||
self.canonicalize().await?.into()
|
||||
})
|
||||
}
|
||||
|
||||
async fn canonicalize(&self) -> io::Result<UrlBuf> {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ impl Linked {
|
|||
Ok(to) if to != *from => _ = linked.write().entry_ref(from).insert(to),
|
||||
Ok(_) => _ = linked.write().remove(from),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => _ = linked.write().remove(from),
|
||||
Err(_) => {}
|
||||
Err(e) => tracing::error!("Failed to canonicalize watched path {from:?}: {e:?}"),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -56,10 +56,8 @@ impl Watcher {
|
|||
|
||||
if !to_unwatch.is_empty() || !to_watch.is_empty() {
|
||||
backend = Self::sync(backend, to_unwatch, to_watch).await;
|
||||
if !rx.has_changed().unwrap_or(false) {
|
||||
backend = backend.sync().await;
|
||||
}
|
||||
}
|
||||
|
||||
if rx.changed().await.is_err() {
|
||||
break;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue