mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: remote file management (#3396)
This commit is contained in:
parent
878b74acb7
commit
c7739c5941
19 changed files with 105 additions and 62 deletions
|
|
@ -14,7 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Remote file management ([#3166], [#3170], [#3172], [#3198], [#3201], [#3243], [#3264], [#3268])
|
- Remote file management ([#3396])
|
||||||
- Virtual file system ([#3034], [#3035], [#3094], [#3108], [#3187], [#3203])
|
- Virtual file system ([#3034], [#3035], [#3094], [#3108], [#3187], [#3203])
|
||||||
- Shell formatting ([#3232])
|
- Shell formatting ([#3232])
|
||||||
- Multi-entry support for plugin system ([#3154])
|
- Multi-entry support for plugin system ([#3154])
|
||||||
|
|
@ -83,6 +83,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
||||||
|
|
||||||
- Make preload tasks discardable ([#2875])
|
- Make preload tasks discardable ([#2875])
|
||||||
- Reduce file change event frequency ([#2820])
|
- Reduce file change event frequency ([#2820])
|
||||||
|
- Upload and download of a single file over SFTP in chunks concurrently ([#3393])
|
||||||
- Do not listen for file changes in inactive tabs ([#2958])
|
- Do not listen for file changes in inactive tabs ([#2958])
|
||||||
- Switch to a higher-performance hash algorithm ([#3083])
|
- Switch to a higher-performance hash algorithm ([#3083])
|
||||||
- Sequence-based rendering merge strategy ([#2861])
|
- Sequence-based rendering merge strategy ([#2861])
|
||||||
|
|
@ -1548,3 +1549,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
||||||
[#3385]: https://github.com/sxyazi/yazi/pull/3385
|
[#3385]: https://github.com/sxyazi/yazi/pull/3385
|
||||||
[#3387]: https://github.com/sxyazi/yazi/pull/3387
|
[#3387]: https://github.com/sxyazi/yazi/pull/3387
|
||||||
[#3391]: https://github.com/sxyazi/yazi/pull/3391
|
[#3391]: https://github.com/sxyazi/yazi/pull/3391
|
||||||
|
[#3393]: https://github.com/sxyazi/yazi/pull/3393
|
||||||
|
[#3396]: https://github.com/sxyazi/yazi/pull/3396
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use yazi_fs::{CWD, path::expand_url, provider::{DirReader, FileHolder}};
|
||||||
use yazi_macro::{act, render, succ};
|
use yazi_macro::{act, render, succ};
|
||||||
use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt};
|
use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt};
|
||||||
use yazi_proxy::CmpProxy;
|
use yazi_proxy::CmpProxy;
|
||||||
use yazi_shared::{AnyAsciiChar, data::Data, natsort, path::{PathBufDyn, PathDyn, PathLike}, scheme::{SchemeCow, SchemeLike}, strand::StrandLike, url::{UrlBuf, UrlCow, UrlLike}};
|
use yazi_shared::{AnyAsciiChar, data::Data, natsort, path::{PathBufDyn, PathDyn, PathLike}, scheme::{SchemeCow, SchemeLike}, strand::{AsStrand, StrandLike}, url::{UrlBuf, UrlCow, UrlLike}};
|
||||||
use yazi_vfs::provider;
|
use yazi_vfs::provider;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
@ -69,7 +69,8 @@ impl Trigger {
|
||||||
fn split_url(s: &str) -> Option<(UrlBuf, PathBufDyn)> {
|
fn split_url(s: &str) -> Option<(UrlBuf, PathBufDyn)> {
|
||||||
let (scheme, path) = SchemeCow::parse(s.as_bytes()).ok()?;
|
let (scheme, path) = SchemeCow::parse(s.as_bytes()).ok()?;
|
||||||
|
|
||||||
if scheme.is_local() && path == "~" {
|
tracing::debug!(?scheme, ?path);
|
||||||
|
if scheme.is_local() && path.as_strand() == "~" {
|
||||||
return None; // We don't autocomplete a `~`, but `~/`
|
return None; // We don't autocomplete a `~`, but `~/`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
|
use yazi_fs::FilesOp;
|
||||||
use yazi_macro::{act, succ};
|
use yazi_macro::{act, succ};
|
||||||
use yazi_parser::{VoidOpt, mgr::{CdSource, DisplaceDoOpt}};
|
use yazi_parser::{VoidOpt, mgr::{CdSource, DisplaceDoOpt}};
|
||||||
use yazi_proxy::MgrProxy;
|
use yazi_proxy::MgrProxy;
|
||||||
|
|
@ -22,11 +23,10 @@ impl Actor for Displace {
|
||||||
let tab = cx.tab().id;
|
let tab = cx.tab().id;
|
||||||
let from = cx.cwd().to_owned();
|
let from = cx.cwd().to_owned();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Ok(to) = provider::absolute(&from).await
|
MgrProxy::displace_do(tab, DisplaceDoOpt {
|
||||||
&& to.is_owned()
|
to: provider::absolute(&from).await.map(|u| u.into_owned()),
|
||||||
{
|
from,
|
||||||
MgrProxy::displace_do(tab, DisplaceDoOpt { to: to.into(), from });
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
succ!();
|
succ!();
|
||||||
|
|
@ -42,18 +42,23 @@ impl Actor for DisplaceDo {
|
||||||
const NAME: &str = "displace_do";
|
const NAME: &str = "displace_do";
|
||||||
|
|
||||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
if !opt.to.is_absolute() {
|
|
||||||
bail!("Target URL must be absolute");
|
|
||||||
}
|
|
||||||
|
|
||||||
if cx.cwd() != opt.from {
|
if cx.cwd() != opt.from {
|
||||||
succ!()
|
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()
|
} else if let Some(hovered) = cx.hovered()
|
||||||
&& let Ok(url) = opt.to.try_join(hovered.urn())
|
&& let Ok(url) = to.try_join(hovered.urn())
|
||||||
{
|
{
|
||||||
act!(mgr:reveal, cx, (url, CdSource::Displace))
|
act!(mgr:reveal, cx, (url, CdSource::Displace))
|
||||||
} else {
|
} else {
|
||||||
act!(mgr:cd, cx, (opt.to, CdSource::Displace))
|
act!(mgr:cd, cx, (to, CdSource::Displace))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ impl Adapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Adapter {
|
impl Adapter {
|
||||||
pub fn matches(emulator: Emulator) -> Self {
|
pub fn matches(emulator: &Emulator) -> Self {
|
||||||
let mut protocols = emulator.adapters().to_owned();
|
let mut protocols = emulator.adapters().to_owned();
|
||||||
if env_exists("ZELLIJ_SESSION_NAME") {
|
if env_exists("ZELLIJ_SESSION_NAME") {
|
||||||
protocols.retain(|p| *p == Self::Sixel);
|
protocols.retain(|p| *p == Self::Sixel);
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ impl Dimension {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cell_size() -> Option<(f64, f64)> {
|
pub fn cell_size() -> Option<(f64, f64)> {
|
||||||
let emu = EMULATOR.get();
|
let emu = &*EMULATOR;
|
||||||
Some(if emu.force_16t {
|
Some(if emu.force_16t {
|
||||||
(emu.csi_16t.0 as f64, emu.csi_16t.1 as f64)
|
(emu.csi_16t.0 as f64, emu.csi_16t.1 as f64)
|
||||||
} else if let Some(r) = Self::available().ratio() {
|
} else if let Some(r) = Self::available().ratio() {
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,25 @@ use yazi_term::tty::{Handle, TTY};
|
||||||
|
|
||||||
use crate::{Adapter, Brand, Dimension, Mux, TMUX, Unknown};
|
use crate::{Adapter, Brand, Dimension, Mux, TMUX, Unknown};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Emulator {
|
pub struct Emulator {
|
||||||
pub kind: Either<Brand, Unknown>,
|
pub kind: Either<Brand, Unknown>,
|
||||||
|
pub version: String,
|
||||||
pub light: bool,
|
pub light: bool,
|
||||||
pub csi_16t: (u16, u16),
|
pub csi_16t: (u16, u16),
|
||||||
pub force_16t: bool,
|
pub force_16t: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Emulator {
|
impl Default for Emulator {
|
||||||
fn default() -> Self { Self::unknown() }
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
kind: Either::Right(Unknown::default()),
|
||||||
|
version: String::new(),
|
||||||
|
light: false,
|
||||||
|
csi_16t: (0, 0),
|
||||||
|
force_16t: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Emulator {
|
impl Emulator {
|
||||||
|
|
@ -60,22 +69,14 @@ impl Emulator {
|
||||||
let csi_16t = Self::csi_16t(&resp).unwrap_or_default();
|
let csi_16t = Self::csi_16t(&resp).unwrap_or_default();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
kind,
|
kind,
|
||||||
|
version: Self::csi_gt_q(&resp).unwrap_or_default(),
|
||||||
light: Self::light_bg(&resp).unwrap_or_default(),
|
light: Self::light_bg(&resp).unwrap_or_default(),
|
||||||
csi_16t,
|
csi_16t,
|
||||||
force_16t: Self::force_16t(csi_16t),
|
force_16t: Self::force_16t(csi_16t),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn unknown() -> Self {
|
pub fn adapters(&self) -> &'static [Adapter] {
|
||||||
Self {
|
|
||||||
kind: Either::Right(Unknown::default()),
|
|
||||||
light: false,
|
|
||||||
csi_16t: (0, 0),
|
|
||||||
force_16t: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn adapters(self) -> &'static [Adapter] {
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Either::Left(brand) => brand.adapters(),
|
Either::Left(brand) => brand.adapters(),
|
||||||
Either::Right(unknown) => unknown.adapters(),
|
Either::Right(unknown) => unknown.adapters(),
|
||||||
|
|
@ -182,6 +183,11 @@ impl Emulator {
|
||||||
Some((w.parse().ok()?, h.parse().ok()?))
|
Some((w.parse().ok()?, h.parse().ok()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn csi_gt_q(resp: &str) -> Option<String> {
|
||||||
|
let (_, s) = resp.split_once("\x1bP>|")?;
|
||||||
|
Some(s[..s.find("\x1b\\")?].to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
fn light_bg(resp: &str) -> Result<bool> {
|
fn light_bg(resp: &str) -> Result<bool> {
|
||||||
match resp.split_once("]11;rgb:") {
|
match resp.split_once("]11;rgb:") {
|
||||||
Some((_, s)) if s.len() >= 14 => {
|
Some((_, s)) if s.len() >= 14 => {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ yazi_macro::mod_pub!(drivers);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown);
|
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown);
|
||||||
|
|
||||||
use yazi_shared::{SyncCell, in_wsl};
|
use yazi_shared::{RoCell, SyncCell, in_wsl};
|
||||||
|
|
||||||
pub static EMULATOR: SyncCell<Emulator> = SyncCell::new(Emulator::unknown());
|
pub static EMULATOR: RoCell<Emulator> = RoCell::new();
|
||||||
pub static ADAPTOR: SyncCell<Adapter> = SyncCell::new(Adapter::Chafa);
|
pub static ADAPTOR: SyncCell<Adapter> = SyncCell::new(Adapter::Chafa);
|
||||||
|
|
||||||
// Image state
|
// Image state
|
||||||
|
|
@ -24,8 +24,8 @@ pub fn init() -> anyhow::Result<()> {
|
||||||
WSL.set(in_wsl());
|
WSL.set(in_wsl());
|
||||||
|
|
||||||
// Emulator detection
|
// Emulator detection
|
||||||
EMULATOR.set(Emulator::detect().unwrap_or_default());
|
let mut emulator = Emulator::detect().unwrap_or_default();
|
||||||
TMUX.set(EMULATOR.get().kind.is_left_and(|&b| b == Brand::Tmux));
|
TMUX.set(emulator.kind.is_left_and(|&b| b == Brand::Tmux));
|
||||||
|
|
||||||
// Tmux support
|
// Tmux support
|
||||||
if TMUX.get() {
|
if TMUX.get() {
|
||||||
|
|
@ -33,12 +33,13 @@ pub fn init() -> anyhow::Result<()> {
|
||||||
START.set("\x1bPtmux;\x1b\x1b");
|
START.set("\x1bPtmux;\x1b\x1b");
|
||||||
CLOSE.set("\x1b\\");
|
CLOSE.set("\x1b\\");
|
||||||
Mux::tmux_passthrough();
|
Mux::tmux_passthrough();
|
||||||
EMULATOR.set(Emulator::detect().unwrap_or_default());
|
emulator = Emulator::detect().unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
yazi_config::init_flavor(EMULATOR.get().light)?;
|
EMULATOR.init(emulator);
|
||||||
|
yazi_config::init_flavor(EMULATOR.light)?;
|
||||||
|
|
||||||
ADAPTOR.set(Adapter::matches(EMULATOR.get()));
|
ADAPTOR.set(Adapter::matches(&EMULATOR));
|
||||||
ADAPTOR.get().start();
|
ADAPTOR.get().start();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
use crate::Adapter;
|
use crate::Adapter;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct Unknown {
|
pub struct Unknown {
|
||||||
pub kgp: bool,
|
pub kgp: bool,
|
||||||
pub sixel: bool,
|
pub sixel: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Unknown {
|
impl Unknown {
|
||||||
pub(super) const fn default() -> Self { Self { kgp: false, sixel: false } }
|
|
||||||
|
|
||||||
pub(super) fn adapters(self) -> &'static [Adapter] {
|
pub(super) fn adapters(self) -> &'static [Adapter] {
|
||||||
use Adapter as A;
|
use Adapter as A;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use std::{env, ffi::OsStr, fmt::Write, path::Path};
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use yazi_adapter::Mux;
|
use yazi_adapter::Mux;
|
||||||
use yazi_config::YAZI;
|
use yazi_config::{THEME, YAZI};
|
||||||
|
use yazi_fs::Xdg;
|
||||||
use yazi_shared::timestamp_us;
|
use yazi_shared::timestamp_us;
|
||||||
|
|
||||||
use super::Actions;
|
use super::Actions;
|
||||||
|
|
@ -19,12 +20,20 @@ impl Actions {
|
||||||
writeln!(s, "\nYa")?;
|
writeln!(s, "\nYa")?;
|
||||||
writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?;
|
writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?;
|
||||||
|
|
||||||
|
writeln!(s, "\nConfig")?;
|
||||||
|
writeln!(s, " Yazi : {}", Self::config_state("yazi"))?;
|
||||||
|
writeln!(s, " Keymap : {}", Self::config_state("keymap"))?;
|
||||||
|
writeln!(s, " Theme : {}", Self::config_state("theme"))?;
|
||||||
|
writeln!(s, " VFS : {}", Self::config_state("vfs"))?;
|
||||||
|
writeln!(s, " Package : {}", Self::config_state("package"))?;
|
||||||
|
writeln!(s, " Dark/light flavor: {:?} / {:?}", THEME.flavor.dark, THEME.flavor.light)?;
|
||||||
|
|
||||||
writeln!(s, "\nEmulator")?;
|
writeln!(s, "\nEmulator")?;
|
||||||
writeln!(s, " TERM : {:?}", env::var_os("TERM"))?;
|
writeln!(s, " TERM : {:?}", env::var_os("TERM"))?;
|
||||||
writeln!(s, " TERM_PROGRAM : {:?}", env::var_os("TERM_PROGRAM"))?;
|
writeln!(s, " TERM_PROGRAM : {:?}", env::var_os("TERM_PROGRAM"))?;
|
||||||
writeln!(s, " TERM_PROGRAM_VERSION: {:?}", env::var_os("TERM_PROGRAM_VERSION"))?;
|
writeln!(s, " TERM_PROGRAM_VERSION: {:?}", env::var_os("TERM_PROGRAM_VERSION"))?;
|
||||||
writeln!(s, " Brand.from_env : {:?}", yazi_adapter::Brand::from_env())?;
|
writeln!(s, " Brand.from_env : {:?}", yazi_adapter::Brand::from_env())?;
|
||||||
writeln!(s, " Emulator.detect : {:?}", yazi_adapter::EMULATOR)?;
|
writeln!(s, " Emulator.detect : {:?}", &*yazi_adapter::EMULATOR)?;
|
||||||
|
|
||||||
writeln!(s, "\nAdapter")?;
|
writeln!(s, "\nAdapter")?;
|
||||||
writeln!(s, " Adapter.matches : {:?}", yazi_adapter::ADAPTOR)?;
|
writeln!(s, " Adapter.matches : {:?}", yazi_adapter::ADAPTOR)?;
|
||||||
|
|
@ -115,6 +124,16 @@ impl Actions {
|
||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn config_state(name: &str) -> String {
|
||||||
|
let p = Xdg::config_dir().join(format!("{name}.toml"));
|
||||||
|
match std::fs::read_to_string(&p) {
|
||||||
|
Ok(s) if s.is_empty() => format!("{} (empty)", p.display()),
|
||||||
|
Ok(s) if s.trim().is_empty() => format!("{} (whitespaces)", p.display()),
|
||||||
|
Ok(s) => format!("{} ({} chars)", p.display(), s.chars().count()),
|
||||||
|
Err(e) => format!("{} ({e})", p.display()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn process_output(name: impl AsRef<OsStr>, arg: impl AsRef<OsStr>) -> String {
|
fn process_output(name: impl AsRef<OsStr>, arg: impl AsRef<OsStr>) -> String {
|
||||||
match std::process::Command::new(&name).arg(arg).output() {
|
match std::process::Command::new(&name).arg(arg).output() {
|
||||||
Ok(out) if out.status.success() => {
|
Ok(out) if out.status.success() => {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ impl Provider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SFTP
|
// --- SFTP
|
||||||
#[derive(Deserialize, Hash, Serialize, Eq, PartialEq)]
|
#[derive(Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
pub struct ProviderSftp {
|
pub struct ProviderSftp {
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub user: String,
|
pub user: String,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::{io, path::{Path, PathBuf}};
|
use std::{io, path::{Path, PathBuf}};
|
||||||
|
|
||||||
pub async fn must_case_match(path: impl AsRef<Path>) -> bool {
|
pub async fn match_name_case(path: impl AsRef<Path>) -> bool {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
casefold(path).await.is_ok_and(|p| p == path)
|
casefold(path).await.is_ok_and(|p| p.file_name() == path.file_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn casefold(path: impl AsRef<Path>) -> io::Result<PathBuf> {
|
pub(super) async fn casefold(path: impl AsRef<Path>) -> io::Result<PathBuf> {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DisplaceDoOpt {
|
pub struct DisplaceDoOpt {
|
||||||
pub to: UrlBuf,
|
pub to: std::io::Result<UrlBuf>,
|
||||||
pub from: UrlBuf,
|
pub from: UrlBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ impl TryFrom<CmdCow> for UpdateFilesOpt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<FilesOp> for UpdateFilesOpt {
|
||||||
|
fn from(op: FilesOp) -> Self { Self { op } }
|
||||||
|
}
|
||||||
|
|
||||||
impl FromLua for UpdateFilesOpt {
|
impl FromLua for UpdateFilesOpt {
|
||||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ function M:try_with(from, pwd, to)
|
||||||
if not output then
|
if not output then
|
||||||
fail("7zip failed to output when extracting '%s', error: %s", from, err)
|
fail("7zip failed to output when extracting '%s', error: %s", from, err)
|
||||||
elseif output.status.code ~= 0 then
|
elseif output.status.code ~= 0 then
|
||||||
fail("7zip exited when extracting '%s', error code %s", from, output.status.code)
|
fail("7zip exited with error code %s when extracting '%s':\n%s", output.status.code, from, output.stderr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ end
|
||||||
|
|
||||||
function M:spot(job)
|
function M:spot(job)
|
||||||
self.size, self.last = 0, 0
|
self.size, self.last = 0, 0
|
||||||
|
self:spot_multi(job, false)
|
||||||
|
|
||||||
local url = job.file.url
|
local url = job.file.url
|
||||||
local it = fs.calc_size(url)
|
local it = fs.calc_size(url)
|
||||||
|
|
@ -69,15 +70,15 @@ function M:spot(job)
|
||||||
self:spot_multi(job, true)
|
self:spot_multi(job, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M:spot_multi(job, force)
|
function M:spot_multi(job, comp)
|
||||||
local now = ya.time()
|
local now = ya.time()
|
||||||
if not force and now < self.last + 0.1 then
|
if not comp and now < self.last + 0.1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local rows = {
|
local rows = {
|
||||||
ui.Row({ "Folder" }):style(ui.Style():fg("green")),
|
ui.Row({ "Folder" }):style(ui.Style():fg("green")),
|
||||||
ui.Row { " Size:", ya.readable_size(self.size) .. (force and "" or " (?)") },
|
ui.Row { " Size:", ya.readable_size(self.size) .. (comp and "" or " (?)") },
|
||||||
ui.Row {},
|
ui.Row {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use yazi_binding::{Composer, ComposerGet, ComposerSet};
|
||||||
pub(super) fn term() -> Composer<ComposerGet, ComposerSet> {
|
pub(super) fn term() -> Composer<ComposerGet, ComposerSet> {
|
||||||
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
|
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
|
||||||
match key {
|
match key {
|
||||||
b"light" => EMULATOR.get().light.into_lua(lua),
|
b"light" => EMULATOR.light.into_lua(lua),
|
||||||
b"cell_size" => cell_size(lua)?.into_lua(lua),
|
b"cell_size" => cell_size(lua)?.into_lua(lua),
|
||||||
_ => Ok(Value::Nil),
|
_ => Ok(Value::Nil),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ pub(super) fn copy_with_progress_impl(
|
||||||
};
|
};
|
||||||
|
|
||||||
let chunks = (cha.len + 10485760 - 1) / 10485760;
|
let chunks = (cha.len + 10485760 - 1) / 10485760;
|
||||||
let result = futures::stream::iter(0..chunks)
|
let mut result = futures::stream::iter(0..chunks)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let acc_ = acc_.clone();
|
let acc_ = acc_.clone();
|
||||||
let (from, to) = (from.clone(), to.clone());
|
let (from, to) = (from.clone(), to.clone());
|
||||||
|
|
@ -79,24 +79,22 @@ pub(super) fn copy_with_progress_impl(
|
||||||
copied += n as u64;
|
copied += n as u64;
|
||||||
acc_.fetch_add(n as u64, Ordering::SeqCst);
|
acc_.fetch_add(n as u64, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
dist.flush().await?;
|
dist.flush().await?;
|
||||||
if i == chunks - 1 {
|
|
||||||
dist.get_ref().set_attrs(attrs).await.ok();
|
|
||||||
}
|
|
||||||
dist.shutdown().await.ok();
|
|
||||||
|
|
||||||
if copied == take {
|
if copied != take {
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(io::Error::other(format!(
|
Err(io::Error::other(format!(
|
||||||
"short copy for chunk {i}: copied {copied} bytes, expected {take}"
|
"short copy for chunk {i}: copied {copied} bytes, expected {take}"
|
||||||
)))
|
)))
|
||||||
|
} else if i == chunks - 1 {
|
||||||
|
Ok(Some(dist.into_inner()))
|
||||||
|
} else {
|
||||||
|
dist.shutdown().await.ok();
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.buffer_unordered(3)
|
.buffer_unordered(3)
|
||||||
.try_for_each(|_| async { Ok(()) })
|
.try_fold(None, |first, file| async { Ok(first.or(file)) })
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let n = acc_.swap(0, Ordering::SeqCst);
|
let n = acc_.swap(0, Ordering::SeqCst);
|
||||||
|
|
@ -104,6 +102,11 @@ pub(super) fn copy_with_progress_impl(
|
||||||
prog_tx_.send(Ok(n)).await.ok();
|
prog_tx_.send(Ok(n)).await.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Ok(Some(file)) = &mut result {
|
||||||
|
file.set_attrs(attrs).await.ok();
|
||||||
|
file.shutdown().await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
prog_tx_.send(Err(e)).await.ok();
|
prog_tx_.send(Err(e)).await.ok();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -164,9 +164,11 @@ impl Conn {
|
||||||
russh::client::connect(pref, (self.config.host.as_str(), self.config.port), self).await?;
|
russh::client::connect(pref, (self.config.host.as_str(), self.config.port), self).await?;
|
||||||
|
|
||||||
for key in keys {
|
for key in keys {
|
||||||
match session.authenticate_publickey_with(&self.config.user, key, None, &mut agent).await {
|
let hash_alg = session.best_supported_rsa_hash().await?.flatten();
|
||||||
|
match session.authenticate_publickey_with(&self.config.user, key, hash_alg, &mut agent).await
|
||||||
|
{
|
||||||
Ok(result) if result.success() => return Ok(session),
|
Ok(result) if result.success() => return Ok(session),
|
||||||
Ok(_) => {}
|
Ok(result) => tracing::debug!("Identity agent authentication failed: {result:?}"),
|
||||||
Err(e) => tracing::error!("Identity agent authentication error: {e}"),
|
Err(e) => tracing::error!("Identity agent authentication error: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ impl Local {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(p) = file.url.as_local()
|
if let Some(p) = file.url.as_local()
|
||||||
&& !provider::local::must_case_match(p).await
|
&& !provider::local::match_name_case(p).await
|
||||||
{
|
{
|
||||||
ops.push(FilesOp::Deleting(parent.into(), [urn.into()].into()));
|
ops.push(FilesOp::Deleting(parent.into(), [urn.into()].into()));
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue