From fde563380b45f19da10221e07d5415836313c743 Mon Sep 17 00:00:00 2001 From: Daniel Vincent <93463622+Dan1718@users.noreply.github.com> Date: Mon, 11 May 2026 23:42:08 +0530 Subject: [PATCH] feat: bulk create (#3793) Co-authored-by: sxyazi --- CHANGELOG.md | 5 + README.md | 2 +- yazi-actor/src/mgr/bulk_create.rs | 176 ++++++++++++++++++ yazi-actor/src/mgr/bulk_rename.rs | 4 +- yazi-actor/src/mgr/mod.rs | 1 + yazi-config/preset/keymap-default.toml | 5 +- yazi-config/preset/yazi-default.toml | 4 +- .../src/ember/{bulk.rs => bulk_rename.rs} | 12 +- yazi-dds/src/ember/ember.rs | 12 +- yazi-dds/src/ember/mod.rs | 2 +- yazi-dds/src/payload.rs | 2 +- yazi-dds/src/pubsub.rs | 16 +- yazi-fm/src/executor.rs | 1 + yazi-fs/src/op.rs | 12 ++ yazi-parser/src/spark/spark.rs | 2 + yazi-shim/src/lib.rs | 2 +- yazi-shim/src/path/mod.rs | 1 + yazi-shim/src/path/separator.rs | 5 + yazi-widgets/src/input/actor/complete.rs | 9 +- yazi-widgets/src/input/input.rs | 5 +- yazi-widgets/src/input/mod.rs | 2 +- yazi-widgets/src/input/separator.rs | 5 - 22 files changed, 243 insertions(+), 42 deletions(-) create mode 100644 yazi-actor/src/mgr/bulk_create.rs rename yazi-dds/src/ember/{bulk.rs => bulk_rename.rs} (77%) create mode 100644 yazi-shim/src/path/mod.rs create mode 100644 yazi-shim/src/path/separator.rs delete mode 100644 yazi-widgets/src/input/separator.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index aa01d2bf..4562efa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): ## [Unreleased] +### Added + +- Bulk create ([#3793]) + ## [v26.5.6] ### Added @@ -1704,6 +1708,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3780]: https://github.com/sxyazi/yazi/pull/3780 [#3781]: https://github.com/sxyazi/yazi/pull/3781 [#3792]: https://github.com/sxyazi/yazi/pull/3792 +[#3793]: https://github.com/sxyazi/yazi/pull/3793 [#3804]: https://github.com/sxyazi/yazi/pull/3804 [#3813]: https://github.com/sxyazi/yazi/pull/3813 [#3846]: https://github.com/sxyazi/yazi/pull/3846 diff --git a/README.md b/README.md index 63e6513a..1cd2e097 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo - 🧰 Integration with ripgrep, fd, fzf, zoxide - 💫 Vim-like input/pick/confirm/which/notify component, auto-completion for cd paths - 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, code, directories, etc.) -- 🔄 Bulk Renaming, Archive Extraction, Visual Mode, File Chooser, [Git Integration](https://github.com/yazi-rs/plugins/tree/main/git.yazi), [Mount Manager](https://github.com/yazi-rs/plugins/tree/main/mount.yazi) +- 🔄 Bulk Rename/Create, Archive Extraction, Visual Mode, File Chooser, [Git Integration](https://github.com/yazi-rs/plugins/tree/main/git.yazi), [Mount Manager](https://github.com/yazi-rs/plugins/tree/main/mount.yazi) - 🎨 Theme System, Mouse Support, Trash Bin, Custom Layouts, CSI u, OSC 52 - ... and more! diff --git a/yazi-actor/src/mgr/bulk_create.rs b/yazi-actor/src/mgr/bulk_create.rs new file mode 100644 index 00000000..f3d67491 --- /dev/null +++ b/yazi-actor/src/mgr/bulk_create.rs @@ -0,0 +1,176 @@ +use std::{fmt::{self, Display}, io::{self, Read, Write}, path::{MAIN_SEPARATOR, Path}, sync::Arc}; + +use anyhow::{Result, anyhow}; +use scopeguard::defer; +use yazi_binding::Permit; +use yazi_config::{YAZI, opener::OpenerRule}; +use yazi_fs::{File, FilesOp, Splatter, provider::{Provider, local::Local}}; +use yazi_macro::succ; +use yazi_parser::VoidForm; +use yazi_proxy::TasksProxy; +use yazi_scheduler::{AppProxy, NotifyProxy}; +use yazi_shared::{data::Data, strand::Strand, terminal_clear, url::{AsUrl, UrlBuf, UrlCow, UrlLike}}; +use yazi_shim::path::CROSS_SEPARATOR; +use yazi_term::YIELD_TO_SUBPROCESS; +use yazi_tty::TTY; +use yazi_vfs::{VfsFile, provider}; +use yazi_watcher::WATCHER; + +use crate::{Actor, Ctx}; +pub struct BulkCreate; +impl Actor for BulkCreate { + type Form = VoidForm; + + const NAME: &str = "bulk_create"; + + fn act(cx: &mut Ctx, _: Self::Form) -> Result { + let Some(opener) = Self::opener() else { + succ!(NotifyProxy::push_warn("Bulk create", "No text opener found")); + }; + + let cwd = cx.cwd().clone(); + tokio::spawn(async move { + let tmp = YAZI.preview.tmpfile("bulk-create"); + provider::create_new(&tmp).await?; + + defer! { + let tmp = tmp.clone(); + tokio::spawn(async move { + Local::regular(&tmp).remove_file().await + }); + } + + TasksProxy::process_exec( + cwd.clone(), + Splatter::new(&[UrlCow::default(), tmp.as_url().into()]).splat(&opener.run), + vec![UrlCow::default(), UrlBuf::from(&tmp).into()], + opener.block, + opener.orphan, + ) + .await; + + let _permit = Permit::new(YIELD_TO_SUBPROCESS.acquire().await.unwrap(), AppProxy::resume()); + AppProxy::stop().await; + + let content = Local::regular(&tmp).read_to_string().await?; + Self::r#do(cwd, content.lines().filter_map(Entry::parse).collect()).await + }); + succ!() + } +} + +impl BulkCreate { + async fn r#do(cwd: UrlBuf, todo: Vec>) -> Result<()> { + terminal_clear(TTY.writer())?; + if todo.is_empty() { + return Ok(()); + } else if !Self::ask_continue(&todo, None)? { + return Ok(()); // TODO: support `bulk_exit`? + } + + let _permit = WATCHER.acquire().await.unwrap(); + let (mut failed, mut succeeded) = (vec![], Vec::with_capacity(todo.len())); + for entry in todo { + let Ok(dist) = cwd.try_join(entry.path) else { + failed.push((entry, anyhow!("Invalid path"))); + continue; + }; + + let result: io::Result<()> = if entry.is_dir { + provider::create_dir_all(&dist).await + } else if let Some(parent) = dist.parent() { + provider::create_dir_all(parent).await.ok(); + provider::create_new(&dist).await.map(|_| ()) + } else { + Err(io::Error::other("No parent directory")) + }; + + if let Err(e) = result { + failed.push((entry, e.into())); + } else if let Ok(f) = File::new(dist).await { + succeeded.push(f); + } else { + failed.push((entry, anyhow!("Failed to retrieve file info"))); + } + } + + if !succeeded.is_empty() { + // err!(Pubsub::pub_after_bulk_create(it)); // FIXME + FilesOp::create(succeeded); + } + drop(_permit); + + if !failed.is_empty() { + Self::output_failed(failed).await?; + } + Ok(()) + } + + fn opener() -> Option> { + YAZI + .open + .match_dummy(Path::new("bulk-create.txt"), "text/plain") + .and_then(|r| YAZI.opener.block(&r)) + } + + fn ask_continue(todo: &[Entry], decision: Option) -> Result { + if let Some(decision) = decision { + return Ok(decision); + } + + { + let mut w = TTY.lockout(); + for entry in todo { + writeln!(w, "{entry}")?; + } + write!(w, "Continue to create? (y/N): ")?; + w.flush()?; + } + + let mut buf = [0; 10]; + _ = TTY.reader().read(&mut buf)?; + Ok(buf[0] == b'y' || buf[0] == b'Y') + } + + async fn output_failed(failed: Vec<(Entry<'_>, anyhow::Error)>) -> Result<()> { + let mut stdout = TTY.lockout(); + terminal_clear(&mut *stdout)?; + + writeln!(stdout, "Failed to create:")?; + for (entry, err) in failed { + writeln!(stdout, "{entry}: {err}")?; + } + writeln!(stdout, "\nPress ENTER to exit")?; + + stdout.flush()?; + TTY.reader().read_exact(&mut [0])?; + Ok(()) + } +} + +// --- Entry +struct Entry<'a> { + path: Strand<'a>, + is_dir: bool, +} + +impl<'a> Entry<'a> { + fn parse(s: &'a str) -> Option { + let (path, is_dir) = match s.strip_suffix(CROSS_SEPARATOR) { + Some(p) => (p, true), + None => (s, false), + }; + + Some(Self { path: path.into(), is_dir }).filter(|_| !path.is_empty()) + } +} + +impl Display for Entry<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_dir { + write!(f, "{}{MAIN_SEPARATOR}", self.path.display()) + } else { + self.path.display().fmt(f) + } + } +} diff --git a/yazi-actor/src/mgr/bulk_rename.rs b/yazi-actor/src/mgr/bulk_rename.rs index 0e46be3d..d24cec4c 100644 --- a/yazi-actor/src/mgr/bulk_rename.rs +++ b/yazi-actor/src/mgr/bulk_rename.rs @@ -45,7 +45,7 @@ impl Actor for BulkRename { let cwd = cx.cwd().clone(); let batcher = cx.core.mgr.batcher.clone(); tokio::spawn(async move { - let tmp = YAZI.preview.tmpfile("bulk"); + let tmp = YAZI.preview.tmpfile("bulk-rename"); Gate::default() .write(true) @@ -143,7 +143,7 @@ impl BulkRename { if !succeeded.is_empty() { let it = succeeded.iter().map(|(o, n)| (o.as_url(), n.url.as_url())); - err!(Pubsub::pub_after_bulk(it)); + err!(Pubsub::pub_after_bulk_rename(it)); FilesOp::rename(succeeded); } drop(permit); diff --git a/yazi-actor/src/mgr/mod.rs b/yazi-actor/src/mgr/mod.rs index 5276e8c0..dfbc3aae 100644 --- a/yazi-actor/src/mgr/mod.rs +++ b/yazi-actor/src/mgr/mod.rs @@ -1,6 +1,7 @@ yazi_macro::mod_flat!( arrow back + bulk_create bulk_exit bulk_rename cd diff --git a/yazi-config/preset/keymap-default.toml b/yazi-config/preset/keymap-default.toml index 242e4325..7d621ec0 100644 --- a/yazi-config/preset/keymap-default.toml +++ b/yazi-config/preset/keymap-default.toml @@ -1,5 +1,5 @@ -# A TOML linter such as https://github.com/tombi-toml/tombi can use this schema to validate your config. -# If you encounter any problems, please make an issue at https://github.com/yazi-rs/schemas. +# A TOML linter such as Tombi can use this schema to validate your config. +# If you encounter any problems, please file an issue at https://github.com/yazi-rs/schemas. #:schema https://yazi-rs.github.io/schemas/keymap.json @@ -76,6 +76,7 @@ keymap = [ { on = "d", run = "remove", desc = "Trash selected files" }, { on = "D", run = "remove --permanently", desc = "Permanently delete selected files" }, { on = "a", run = "create", desc = "Create a file (ends with / for directories)" }, + { on = "A", run = "bulk_create", desc = "Bulk create files" }, { on = "r", run = "rename --cursor=before_ext", desc = "Rename selected file(s)" }, { on = ";", run = "shell --interactive", desc = "Run a shell command" }, { on = ":", run = "shell --block --interactive", desc = "Run a shell command (block until finishes)" }, diff --git a/yazi-config/preset/yazi-default.toml b/yazi-config/preset/yazi-default.toml index 80e9726f..1fc65c7f 100644 --- a/yazi-config/preset/yazi-default.toml +++ b/yazi-config/preset/yazi-default.toml @@ -1,5 +1,5 @@ -# A TOML linter such as https://github.com/tombi-toml/tombi can use this schema to validate your config. -# If you encounter any issues, please make an issue at https://github.com/yazi-rs/schemas. +# A TOML linter such as Tombi can use this schema to validate your config. +# If you encounter any problems, please file an issue at https://github.com/yazi-rs/schemas. #:schema https://yazi-rs.github.io/schemas/yazi.json diff --git a/yazi-dds/src/ember/bulk.rs b/yazi-dds/src/ember/bulk_rename.rs similarity index 77% rename from yazi-dds/src/ember/bulk.rs rename to yazi-dds/src/ember/bulk_rename.rs index 93650d14..fedd25bf 100644 --- a/yazi-dds/src/ember/bulk.rs +++ b/yazi-dds/src/ember/bulk_rename.rs @@ -6,11 +6,11 @@ use yazi_shared::url::{Url, UrlCow}; use super::Ember; #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct EmberBulk<'a> { +pub struct EmberBulkRename<'a> { pub changes: HashMap, UrlCow<'a>>, } -impl<'a> EmberBulk<'a> { +impl<'a> EmberBulkRename<'a> { pub fn borrowed(changes: I) -> Ember<'a> where I: Iterator, Url<'a>)>, @@ -19,7 +19,7 @@ impl<'a> EmberBulk<'a> { } } -impl EmberBulk<'static> { +impl EmberBulkRename<'static> { pub fn owned<'a, I>(changes: I) -> Ember<'static> where I: Iterator, Url<'a>)>, @@ -31,11 +31,11 @@ impl EmberBulk<'static> { } } -impl<'a> From> for Ember<'a> { - fn from(value: EmberBulk<'a>) -> Self { Self::Bulk(value) } +impl<'a> From> for Ember<'a> { + fn from(value: EmberBulkRename<'a>) -> Self { Self::BulkRename(value) } } -impl IntoLua for EmberBulk<'_> { +impl IntoLua for EmberBulkRename<'_> { fn into_lua(self, lua: &Lua) -> mlua::Result { lua .create_table_from( diff --git a/yazi-dds/src/ember/ember.rs b/yazi-dds/src/ember/ember.rs index b2325752..aeafaec1 100644 --- a/yazi-dds/src/ember/ember.rs +++ b/yazi-dds/src/ember/ember.rs @@ -2,7 +2,7 @@ use anyhow::{Result, bail}; use mlua::{ExternalResult, IntoLua, Lua, Value}; use yazi_shared::Id; -use super::{EmberBulk, EmberBye, EmberCd, EmberCustom, EmberDelete, EmberDownload, EmberDuplicate, EmberHey, EmberHi, EmberHover, EmberLoad, EmberMount, EmberMove, EmberRename, EmberTab, EmberTrash, EmberYank}; +use super::{EmberBulkRename, EmberBye, EmberCd, EmberCustom, EmberDelete, EmberDownload, EmberDuplicate, EmberHey, EmberHi, EmberHover, EmberLoad, EmberMount, EmberMove, EmberRename, EmberTab, EmberTrash, EmberYank}; use crate::Payload; #[derive(Clone, Debug)] @@ -15,7 +15,7 @@ pub enum Ember<'a> { Load(EmberLoad<'a>), Hover(EmberHover<'a>), Rename(EmberRename<'a>), - Bulk(EmberBulk<'a>), + BulkRename(EmberBulkRename<'a>), Yank(EmberYank<'a>), Duplicate(EmberDuplicate<'a>), Move(EmberMove<'a>), @@ -37,7 +37,7 @@ impl Ember<'static> { "load" => Self::Load(serde_json::from_str(body)?), "hover" => Self::Hover(serde_json::from_str(body)?), "rename" => Self::Rename(serde_json::from_str(body)?), - "bulk" => Self::Bulk(serde_json::from_str(body)?), + "bulk-rename" => Self::BulkRename(serde_json::from_str(body)?), "@yank" => Self::Yank(serde_json::from_str(body)?), "duplicate" => Self::Duplicate(serde_json::from_str(body)?), "move" => Self::Move(serde_json::from_str(body)?), @@ -65,7 +65,7 @@ impl Ember<'static> { | "load" | "hover" | "rename" - | "bulk" + | "bulk-rename" | "@yank" | "duplicate" | "move" @@ -104,7 +104,7 @@ impl<'a> Ember<'a> { Self::Load(_) => "load", Self::Hover(_) => "hover", Self::Rename(_) => "rename", - Self::Bulk(_) => "bulk", + Self::BulkRename(_) => "bulk-rename", Self::Yank(_) => "@yank", Self::Duplicate(_) => "duplicate", Self::Move(_) => "move", @@ -132,7 +132,7 @@ impl<'a> IntoLua for Ember<'a> { Self::Hover(b) => b.into_lua(lua), Self::Tab(b) => b.into_lua(lua), Self::Rename(b) => b.into_lua(lua), - Self::Bulk(b) => b.into_lua(lua), + Self::BulkRename(b) => b.into_lua(lua), Self::Yank(b) => b.into_lua(lua), Self::Duplicate(b) => b.into_lua(lua), Self::Move(b) => b.into_lua(lua), diff --git a/yazi-dds/src/ember/mod.rs b/yazi-dds/src/ember/mod.rs index 28711e9d..6a83139d 100644 --- a/yazi-dds/src/ember/mod.rs +++ b/yazi-dds/src/ember/mod.rs @@ -1,3 +1,3 @@ yazi_macro::mod_flat!( - bulk bye cd custom delete download duplicate ember hey hi hover load mount r#move rename tab trash yank + bulk_rename bye cd custom delete download duplicate ember hey hi hover load mount r#move rename tab trash yank ); diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index 1e72f1a6..5d3ffe77 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -94,7 +94,7 @@ impl Display for Payload<'_> { Ember::Hover(b) => serde_json::to_string(b), Ember::Tab(b) => serde_json::to_string(b), Ember::Rename(b) => serde_json::to_string(b), - Ember::Bulk(b) => serde_json::to_string(b), + Ember::BulkRename(b) => serde_json::to_string(b), Ember::Yank(b) => serde_json::to_string(b), Ember::Duplicate(b) => serde_json::to_string(b), Ember::Move(b) => serde_json::to_string(b), diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index f8daa8d6..ff012795 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -8,7 +8,7 @@ use yazi_fs::FolderStage; use yazi_shared::{Id, url::{Url, UrlBuf, UrlBufCov}}; use yazi_shim::cell::RoCell; -use crate::{Client, ID, PEERS, ember::{Ember, EmberBulk, EmberDuplicateItem, EmberHi, EmberMoveItem}}; +use crate::{Client, ID, PEERS, ember::{Ember, EmberBulkRename, EmberDuplicateItem, EmberHi, EmberMoveItem}}; pub static LOCAL: RoCell>>> = RoCell::new(); @@ -128,18 +128,18 @@ impl Pubsub { true } - pub fn pub_after_bulk<'a, I>(changes: I) -> Result<()> + pub fn pub_after_bulk_rename<'a, I>(changes: I) -> Result<()> where I: Iterator, Url<'a>)> + Clone, { - if BOOT.local_events.contains("bulk") { - EmberBulk::borrowed(changes.clone()).with_receiver(*ID).flush()?; + if BOOT.local_events.contains("bulk-rename") { + EmberBulkRename::borrowed(changes.clone()).with_receiver(*ID).flush()?; } - if PEERS.read().values().any(|p| p.able("bulk")) { - Client::push(EmberBulk::borrowed(changes.clone()))?; + if PEERS.read().values().any(|p| p.able("bulk-rename")) { + Client::push(EmberBulkRename::borrowed(changes.clone()))?; } - if LOCAL.read().contains_key("bulk") { - Self::r#pub(EmberBulk::owned(changes))?; + if LOCAL.read().contains_key("bulk-rename") { + Self::r#pub(EmberBulkRename::owned(changes))?; } Ok(()) } diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 9f9b319b..3a8c461f 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -132,6 +132,7 @@ impl<'a> Executor<'a> { on!(search_do); on!(bulk_exit); on!(bulk_rename); + on!(bulk_create); // Filter on!(filter); diff --git a/yazi-fs/src/op.rs b/yazi-fs/src/op.rs index 779f2ae7..ad4eb92e 100644 --- a/yazi-fs/src/op.rs +++ b/yazi-fs/src/op.rs @@ -51,6 +51,18 @@ impl FilesOp { ticket } + pub fn create(files: Vec) { + let mut parents: HashMap> = Default::default(); + for file in files { + if let Some(p) = file.url.parent() { + parents.get_or_insert_default(p).push(file); + } + } + for (p, files) in parents { + Self::Creating(p, files).emit(); + } + } + pub fn rename(map: HashMap) { let mut parents: HashMap, HashMap<_, _>)> = Default::default(); for (o, n) in map { diff --git a/yazi-parser/src/spark/spark.rs b/yazi-parser/src/spark/spark.rs index 6b82c4db..a6cbc703 100644 --- a/yazi-parser/src/spark/spark.rs +++ b/yazi-parser/src/spark/spark.rs @@ -28,6 +28,7 @@ pub enum Spark<'a> { // Mgr Arrow(crate::ArrowForm), Back(crate::VoidForm), + BulkCreate(crate::VoidForm), BulkExit(crate::mgr::BulkExitForm), BulkRename(crate::VoidForm), Cd(crate::mgr::CdForm), @@ -215,6 +216,7 @@ impl<'a> IntoLua for Spark<'a> { // Mgr Self::Arrow(b) => b.into_lua(lua), Self::Back(b) => b.into_lua(lua), + Self::BulkCreate(b) => b.into_lua(lua), Self::BulkExit(b) => b.into_lua(lua), Self::BulkRename(b) => b.into_lua(lua), Self::Cd(b) => b.into_lua(lua), diff --git a/yazi-shim/src/lib.rs b/yazi-shim/src/lib.rs index db58fed4..cfabeb98 100644 --- a/yazi-shim/src/lib.rs +++ b/yazi-shim/src/lib.rs @@ -1,3 +1,3 @@ -yazi_macro::mod_pub!(arc_swap cell crossterm mlua ratatui serde strum toml vec); +yazi_macro::mod_pub!(arc_swap cell crossterm mlua path ratatui serde strum toml vec); yazi_macro::mod_flat!(twox); diff --git a/yazi-shim/src/path/mod.rs b/yazi-shim/src/path/mod.rs new file mode 100644 index 00000000..2122106c --- /dev/null +++ b/yazi-shim/src/path/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(separator); diff --git a/yazi-shim/src/path/separator.rs b/yazi-shim/src/path/separator.rs new file mode 100644 index 00000000..1b4fa75d --- /dev/null +++ b/yazi-shim/src/path/separator.rs @@ -0,0 +1,5 @@ +#[cfg(windows)] +pub const CROSS_SEPARATOR: [char; 2] = ['/', '\\']; + +#[cfg(not(windows))] +pub const CROSS_SEPARATOR: char = std::path::MAIN_SEPARATOR; diff --git a/yazi-widgets/src/input/actor/complete.rs b/yazi-widgets/src/input/actor/complete.rs index f07c8d0b..00884ecb 100644 --- a/yazi-widgets/src/input/actor/complete.rs +++ b/yazi-widgets/src/input/actor/complete.rs @@ -3,16 +3,17 @@ use std::path::MAIN_SEPARATOR_STR; use anyhow::Result; use yazi_macro::{act, render, succ}; use yazi_shared::data::Data; +use yazi_shim::path::CROSS_SEPARATOR; -use crate::input::{Input, SEPARATOR, parser::CompleteOpt}; +use crate::input::{Input, parser::CompleteOpt}; impl Input { pub fn complete(&mut self, opt: CompleteOpt) -> Result { let (before, after) = self.partition(); - let new = if let Some((prefix, _)) = before.rsplit_once(SEPARATOR) { - format!("{prefix}/{}{after}", opt.completable()).replace(SEPARATOR, MAIN_SEPARATOR_STR) + let new = if let Some((prefix, _)) = before.rsplit_once(CROSS_SEPARATOR) { + format!("{prefix}/{}{after}", opt.completable()).replace(CROSS_SEPARATOR, MAIN_SEPARATOR_STR) } else { - format!("{}{after}", opt.completable()).replace(SEPARATOR, MAIN_SEPARATOR_STR) + format!("{}{after}", opt.completable()).replace(CROSS_SEPARATOR, MAIN_SEPARATOR_STR) }; let snap = self.snap_mut(); diff --git a/yazi-widgets/src/input/input.rs b/yazi-widgets/src/input/input.rs index d62edd0f..936fbda6 100644 --- a/yazi-widgets/src/input/input.rs +++ b/yazi-widgets/src/input/input.rs @@ -6,9 +6,10 @@ use tokio::sync::mpsc; use yazi_config::YAZI; use yazi_macro::act; use yazi_shared::Ids; +use yazi_shim::path::CROSS_SEPARATOR; use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp}; -use crate::{CLIPBOARD, input::{InputEvent, InputOpt, SEPARATOR}}; +use crate::{CLIPBOARD, input::{InputEvent, InputOpt}}; #[derive(Default)] pub struct Input { @@ -152,7 +153,7 @@ impl Input { let snap = self.snap(); let idx = snap.idx(snap.cursor).unwrap(); - if let Some(sep) = snap.value[idx..].find(SEPARATOR).map(|i| idx + i) { + if let Some(sep) = snap.value[idx..].find(CROSS_SEPARATOR).map(|i| idx + i) { (&snap.value[..sep], &snap.value[sep + 1..]) } else { (&snap.value, "") diff --git a/yazi-widgets/src/input/mod.rs b/yazi-widgets/src/input/mod.rs index 67838f22..dde2b4e8 100644 --- a/yazi-widgets/src/input/mod.rs +++ b/yazi-widgets/src/input/mod.rs @@ -1,3 +1,3 @@ yazi_macro::mod_pub!(actor parser); -yazi_macro::mod_flat!(event input mode op option separator snap snaps widget); +yazi_macro::mod_flat!(event input mode op option snap snaps widget); diff --git a/yazi-widgets/src/input/separator.rs b/yazi-widgets/src/input/separator.rs deleted file mode 100644 index 07e410a1..00000000 --- a/yazi-widgets/src/input/separator.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[cfg(windows)] -pub(super) const SEPARATOR: [char; 2] = ['/', '\\']; - -#[cfg(not(windows))] -pub(super) const SEPARATOR: char = std::path::MAIN_SEPARATOR;