diff --git a/CHANGELOG.md b/CHANGELOG.md index bd2f8cd..ef4cf2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Fzf: show preview window below results. + ## [0.8.1] - 2021-04-23 ### Changed diff --git a/Cargo.lock b/Cargo.lock index b23d07c..90c577d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -770,6 +770,17 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -832,4 +843,5 @@ dependencies = [ "rstest_reuse", "serde", "tempfile", + "which", ] diff --git a/Cargo.toml b/Cargo.toml index 4ed0ca7..b55aad2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,9 @@ nix = { version = "0.24.1", default-features = false, features = [ "user", ] } +[target.'cfg(windows)'.dependencies] +which = "4.2.5" + [build-dependencies] clap = { version = "3.1.0", features = ["derive"] } clap_complete = "3.1.0" diff --git a/README.md b/README.md index a37c536..82a4ba1 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,7 @@ They must be set before `zoxide init` is called. | [clink] | Improved cmd.exe for Windows | [clink-zoxide] | | [emacs] | Text editor | [zoxide.el] | | [felix] | File manager | Natively supported | +| [lf] | File manager | See the [wiki][lf-wiki] | | [nnn] | File manager | [nnn-autojump] | | [ranger] | File manager | [ranger-zoxide] | | [telescope.nvim] | Fuzzy finder for Neovim | [telescope-zoxide] | @@ -398,6 +399,8 @@ They must be set before `zoxide init` is called. [guru overlay]: https://github.com/gentoo-mirror/guru [homebrew]: https://formulae.brew.sh/formula/zoxide [issues]: https://github.com/ajeetdsouza/zoxide/issues/new +[lf]: https://github.com/gokcehan/lf +[lf-wiki]: https://github.com/gokcehan/lf/wiki/Integrations#zoxide [license-badge]: https://img.shields.io/github/license/ajeetdsouza/zoxide?color=lightgray&style=flat-square [license]: https://github.com/ajeetdsouza/zoxide/blob/main/LICENSE [linuxbrew]: https://formulae.brew.sh/formula-linux/zoxide diff --git a/src/util.rs b/src/util.rs index 2ad0b16..9b62a8e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,7 +7,9 @@ use std::process::Command; use std::process::{Child, ChildStdin, Stdio}; use std::time::SystemTime; -use anyhow::{anyhow, bail, Context, Result}; +#[cfg(windows)] +use anyhow::anyhow; +use anyhow::{bail, Context, Result}; use crate::config; use crate::db::Epoch; @@ -18,15 +20,21 @@ pub struct Fzf { } impl Fzf { - const ERR_NOT_FOUND: &'static str = "could not find fzf, is it installed?"; - pub fn new(multiple: bool) -> Result { - let bin = if cfg!(windows) { "fzf.exe" } else { "fzf" }; - let mut command = get_command(bin).map_err(|_| anyhow!(Self::ERR_NOT_FOUND))?; + const ERR_FZF_NOT_FOUND: &str = "could not find fzf, is it installed?"; + + // On Windows, CreateProcess implicitly searches the current working + // directory for the executable, which is a potential security issue. + // Instead, we resolve the path to the executable and then pass it to + // CreateProcess. + #[cfg(windows)] + let mut command = Command::new(which::which("fzf.exe").map_err(|_| anyhow!(ERR_FZF_NOT_FOUND))?); + #[cfg(not(windows))] + let mut command = Command::new("fzf"); if multiple { command.arg("-m"); } - command.arg("-n2..").stdin(Stdio::piped()).stdout(Stdio::piped()); + command.arg("--nth=2..").stdin(Stdio::piped()).stdout(Stdio::piped()); if let Some(fzf_opts) = config::fzf_opts() { command.env("FZF_DEFAULT_OPTS", fzf_opts); } else { @@ -36,7 +44,7 @@ impl Fzf { // Interface "--keep-right", // Layout - "--height=40%", + "--height=50%", "--info=inline", "--layout=reverse", // Scripting @@ -47,13 +55,13 @@ impl Fzf { ]); if cfg!(unix) { command.env("SHELL", "sh"); - command.arg(r"--preview=\command -p ls -p {2..}"); + command.args(&[r"--preview=\command -p ls -p {2..}", "--preview-window=down"]); } } let child = match command.spawn() { Ok(child) => child, - Err(e) if e.kind() == io::ErrorKind::NotFound => bail!(Self::ERR_NOT_FOUND), + Err(e) if e.kind() == io::ErrorKind::NotFound => bail!(ERR_FZF_NOT_FOUND), Err(e) => Err(e).context("could not launch fzf")?, }; @@ -183,35 +191,6 @@ pub fn current_time() -> Result { Ok(current_time) } -/// Constructs a new [`Command`] for launching the program with the name -/// `program`. -/// -/// On Windows, CreateProcess implicitly searches the current working directory -/// for the executable, which is a potential security issue. Instead, we resolve -/// the path to the executable and then pass it to CreateProcess. -/// -/// On other platforms, this is a no-op. -/// -pub fn get_command>(program: P) -> Result { - let program = program.as_ref(); - if !cfg!(windows) { - return Ok(Command::new(program)); - } - - let paths = env::var_os("PATH").context("PATH environment variable not set")?; - for path in env::split_paths(&paths) { - if path.as_os_str().is_empty() { - continue; - } - let path = path.join(program); - if path.metadata().map_or(false, |m| !m.is_dir()) { - return Ok(Command::new(path)); - } - } - - bail!("executable not found in PATH: {}", program.display()); -} - pub fn path_to_str>(path: &P) -> Result<&str> { let path = path.as_ref(); path.to_str().with_context(|| format!("invalid unicode in path: {}", path.display()))