mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 14:51:03 +00:00
feat: new target parameter for create action (#4088)
This commit is contained in:
parent
39527f488f
commit
21550a7eb5
9 changed files with 90 additions and 23 deletions
|
|
@ -66,14 +66,8 @@ impl Actor for Trigger {
|
|||
|
||||
impl Trigger {
|
||||
fn split_url(s: &str) -> Option<(UrlBuf, PathBufDyn)> {
|
||||
let sep = if cfg!(windows) {
|
||||
AnyAsciiChar::new(b"/\\").unwrap()
|
||||
} else {
|
||||
AnyAsciiChar::new(b"/").unwrap()
|
||||
};
|
||||
|
||||
let (scheme, path) = SchemeCow::parse(s.as_bytes()).ok()?;
|
||||
if path.is_empty() && !sep.predicate(s.bytes().last()?) {
|
||||
if path.is_empty() && !AnyAsciiChar::SEP.predicate(s.bytes().last()?) {
|
||||
return None; // We don't complete a `sftp://test`, but `sftp://test/`
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +78,7 @@ impl Trigger {
|
|||
}
|
||||
|
||||
// Child
|
||||
let child = path.rsplit_pred(sep).map_or(path.as_path(), |(_, c)| c).to_owned();
|
||||
let child = path.rsplit_pred(AnyAsciiChar::SEP).map_or(path.as_path(), |(_, c)| c).to_owned();
|
||||
|
||||
// Parent
|
||||
let url = UrlCow::try_from((scheme.clone().zeroed(), path)).ok()?;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
use std::pin::Pin;
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use futures::{Stream, StreamExt};
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
use yazi_config::{YAZI, popup::ConfirmCfg};
|
||||
use yazi_fs::{FilesOp, file::File};
|
||||
use yazi_macro::{input, ok_or_not_found, succ};
|
||||
use yazi_parser::mgr::CreateForm;
|
||||
use yazi_proxy::{ConfirmProxy, MgrProxy};
|
||||
use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}};
|
||||
use yazi_shared::{AnyAsciiChar, BytePredictor, data::Data, strand::{StrandBuf, StrandLike}, url::{UrlBuf, UrlLike}};
|
||||
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
||||
use yazi_watcher::WATCHER;
|
||||
use yazi_widgets::input::InputEvent;
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -18,28 +21,35 @@ impl Actor for Create {
|
|||
|
||||
const NAME: &str = "create";
|
||||
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, CreateForm { target, dir, force }: Self::Form) -> Result<Data> {
|
||||
let cwd = cx.cwd().to_owned();
|
||||
let mut input = input!(cx, YAZI.input.create(form.dir))?;
|
||||
|
||||
let mut target: Pin<Box<dyn Stream<Item = StrandBuf> + Send>> = if target.is_empty() {
|
||||
let input = input!(cx, YAZI.input.create(dir))?;
|
||||
Box::pin(
|
||||
UnboundedReceiverStream::new(input).filter_map(|event| async { event.map(Into::into) }),
|
||||
)
|
||||
} else {
|
||||
Box::pin(tokio_stream::iter(vec![target]))
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
let Some(InputEvent::Submit(name)) = input.recv().await else { return };
|
||||
let Some(name) = target.next().await else { return };
|
||||
if name.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Ok(new) = cwd.try_join(&name) else {
|
||||
return;
|
||||
};
|
||||
let Ok(new) = cwd.try_join(&name) else { return };
|
||||
|
||||
if !form.force
|
||||
if !force
|
||||
&& maybe_exists(&new).await
|
||||
&& !ConfirmProxy::show(ConfirmCfg::overwrite(&new)).await
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ = Self::r#do(new, form.dir || name.ends_with('/') || name.ends_with('\\')).await;
|
||||
let end_sep = AnyAsciiChar::SEP.predicate(*name.encoded_bytes().last().unwrap());
|
||||
_ = Self::r#do(new, dir || end_sep).await;
|
||||
});
|
||||
succ!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use serde::Deserialize;
|
||||
use yazi_shared::event::ActionCow;
|
||||
use yazi_shared::{event::ActionCow, strand::StrandBuf};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateForm {
|
||||
#[serde(alias = "0", default)]
|
||||
pub target: StrandBuf,
|
||||
#[serde(default)]
|
||||
pub dir: bool,
|
||||
pub dir: bool,
|
||||
#[serde(default)]
|
||||
pub force: bool,
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for CreateForm {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ pub trait Utf8BytePredictor {
|
|||
pub struct AnyAsciiChar<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> AnyAsciiChar<'a> {
|
||||
#[cfg(windows)]
|
||||
pub const SEP: Self = Self(b"/\\");
|
||||
#[cfg(not(windows))]
|
||||
pub const SEP: Self = Self(b"/");
|
||||
|
||||
pub fn new(chars: &'a [u8]) -> Option<Self> {
|
||||
if chars.iter().all(|&b| b <= 0x7f) { Some(Self(chars)) } else { None }
|
||||
}
|
||||
|
|
|
|||
40
yazi-shared/src/strand/de.rs
Normal file
40
yazi-shared/src/strand/de.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Deserializer, de::{self, Visitor}};
|
||||
|
||||
use crate::strand::StrandBuf;
|
||||
|
||||
impl<'de> Deserialize<'de> for StrandBuf {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct V;
|
||||
|
||||
impl<'de> Visitor<'de> for V {
|
||||
type Value = StrandBuf;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("a string or bytes")
|
||||
}
|
||||
|
||||
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
|
||||
Ok(StrandBuf::Utf8(v.to_owned()))
|
||||
}
|
||||
|
||||
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
|
||||
Ok(StrandBuf::Utf8(v))
|
||||
}
|
||||
|
||||
fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
|
||||
Ok(StrandBuf::Bytes(v.to_owned()))
|
||||
}
|
||||
|
||||
fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
|
||||
Ok(StrandBuf::Bytes(v))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(V)
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@ pub trait StrandLike: AsStrand {
|
|||
|
||||
fn encoded_bytes(&self) -> &[u8] { self.as_strand().encoded_bytes() }
|
||||
|
||||
fn ends_with(&self, needle: impl AsStrand) -> bool { self.as_strand().ends_with(needle) }
|
||||
|
||||
fn eq_ignore_ascii_case(&self, other: impl AsStrand) -> bool {
|
||||
self.as_strand().eq_ignore_ascii_case(other)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(buf conversion cow error extensions kind like strand view);
|
||||
yazi_macro::mod_flat!(buf conversion cow de error extensions kind like strand view);
|
||||
|
|
|
|||
|
|
@ -156,6 +156,10 @@ impl<'a> Strand<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ends_with(self, needle: impl AsStrand) -> bool {
|
||||
self.encoded_bytes().ends_with(needle.as_strand().encoded_bytes())
|
||||
}
|
||||
|
||||
pub fn eq_ignore_ascii_case(self, other: impl AsStrand) -> bool {
|
||||
self.encoded_bytes().eq_ignore_ascii_case(other.as_strand().encoded_bytes())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,21 @@ pub enum InputEvent {
|
|||
}
|
||||
|
||||
impl InputEvent {
|
||||
pub fn is_submit(&self) -> bool { matches!(self, Self::Submit(_)) }
|
||||
|
||||
pub fn value(&self) -> &str {
|
||||
match self {
|
||||
Self::Submit(v) | Self::Cancel(v) | Self::Type(v) | Self::Trigger(v, _) => v.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_submit(&self) -> bool { matches!(self, Self::Submit(_)) }
|
||||
pub fn map<T, F>(self, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(String) -> T,
|
||||
{
|
||||
match self {
|
||||
Self::Submit(v) => Some(f(v)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue