feat: choose path separator for copy command

This commit is contained in:
alan910127 2024-11-02 02:50:32 +08:00
parent 0baccdc9aa
commit d4f8649f5d

View file

@ -1,4 +1,7 @@
use std::ffi::{OsStr, OsString};
#[cfg(target_os = "windows")]
use std::path::Component;
use std::path::Path;
use yazi_plugin::CLIPBOARD;
use yazi_shared::event::Cmd;
@ -7,10 +10,13 @@ use crate::tab::Tab;
struct Opt {
type_: String,
separator: PathSeparator,
}
impl From<Cmd> for Opt {
fn from(mut c: Cmd) -> Self { Self { type_: c.take_first_str().unwrap_or_default() } }
fn from(mut c: Cmd) -> Self {
Self { type_: c.take_first_str().unwrap_or_default(), separator: PathSeparator::from(&c) }
}
}
impl Tab {
@ -24,8 +30,8 @@ impl Tab {
let mut it = self.selected_or_hovered(true).peekable();
while let Some(u) = it.next() {
s.push(match opt.type_.as_str() {
"path" => u.as_os_str(),
"dirname" => u.parent().map_or(OsStr::new(""), |p| p.as_os_str()),
"path" => path_to_os_str(u, opt.separator),
"dirname" => u.parent().map_or(OsStr::new(""), |p| path_to_os_str(p, opt.separator)),
"filename" => u.name(),
"name_without_ext" => u.file_stem().unwrap_or(OsStr::new("")),
_ => return,
@ -43,3 +49,103 @@ impl Tab {
futures::executor::block_on(CLIPBOARD.set(s));
}
}
#[derive(Default, Clone, Copy)]
enum PathSeparator {
Unix,
#[default]
Auto,
}
impl From<&Cmd> for PathSeparator {
fn from(c: &Cmd) -> Self {
match c.str("separator") {
Some("unix") => PathSeparator::Unix,
Some("auto") => PathSeparator::Auto,
_ => Default::default(),
}
}
}
#[cfg(not(target_os = "windows"))]
fn path_to_os_str(path: &Path, _separator: PathSeparator) -> &OsStr {
return path.as_os_str();
}
#[cfg(target_os = "windows")]
fn path_to_os_str(path: &Path, separator: PathSeparator) -> &OsStr {
if let PathSeparator::Auto = separator {
return path.as_os_str();
};
let mut s = OsString::new();
for component in path.components() {
match component {
Component::RootDir => {}
Component::CurDir => s.push("."),
Component::ParentDir => s.push(".."),
Component::Normal(path) => s.push(path),
Component::Prefix(prefix) => {
// "C:\foo" => [Prefix("C:"), RootDir, Normal(foo)]
s.push(prefix.as_os_str());
// If we push a "/" below, we will met a RootDir and push a "/"
// again resulting in "C://". So we need to skip that.
continue;
}
};
s.push("/");
}
return s.as_os_str();
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
#[cfg(target_os = "windows")]
#[test]
fn test_path_to_os_str_windows_auto() {
let path = PathBuf::from("C:\\Users\\JohnDoe\\Downloads\\image.png");
assert_eq!(
path_to_os_str(&path, PathSeparator::Auto),
"C:\\Users\\JohnDoe\\Downloads\\image.png",
"windows-auto",
);
}
#[cfg(target_os = "windows")]
#[test]
fn test_path_to_os_str_windows_unix() {
let path = PathBuf::from("C:\\Users\\JohnDoe\\Downloads\\image.png");
assert_eq!(
path_to_os_str(&path, PathSeparator::Unix),
"C:/Users/JohnDow/Downloads/image.png",
"windows-unix",
);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn test_path_to_os_str_unix_auto() {
let path = PathBuf::from("/home/johndoe/Downloads/image.png");
assert_eq!(
path_to_os_str(&path, PathSeparator::Auto),
"/home/johndoe/Downloads/image.png",
"unix-auto"
);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn test_path_to_os_str_unix_unix() {
let path = PathBuf::from("/home/johndoe/Downloads/image.png");
assert_eq!(
path_to_os_str(&path, PathSeparator::Unix),
"/home/johndoe/Downloads/image.png",
"unix-unix"
);
}
}