diff --git a/src/config.rs b/src/config.rs index 0aeda5c..70d031d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -60,3 +60,7 @@ pub fn maxage() -> Result { pub fn resolve_symlinks() -> bool { env::var_os("_ZO_RESOLVE_SYMLINKS").is_some_and(|var| var == "1") } + +pub fn get_home_dir() -> OsString { + env::var_os("HOME").unwrap_or_else(|| "".into()) +} diff --git a/src/db/dir.rs b/src/db/dir.rs index 5d6d62c..0c30af4 100644 --- a/src/db/dir.rs +++ b/src/db/dir.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Display, Formatter}; use serde::{Deserialize, Serialize}; +use crate::config; use crate::util::{DAY, HOUR, WEEK}; #[derive(Clone, Debug, Deserialize, Serialize)] @@ -53,6 +54,27 @@ impl<'a> DirDisplay<'a> { self.separator = separator; self } + + pub fn shorten_home_dir(&self) -> String { + let prefix = config::get_home_dir(); + let prefix = prefix.to_str().expect("cannot convert $HOME to &str"); + let path = if prefix.is_empty() { + self.dir.path.to_string() + } else { + match self.dir.path.as_ref().strip_prefix(prefix) { + Some(path) => format!("~{path}"), + None => self.dir.path.to_string(), + } + }; + + match self.now { + Some(now) => { + let score = self.dir.score(now).clamp(0.0, 9999.0); + format!("{score:>6.1}{}{path}", self.separator) + } + None => path, + } + } } impl Display for DirDisplay<'_> { diff --git a/src/util.rs b/src/util.rs index 4c0a27c..dea4be8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -60,9 +60,9 @@ impl Fzf { self.args([ // Non-POSIX args are only available on certain operating systems. if cfg!(target_os = "linux") { - r"--preview=\command -p ls -Cp --color=always --group-directories-first {2..}" + r#"--preview=sh -c 'dir=$1; case $dir in "~") dir=$HOME ;; "~/"*) dir=$HOME/${dir#"~/"} ;; esac; \command -p ls -Cp --color=always --group-directories-first -- "$dir"' sh {2..}"# } else { - r"--preview=\command -p ls -Cp {2..}" + r#"--preview=sh -c 'dir=$1; case $dir in "~") dir=$HOME ;; "~/"*) dir=$HOME/${dir#"~/"} ;; esac; \command -p ls -Cp -- "$dir"' sh {2..}"# }, // Rounded edges don't display correctly on some terminals. "--preview-window=down,30%,sharp", @@ -123,7 +123,11 @@ pub struct FzfChild(Child); impl FzfChild { pub fn write(&mut self, dir: &Dir, now: Epoch) -> Result> { let handle = self.0.stdin.as_mut().unwrap(); - match write!(handle, "{}\0", dir.display().with_score(now).with_separator('\t')) { + match write!( + handle, + "{}\0", + dir.display().with_score(now).with_separator('\t').shorten_home_dir() + ) { Ok(()) => Ok(None), Err(e) if e.kind() == io::ErrorKind::BrokenPipe => self.wait().map(Some), Err(e) => Err(e).context("could not write to fzf"), @@ -140,7 +144,10 @@ impl FzfChild { let status = self.0.wait().context("wait failed on fzf")?; match status.code() { - Some(0) => Ok(output), + Some(0) => Ok(match output.split_once('\t') { + Some((score, path)) => format!("{score}\t{}", expand_home_dir(path)), + None => expand_home_dir(&output), + }), Some(1) => bail!("no match found"), Some(2) => bail!("fzf returned an error"), Some(130) => bail!(SilentExit { code: 130 }), @@ -150,6 +157,20 @@ impl FzfChild { } } +fn expand_home_dir(path: &str) -> String { + let home = crate::config::get_home_dir(); + if home.is_empty() { + return path.to_string(); + } + + match path.strip_prefix('~') { + Some(path) => { + format!("{}{}", home.to_str().expect("cannot convert OsString to &str"), path) + } + None => path.to_string(), + } +} + /// Similar to [`fs::write`], but atomic (best effort on Windows). pub fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { let path = path.as_ref();