diff --git a/yazi-config/src/popup/options.rs b/yazi-config/src/popup/options.rs index 9ef592e4..b96050ad 100644 --- a/yazi-config/src/popup/options.rs +++ b/yazi-config/src/popup/options.rs @@ -9,6 +9,7 @@ pub struct InputCfg { pub realtime: bool, pub completion: bool, pub highlight: bool, + pub cursor_at: Option, } #[derive(Default)] @@ -130,6 +131,12 @@ impl InputCfg { self.value = value.into(); self } + + #[inline] + pub fn with_cursor_at(mut self, cursor_at: Option) -> Self { + self.cursor_at = cursor_at; + self + } } impl SelectCfg { diff --git a/yazi-core/src/input/commands/show.rs b/yazi-core/src/input/commands/show.rs index 0ff6aa3a..946482ad 100644 --- a/yazi-core/src/input/commands/show.rs +++ b/yazi-core/src/input/commands/show.rs @@ -41,7 +41,7 @@ impl Input { self.highlight = opt.cfg.highlight; // Reset snaps - self.snaps.reset(opt.cfg.value, self.limit()); + self.snaps.reset(opt.cfg.value, self.limit(), opt.cfg.cursor_at); render!(); } } diff --git a/yazi-core/src/input/snaps.rs b/yazi-core/src/input/snaps.rs index 14b8b444..04fc4ef0 100644 --- a/yazi-core/src/input/snaps.rs +++ b/yazi-core/src/input/snaps.rs @@ -11,11 +11,15 @@ pub(super) struct InputSnaps { impl InputSnaps { #[inline] - pub(super) fn reset(&mut self, value: String, limit: usize) { + pub(super) fn reset(&mut self, value: String, limit: usize, cursor_at: Option) { self.idx = 0; self.versions.clear(); self.versions.push(InputSnap::new(value, limit)); self.current = self.versions[0].clone(); + + if let Some(cursor) = cursor_at { + self.current.cursor = cursor; + } } pub(super) fn tag(&mut self, limit: usize) -> bool { diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 632e6092..4581671c 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -11,10 +11,18 @@ use crate::{input::Input, manager::Manager}; pub struct Opt { force: bool, + replace: bool, + keep_ext: bool, } impl From<&Exec> for Opt { - fn from(e: &Exec) -> Self { Self { force: e.named.contains_key("force") } } + fn from(e: &Exec) -> Self { + Self { + force: e.named.contains_key("force"), + replace: e.named.contains_key("replace"), + keep_ext: e.named.contains_key("keep-ext"), + } + } } impl Manager { @@ -40,8 +48,20 @@ impl Manager { let opt = opt.into() as Opt; tokio::spawn(async move { + let full_name = hovered.file_name().unwrap().to_string_lossy(); + let (initial_value, cursor_start) = if opt.keep_ext { + let last = full_name.split('.').last(); + if last == Some(&full_name) { + ("".into(), None) + } else { + (format!(".{}", last.unwrap()), Some(0)) + } + } else { + (opt.replace.then(|| "").unwrap_or(&full_name).into(), None) + }; + let mut result = - Input::_show(InputCfg::rename().with_value(hovered.file_name().unwrap().to_string_lossy())); + Input::_show(InputCfg::rename().with_value(initial_value).with_cursor_at(cursor_start)); let Some(Ok(name)) = result.recv().await else { return;