diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6427cc..87368c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: matrix: os: [ubuntu-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: actions-rs/toolchain@v1 @@ -46,7 +46,7 @@ jobs: authToken: ${{ env.CACHIX_AUTH_TOKEN }} name: zoxide - name: Setup cache - uses: Swatinem/rust-cache@v2.7.8 + uses: Swatinem/rust-cache@v2.8.1 with: key: ${{ matrix.os }} - name: Install just diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6a23046..7398a6a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: target: aarch64-pc-windows-msvc steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Get version @@ -59,7 +59,7 @@ jobs: override: true target: ${{ matrix.target }} - name: Setup cache - uses: Swatinem/rust-cache@v2.7.8 + uses: Swatinem/rust-cache@v2.8.1 with: key: ${{ matrix.target }} - name: Install cross @@ -67,7 +67,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: install - args: --color=always --git=https://github.com/cross-rs/cross.git --locked --rev=02bf930e0cb0c6f1beffece0788f3932ecb2c7eb --verbose cross + args: --color=always --git=https://github.com/cross-rs/cross.git --locked --rev=e281947ca900da425e4ecea7483cfde646c8a1ea --verbose cross - name: Build binary uses: actions-rs/cargo@v1 with: @@ -100,7 +100,7 @@ jobs: CHANGELOG.md LICENSE README.md ./man/ ./contrib/completions/ ` ./target/${{ matrix.target }}/release/zoxide.exe - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ matrix.target }} path: | diff --git a/Cargo.lock b/Cargo.lock index 25a01db..732e696 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -335,7 +335,19 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -384,12 +396,6 @@ dependencies = [ "libc", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -545,6 +551,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -572,7 +584,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", ] [[package]] @@ -581,7 +593,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ - "getrandom", + "getrandom 0.2.16", "libredox", "thiserror", ] @@ -623,19 +635,18 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rstest" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "rstest_macros", - "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", @@ -674,19 +685,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys", -] - [[package]] name = "rustix" version = "1.0.7" @@ -696,7 +694,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys", ] @@ -769,15 +767,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.15.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", - "getrandom", + "getrandom 0.3.3", "once_cell", - "rustix 0.38.44", + "rustix", "windows-sys", ] @@ -840,6 +837,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "which" version = "7.0.3" @@ -848,7 +854,7 @@ checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix 1.0.7", + "rustix", "winsafe", ] @@ -940,6 +946,15 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 8e9fb37..58b0d57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,9 +49,9 @@ color-print = "0.3.4" [dev-dependencies] assert_cmd = "2.0.0" -rstest = { version = "0.25.0", default-features = false } +rstest = { version = "0.26.0", default-features = false } rstest_reuse = "0.7.0" -tempfile = "=3.15.0" +tempfile = "3.15.0" [features] default = [] diff --git a/README.md b/README.md index 9ddd8d8..b397f82 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,13 @@ Special thanks to: -
Warp
-
Warp, the intelligent terminal
-
Available for MacOS, Linux, and Windows
+ +
Sponsored by Warp
+
Warp, built for coding with multiple AI agents.
+
Available for macOS, Linux, and Windows.
Visit - warp.dev + warp.dev to learn more.
@@ -62,10 +63,6 @@ z foo # show interactive completions (zoxide v0.8.0+, bash 4.4+/fis Read more about the matching algorithm [here][algorithm-matching]. - - Follow tutorial in Warp - - ## Installation zoxide can be installed in 4 easy steps: @@ -86,35 +83,30 @@ zoxide can be installed in 4 easy steps: > > Or, you can use a package manager: > - > | Distribution | Repository | Instructions | - > | ------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------- | - > | **_Any_** | **[crates.io]** | `cargo install zoxide --locked` | - > | _Any_ | [asdf] | `asdf plugin add zoxide https://github.com/nyrst/asdf-zoxide.git`
`asdf install zoxide latest` | - > | _Any_ | [conda-forge] | `conda install -c conda-forge zoxide` | - > | _Any_ | [guix] | `guix install zoxide` | - > | _Any_ | [Linuxbrew] | `brew install zoxide` | - > | _Any_ | [nixpkgs] | `nix-env -iA nixpkgs.zoxide` | - > | AlmaLinux 8–9[^3] | | `dnf install zoxide` | - > | Alpine Linux 3.13+ | [Alpine Linux Packages] | `apk add zoxide` | - > | Arch Linux | [Arch Linux Extra] | `pacman -S zoxide` | - > | CentOS Stream 8–9 | | `dnf install zoxide` | - > | ~Debian 11+~[^1] | ~[Debian Packages]~ | ~`apt install zoxide`~ | - > | Devuan 4.0+ | [Devuan Packages] | `apt install zoxide` | - > | Exherbo Linux | [Exherbo packages] | `cave resolve -x repository/rust`
`cave resolve -x zoxide` | - > | Fedora 32+ | [Fedora Packages] | `dnf install zoxide` | - > | Gentoo | [Gentoo Packages] | `emerge app-shells/zoxide` | - > | Linux Mint | [apt.cli.rs] (unofficial) | [Setup the repository][apt.cli.rs-setup], then `apt install zoxide` | - > | Manjaro | | `pacman -S zoxide` | - > | openSUSE Tumbleweed | [openSUSE Factory] | `zypper install zoxide` | - > | ~Parrot OS~[^1] | | ~`apt install zoxide`~ | - > | ~Raspbian 11+~[^1] | ~[Raspbian Packages]~ | ~`apt install zoxide`~ | - > | RHEL 8–9[^3] | | `dnf install zoxide` | - > | Rhino Linux | [Pacstall Packages] | `pacstall -I zoxide-deb` | - > | Rocky Linux 8–9[^3] | | `dnf install zoxide` | - > | Slackware 15.0+ | [SlackBuilds] | [Instructions][slackbuilds-howto] | - > | Solus | [Solus Packages] | `eopkg install zoxide` | - > | Ubuntu | [apt.cli.rs] (unofficial) | [Setup the repository][apt.cli.rs-setup], then `apt install zoxide` | - > | Void Linux | [Void Linux Packages] | `xbps-install -S zoxide` | + > | Distribution | Repository | Instructions | + > | ------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------- | + > | **_Any_** | **[crates.io]** | `cargo install zoxide --locked` | + > | _Any_ | [asdf] | `asdf plugin add zoxide https://github.com/nyrst/asdf-zoxide.git`
`asdf install zoxide latest` | + > | _Any_ | [conda-forge] | `conda install -c conda-forge zoxide` | + > | _Any_ | [guix] | `guix install zoxide` | + > | _Any_ | [Linuxbrew] | `brew install zoxide` | + > | _Any_ | [nixpkgs] | `nix-env -iA nixpkgs.zoxide` | + > | Alpine Linux 3.13+ | [Alpine Linux Packages] | `apk add zoxide` | + > | Arch Linux | [Arch Linux Extra] | `pacman -S zoxide` | + > | ~Debian~[^1] | ~[Debian Packages]~ | ~`apt install zoxide`~ | + > | Devuan 4.0+ | [Devuan Packages] | `apt install zoxide` | + > | Exherbo Linux | [Exherbo packages] | `cave resolve -x repository/rust`
`cave resolve -x zoxide` | + > | Fedora 32+ | [Fedora Packages] | `dnf install zoxide` | + > | Gentoo | [Gentoo Packages] | `emerge app-shells/zoxide` | + > | Manjaro | | `pacman -S zoxide` | + > | openSUSE Tumbleweed | [openSUSE Factory] | `zypper install zoxide` | + > | ~Parrot OS~[^1] | | ~`apt install zoxide`~ | + > | ~Raspbian~[^1] | ~[Raspbian Packages]~ | ~`apt install zoxide`~ | + > | Rhino Linux | [Pacstall Packages] | `pacstall -I zoxide-deb` | + > | Slackware 15.0+ | [SlackBuilds] | [Instructions][slackbuilds-howto] | + > | Solus | [Solus Packages] | `eopkg install zoxide` | + > | ~Ubuntu~[^1] | ~[Ubuntu Packages]~ | ~`apt install zoxide`~ | + > | Void Linux | [Void Linux Packages] | `xbps-install -S zoxide` | @@ -474,6 +466,7 @@ Environment variables[^2] can be used for configuration. They must be set before | [lf] | File manager | See the [wiki][lf-wiki] | | [nnn] | File manager | [nnn-autojump] | | [ranger] | File manager | [ranger-zoxide] | +| [raycast] | macOS launcher | [raycast-zoxide] | | [rfm] | File manager | Natively supported | | [sesh] | `tmux` session manager | Natively supported | | [telescope.nvim] | Fuzzy finder for Neovim | [telescope-zoxide] | @@ -488,24 +481,19 @@ Environment variables[^2] can be used for configuration. They must be set before | [zsh-autocomplete] | Realtime completions for zsh | Natively supported | [^1]: - Debian and its derivatives update their packages very slowly. If you're + Debian / Ubuntu derivatives update their packages very slowly. If you're using one of these distributions, consider using the install script instead. [^2]: If you're not sure how to set an environment variable on your shell, check out the [wiki][wiki-env]. -[^3]: - Zoxide won't be branched for EPEL 10 due to lack of maintainer. - [aerc]: https://github.com/rjarry/aerc [alfred]: https://www.alfredapp.com/ [alfred-zoxide]: https://github.com/yihou/alfred-zoxide [algorithm-aging]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#aging [algorithm-matching]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#matching [alpine linux packages]: https://pkgs.alpinelinux.org/packages?name=zoxide -[apt.cli.rs]: https://apt.cli.rs/ -[apt.cli.rs-setup]: https://github.com/emmatyping/apt.cli.rs#how-to-add-the-repo [arch linux extra]: https://archlinux.org/packages/extra/x86_64/zoxide/ [asdf]: https://github.com/asdf-vm/asdf [builtwithnix-badge]: https://img.shields.io/badge/builtwith-nix-7d81f7?logo=nixos&logoColor=white&style=flat-square @@ -548,6 +536,8 @@ Environment variables[^2] can be used for configuration. They must be set before [ranger-zoxide]: https://github.com/jchook/ranger-zoxide [ranger]: https://github.com/ranger/ranger [raspbian packages]: https://archive.raspbian.org/raspbian/pool/main/r/rust-zoxide/ +[raycast]: https://www.raycast.com/ +[raycast-zoxide]: https://www.raycast.com/mrpunkin/raycast-zoxide [releases]: https://github.com/ajeetdsouza/zoxide/releases [rfm]: https://github.com/dsxmachina/rfm [scoop]: https://github.com/ScoopInstaller/Main/tree/master/bucket/zoxide.json @@ -561,6 +551,7 @@ Environment variables[^2] can be used for configuration. They must be set before [tmux-session-wizard]: https://github.com/27medkamal/tmux-session-wizard [tmux-sessionx]: https://github.com/omerxx/tmux-sessionx [tutorial]: contrib/tutorial.webp +[ubuntu packages]: https://packages.ubuntu.com/jammy/zoxide [vim]: https://github.com/vim/vim [void linux packages]: https://github.com/void-linux/void-packages/tree/master/srcpkgs/zoxide [wiki-env]: https://github.com/ajeetdsouza/zoxide/wiki/HOWTO:-set-environment-variables "HOWTO: set environment variables" diff --git a/contrib/completions/_zoxide b/contrib/completions/_zoxide index 33aaace..97e654f 100644 --- a/contrib/completions/_zoxide +++ b/contrib/completions/_zoxide @@ -120,6 +120,7 @@ _arguments "${_arguments_options[@]}" : \ (query) _arguments "${_arguments_options[@]}" : \ '--exclude=[Exclude the current directory]:path:_files -/' \ +'--base-dir=[Only search within this directory]:path:_files -/' \ '-a[Show unavailable directories]' \ '--all[Show unavailable directories]' \ '(-l --list)-i[Use interactive selection]' \ diff --git a/contrib/completions/_zoxide.ps1 b/contrib/completions/_zoxide.ps1 index a26adda..bb47d3a 100644 --- a/contrib/completions/_zoxide.ps1 +++ b/contrib/completions/_zoxide.ps1 @@ -102,6 +102,7 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock { } 'zoxide;query' { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Exclude the current directory') + [CompletionResult]::new('--base-dir', '--base-dir', [CompletionResultType]::ParameterName, 'Only search within this directory') [CompletionResult]::new('-a', '-a', [CompletionResultType]::ParameterName, 'Show unavailable directories') [CompletionResult]::new('--all', '--all', [CompletionResultType]::ParameterName, 'Show unavailable directories') [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'Use interactive selection') diff --git a/contrib/completions/zoxide.bash b/contrib/completions/zoxide.bash index 1c2ed84..82b174e 100644 --- a/contrib/completions/zoxide.bash +++ b/contrib/completions/zoxide.bash @@ -199,7 +199,7 @@ _zoxide() { return 0 ;; zoxide__query) - opts="-a -i -l -s -h -V --all --interactive --list --score --exclude --help --version [KEYWORDS]..." + opts="-a -i -l -s -h -V --all --interactive --list --score --exclude --base-dir --help --version [KEYWORDS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -212,6 +212,13 @@ _zoxide() { fi return 0 ;; + --base-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; *) COMPREPLY=() ;; diff --git a/contrib/completions/zoxide.elv b/contrib/completions/zoxide.elv index e4cb36b..93c57af 100644 --- a/contrib/completions/zoxide.elv +++ b/contrib/completions/zoxide.elv @@ -90,6 +90,7 @@ set edit:completion:arg-completer[zoxide] = {|@words| } &'zoxide;query'= { cand --exclude 'Exclude the current directory' + cand --base-dir 'Only search within this directory' cand -a 'Show unavailable directories' cand --all 'Show unavailable directories' cand -i 'Use interactive selection' diff --git a/contrib/completions/zoxide.fish b/contrib/completions/zoxide.fish index 85c3c49..3a0bfe7 100644 --- a/contrib/completions/zoxide.fish +++ b/contrib/completions/zoxide.fish @@ -62,6 +62,7 @@ complete -c zoxide -n "__fish_zoxide_using_subcommand init" -l no-cmd -d 'Preven complete -c zoxide -n "__fish_zoxide_using_subcommand init" -s h -l help -d 'Print help' complete -c zoxide -n "__fish_zoxide_using_subcommand init" -s V -l version -d 'Print version' complete -c zoxide -n "__fish_zoxide_using_subcommand query" -l exclude -d 'Exclude the current directory' -r -f -a "(__fish_complete_directories)" +complete -c zoxide -n "__fish_zoxide_using_subcommand query" -l base-dir -d 'Only search within this directory' -r -f -a "(__fish_complete_directories)" complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s a -l all -d 'Show unavailable directories' complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s i -l interactive -d 'Use interactive selection' complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s l -l list -d 'List all matching directories' diff --git a/contrib/completions/zoxide.nu b/contrib/completions/zoxide.nu index 34b3ac0..642908e 100644 --- a/contrib/completions/zoxide.nu +++ b/contrib/completions/zoxide.nu @@ -82,6 +82,7 @@ module completions { --list(-l) # List all matching directories --score(-s) # Print score with results --exclude: path # Exclude the current directory + --base-dir: path # Only search within this directory --help(-h) # Print help --version(-V) # Print version ] diff --git a/contrib/completions/zoxide.ts b/contrib/completions/zoxide.ts index 9e593d0..1e0d404 100644 --- a/contrib/completions/zoxide.ts +++ b/contrib/completions/zoxide.ts @@ -214,6 +214,16 @@ const completion: Fig.Spec = { template: "folders", }, }, + { + name: "--base-dir", + description: "Only search within this directory", + isRepeatable: true, + args: { + name: "base_dir", + isOptional: true, + template: "folders", + }, + }, { name: ["-a", "--all"], description: "Show unavailable directories", diff --git a/contrib/warp-packs-green.png b/contrib/warp-packs-green.png deleted file mode 100644 index f10f0e4..0000000 Binary files a/contrib/warp-packs-green.png and /dev/null differ diff --git a/contrib/warp.png b/contrib/warp.png deleted file mode 100644 index 897cd24..0000000 Binary files a/contrib/warp.png and /dev/null differ diff --git a/src/cmd/cmd.rs b/src/cmd/cmd.rs index d25cda3..7359786 100644 --- a/src/cmd/cmd.rs +++ b/src/cmd/cmd.rs @@ -186,6 +186,10 @@ pub struct Query { /// Exclude the current directory #[clap(long, value_hint = ValueHint::DirPath, value_name = "path")] pub exclude: Option, + + /// Only search within this directory + #[clap(long, value_hint = ValueHint::DirPath, value_name = "path")] + pub base_dir: Option, } /// Remove a directory from the database diff --git a/src/cmd/query.rs b/src/cmd/query.rs index 362d80a..6539c2e 100644 --- a/src/cmd/query.rs +++ b/src/cmd/query.rs @@ -79,7 +79,8 @@ impl Query { fn get_stream<'a>(&self, db: &'a mut Database, now: Epoch) -> Result> { let mut options = StreamOptions::new(now) .with_keywords(self.keywords.iter().map(|s| s.as_str())) - .with_exclude(config::exclude_dirs()?); + .with_exclude(config::exclude_dirs()?) + .with_base_dir(self.base_dir.clone()); if !self.all { let resolve_symlinks = config::resolve_symlinks(); options = options.with_exists(true).with_resolve_symlinks(resolve_symlinks); diff --git a/src/db/mod.rs b/src/db/mod.rs index a19efe9..d459f39 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -183,7 +183,7 @@ impl Database { *self.borrow_dirty() } - pub fn dirs(&self) -> &[Dir] { + pub fn dirs(&self) -> &[Dir<'_>] { self.borrow_dirs() } @@ -203,7 +203,7 @@ impl Database { .context("could not serialize database") } - fn deserialize(bytes: &[u8]) -> Result> { + fn deserialize(bytes: &[u8]) -> Result>> { // Assume a maximum size for the database. This prevents bincode from throwing // strange errors when it encounters invalid data. const MAX_SIZE: u64 = 32 << 20; // 32 MiB diff --git a/src/db/stream.rs b/src/db/stream.rs index 4af7d7a..4b06193 100644 --- a/src/db/stream.rs +++ b/src/db/stream.rs @@ -1,5 +1,6 @@ use std::iter::Rev; use std::ops::Range; +use std::path::Path; use std::{fs, path}; use glob::Pattern; @@ -20,7 +21,7 @@ impl<'a> Stream<'a> { Stream { db, idxs, options } } - pub fn next(&mut self) -> Option<&Dir> { + pub fn next(&mut self) -> Option<&Dir<'_>> { while let Some(idx) = self.idxs.next() { let dir = &self.db.dirs()[idx]; @@ -28,11 +29,16 @@ impl<'a> Stream<'a> { continue; } + if !self.filter_by_base_dir(&dir.path) { + continue; + } + if !self.filter_by_exclude(&dir.path) { self.db.swap_remove(idx); continue; } + // Exists queries are slow, this should always be checked last. if !self.filter_by_exists(&dir.path) { if dir.last_accessed < self.options.ttl { self.db.swap_remove(idx); @@ -47,6 +53,30 @@ impl<'a> Stream<'a> { None } + fn filter_by_base_dir(&self, path: &str) -> bool { + match &self.options.base_dir { + Some(base_dir) => Path::new(path).starts_with(base_dir), + None => true, + } + } + + fn filter_by_exclude(&self, path: &str) -> bool { + !self.options.exclude.iter().any(|pattern| pattern.matches(path)) + } + + fn filter_by_exists(&self, path: &str) -> bool { + if !self.options.exists { + return true; + } + + // The logic here is reversed - if we resolve symlinks when adding entries to + // the database, we should not return symlinks when querying back from + // the database. + let resolver = + if self.options.resolve_symlinks { fs::symlink_metadata } else { fs::metadata }; + resolver(path).map(|metadata| metadata.is_dir()).unwrap_or_default() + } + fn filter_by_keywords(&self, path: &str) -> bool { let (keywords_last, keywords) = match self.options.keywords.split_last() { Some(split) => split, @@ -74,23 +104,6 @@ impl<'a> Stream<'a> { true } - - fn filter_by_exclude(&self, path: &str) -> bool { - !self.options.exclude.iter().any(|pattern| pattern.matches(path)) - } - - fn filter_by_exists(&self, path: &str) -> bool { - if !self.options.exists { - return true; - } - - // The logic here is reversed - if we resolve symlinks when adding entries to - // the database, we should not return symlinks when querying back from - // the database. - let resolver = - if self.options.resolve_symlinks { fs::symlink_metadata } else { fs::metadata }; - resolver(path).map(|metadata| metadata.is_dir()).unwrap_or_default() - } } pub struct StreamOptions { @@ -112,6 +125,10 @@ pub struct StreamOptions { /// Directories that do not exist and haven't been accessed since TTL will /// be lazily removed. ttl: Epoch, + + /// Only return directories within this parent directory + /// Does not check if the path exists + base_dir: Option, } impl StreamOptions { @@ -123,6 +140,7 @@ impl StreamOptions { exists: false, resolve_symlinks: false, ttl: now.saturating_sub(3 * MONTH), + base_dir: None, } } @@ -149,6 +167,11 @@ impl StreamOptions { self.resolve_symlinks = resolve_symlinks; self } + + pub fn with_base_dir(mut self, base_dir: Option) -> Self { + self.base_dir = base_dir; + self + } } #[cfg(test)] diff --git a/src/shell.rs b/src/shell.rs index e77ddd1..37fe1a2 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -97,7 +97,7 @@ mod tests { #[apply(opts)] fn elvish_elvish(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { let opts = Opts { cmd, hook, echo, resolve_symlinks }; - let mut source = String::default(); + let mut source = String::new(); // Filter out lines using edit:*, since those functions are only available in // the interactive editor. diff --git a/src/util.rs b/src/util.rs index f74acaf..996f61d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -135,7 +135,7 @@ impl FzfChild { mem::drop(self.0.stdin.take()); let mut stdout = self.0.stdout.take().unwrap(); - let mut output = String::default(); + let mut output = String::new(); stdout.read_to_string(&mut output).context("failed to read from fzf")?; let status = self.0.wait().context("wait failed on fzf")?; diff --git a/templates/bash.txt b/templates/bash.txt index 84be3a0..8c96deb 100644 --- a/templates/bash.txt +++ b/templates/bash.txt @@ -55,7 +55,12 @@ function __zoxide_hook() { # Initialize hook. if [[ ${PROMPT_COMMAND:=} != *'__zoxide_hook'* ]]; then - PROMPT_COMMAND="__zoxide_hook;${PROMPT_COMMAND#;}" + if [[ "$(declare -p PROMPT_COMMAND 2>&1)" == "declare -a"* ]]; then + PROMPT_COMMAND=(__zoxide_hook "${PROMPT_COMMAND[@]}") + else + # shellcheck disable=SC2178 + PROMPT_COMMAND="__zoxide_hook;${PROMPT_COMMAND#;}" + fi fi {%- endif %} diff --git a/templates/zsh.txt b/templates/zsh.txt index 631251a..80cd428 100644 --- a/templates/zsh.txt +++ b/templates/zsh.txt @@ -84,7 +84,7 @@ function __zoxide_z() { __zoxide_doctor if [[ "$#" -eq 0 ]]; then __zoxide_cd ~ - elif [[ "$#" -eq 1 ]] && { [[ -d "$1" ]] || [[ "$1" = '-' ]] || [[ "$1" =~ ^[-+][0-9]$ ]]; }; then + elif [[ "$#" -eq 1 ]] && { [[ -d "$1" ]] || [[ "$1" = '-' ]] || [[ "$1" =~ ^[-+][0-9]+$ ]]; }; then __zoxide_cd "$1" elif [[ "$#" -eq 2 ]] && [[ "$1" = "--" ]]; then __zoxide_cd "$2"