mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-25 19:03:30 +04:00
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
This commit is contained in:
parent
ec9efdef3b
commit
6db666fce1
@ -53,7 +53,7 @@ fn prioritize_runtime_dirs() -> Vec<PathBuf> {
|
|||||||
rt_dirs.push(conf_rt_dir);
|
rt_dirs.push(conf_rt_dir);
|
||||||
|
|
||||||
if let Ok(dir) = std::env::var("HELIX_RUNTIME") {
|
if let Ok(dir) = std::env::var("HELIX_RUNTIME") {
|
||||||
let dir = path::expand_tilde(dir);
|
let dir = path::expand_tilde(Path::new(&dir));
|
||||||
rt_dirs.push(path::normalize(dir));
|
rt_dirs.push(path::normalize(dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
pub use etcetera::home_dir;
|
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;
|
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
|
/// 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
|
/// unchanged. The tilde will only be expanded when present as the first component of the path
|
||||||
/// and only slash follows it.
|
/// and only slash follows it.
|
||||||
pub fn expand_tilde(path: impl AsRef<Path>) -> PathBuf {
|
pub fn expand_tilde<'a, P>(path: P) -> Cow<'a, Path>
|
||||||
let path = path.as_ref();
|
where
|
||||||
let mut components = path.components().peekable();
|
P: Into<Cow<'a, Path>>,
|
||||||
if let Some(Component::Normal(c)) = components.peek() {
|
{
|
||||||
if c == &"~" {
|
let path = path.into();
|
||||||
if let Ok(home) = home_dir() {
|
let mut components = path.components();
|
||||||
// it's ok to unwrap, the path starts with `~`
|
if let Some(Component::Normal(c)) = components.next() {
|
||||||
return home.join(path.strip_prefix("~").unwrap());
|
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.
|
/// 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
|
/// 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.
|
/// here if the path exists, just normalize it's components.
|
||||||
pub fn canonicalize(path: impl AsRef<Path>) -> PathBuf {
|
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() {
|
let path = if path.is_relative() {
|
||||||
current_working_dir().join(path)
|
Cow::Owned(current_working_dir().join(path))
|
||||||
} else {
|
} else {
|
||||||
path
|
path
|
||||||
};
|
};
|
||||||
@ -183,3 +189,32 @@ pub fn get_truncated_path(path: impl AsRef<Path>) -> PathBuf {
|
|||||||
ret.push(file);
|
ret.push(file);
|
||||||
ret
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -110,14 +110,14 @@ fn open(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
|
|||||||
ensure!(!args.is_empty(), "wrong argument count");
|
ensure!(!args.is_empty(), "wrong argument count");
|
||||||
for arg in args {
|
for arg in args {
|
||||||
let (path, pos) = args::parse_file(arg);
|
let (path, pos) = args::parse_file(arg);
|
||||||
let path = helix_stdx::path::expand_tilde(&path);
|
let path = helix_stdx::path::expand_tilde(path);
|
||||||
// If the path is a directory, open a file picker on that directory and update the status
|
// If the path is a directory, open a file picker on that directory and update the status
|
||||||
// message
|
// message
|
||||||
if let Ok(true) = std::fs::canonicalize(&path).map(|p| p.is_dir()) {
|
if let Ok(true) = std::fs::canonicalize(&path).map(|p| p.is_dir()) {
|
||||||
let callback = async move {
|
let callback = async move {
|
||||||
let call: job::Callback = job::Callback::EditorCompositor(Box::new(
|
let call: job::Callback = job::Callback::EditorCompositor(Box::new(
|
||||||
move |editor: &mut Editor, compositor: &mut Compositor| {
|
move |editor: &mut Editor, compositor: &mut Compositor| {
|
||||||
let picker = ui::file_picker(path, &editor.config());
|
let picker = ui::file_picker(path.into_owned(), &editor.config());
|
||||||
compositor.push(Box::new(overlaid(picker)));
|
compositor.push(Box::new(overlaid(picker)));
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
@ -1078,11 +1078,11 @@ fn change_current_directory(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let dir = helix_stdx::path::expand_tilde(
|
let dir = args
|
||||||
args.first()
|
.first()
|
||||||
.context("target directory not provided")?
|
.context("target directory not provided")?
|
||||||
.as_ref(),
|
.as_ref();
|
||||||
);
|
let dir = helix_stdx::path::expand_tilde(Path::new(dir));
|
||||||
|
|
||||||
helix_stdx::env::set_current_working_dir(dir)?;
|
helix_stdx::env::set_current_working_dir(dir)?;
|
||||||
|
|
||||||
|
@ -428,9 +428,9 @@ fn filename_impl<F>(
|
|||||||
path
|
path
|
||||||
} else {
|
} else {
|
||||||
match path.parent() {
|
match path.parent() {
|
||||||
Some(path) if !path.as_os_str().is_empty() => path.to_path_buf(),
|
Some(path) if !path.as_os_str().is_empty() => Cow::Borrowed(path),
|
||||||
// Path::new("h")'s parent is Some("")...
|
// Path::new("h")'s parent is Some("")...
|
||||||
_ => helix_stdx::env::current_working_dir(),
|
_ => Cow::Owned(helix_stdx::env::current_working_dir()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user