aboutsummaryrefslogtreecommitdiff
path: root/helix-stdx
diff options
context:
space:
mode:
authorMo2024-02-24 15:59:11 +0000
committerGitHub2024-02-24 15:59:11 +0000
commit6db666fce1fb4627c06d147554b8e1eb9970619e (patch)
tree8ed202acf6936485c33f033e6af5f294dfad0da0 /helix-stdx
parentec9efdef3b2f613a86098058f5705e7863e375e2 (diff)
Optimization of tilde expansion (#9709)
* Use next and avoid a redundant prefix strip * Avoid allocations Especially when `expand_tilde` is claled on a path that doesn't contain a tilde. * Add a test * Use Into<Cow<…>> * Put the expand_tilde test at the end of the file * Remove unused importsw
Diffstat (limited to 'helix-stdx')
-rw-r--r--helix-stdx/src/path.rs59
1 files changed, 47 insertions, 12 deletions
diff --git a/helix-stdx/src/path.rs b/helix-stdx/src/path.rs
index 5746657c..1dc4d0b2 100644
--- a/helix-stdx/src/path.rs
+++ b/helix-stdx/src/path.rs
@@ -1,6 +1,9 @@
pub use etcetera::home_dir;
-use std::path::{Component, Path, PathBuf};
+use std::{
+ borrow::Cow,
+ path::{Component, Path, PathBuf},
+};
use crate::env::current_working_dir;
@@ -19,19 +22,22 @@ pub fn fold_home_dir(path: &Path) -> PathBuf {
/// Expands tilde `~` into users home directory if available, otherwise returns the path
/// unchanged. The tilde will only be expanded when present as the first component of the path
/// and only slash follows it.
-pub fn expand_tilde(path: impl AsRef<Path>) -> PathBuf {
- let path = path.as_ref();
- let mut components = path.components().peekable();
- if let Some(Component::Normal(c)) = components.peek() {
- if c == &"~" {
- if let Ok(home) = home_dir() {
- // it's ok to unwrap, the path starts with `~`
- return home.join(path.strip_prefix("~").unwrap());
+pub fn expand_tilde<'a, P>(path: P) -> Cow<'a, Path>
+where
+ P: Into<Cow<'a, Path>>,
+{
+ let path = path.into();
+ let mut components = path.components();
+ if let Some(Component::Normal(c)) = components.next() {
+ if c == "~" {
+ if let Ok(mut buf) = home_dir() {
+ buf.push(components);
+ return Cow::Owned(buf);
}
}
}
- path.to_path_buf()
+ path
}
/// Normalize a path without resolving symlinks.
@@ -109,9 +115,9 @@ pub fn normalize(path: impl AsRef<Path>) -> PathBuf {
/// This function is used instead of [`std::fs::canonicalize`] because we don't want to verify
/// here if the path exists, just normalize it's components.
pub fn canonicalize(path: impl AsRef<Path>) -> PathBuf {
- let path = expand_tilde(path);
+ let path = expand_tilde(path.as_ref());
let path = if path.is_relative() {
- current_working_dir().join(path)
+ Cow::Owned(current_working_dir().join(path))
} else {
path
};
@@ -183,3 +189,32 @@ pub fn get_truncated_path(path: impl AsRef<Path>) -> PathBuf {
ret.push(file);
ret
}
+
+#[cfg(test)]
+mod tests {
+ use std::{
+ ffi::OsStr,
+ path::{Component, Path},
+ };
+
+ use crate::path;
+
+ #[test]
+ fn expand_tilde() {
+ for path in ["~", "~/foo"] {
+ let expanded = path::expand_tilde(Path::new(path));
+
+ let tilde = Component::Normal(OsStr::new("~"));
+
+ let mut component_count = 0;
+ for component in expanded.components() {
+ // No tilde left.
+ assert_ne!(component, tilde);
+ component_count += 1;
+ }
+
+ // The path was at least expanded to something.
+ assert_ne!(component_count, 0);
+ }
+ }
+}