[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\nxtask = \"run --package xtask --\"\n\n# On Windows MSVC, statically link the C runtime so that the resulting EXE does\n# not depend on the vcruntime DLL.\n[target.'cfg(all(windows, target_env = \"msvc\"))']\nrustflags = [\"-C\", \"target-feature=+crt-static\"]\n"
  },
  {
    "path": ".deepsource.toml",
    "content": "version = 1\n\n[[analyzers]]\nname = \"rust\"\n\n  [analyzers.meta]\n  msrv = \"stable\"\n\n[[analyzers]]\nname = \"shell\""
  },
  {
    "path": ".envrc",
    "content": "use nix\n"
  },
  {
    "path": ".gitattributes",
    "content": "/contrib/completions/* eol=lf linguist-generated=true text\n/contrib/completions/README.md -eol -linguist-generated -text\n/init.fish eol=lf text\n/templates/*.txt eol=lf text\n/zoxide.plugin.zsh eol=lf text\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n- Demonstrating empathy and kindness toward other people\n- Being respectful of differing opinions, viewpoints, and experiences\n- Giving and gracefully accepting constructive feedback\n- Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n- Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n- The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n- Trolling, insulting or derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n98ajeet@gmail.com.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: weekly\n"
  },
  {
    "path": ".github/workflows/cd.yml",
    "content": "name: Continuous Deployment\non:\n  push:\n    tags:\n      - \"v*.*.*\"\njobs:\n  publish-crates-io:\n    name: Publish on crates.io\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout the repository\n        uses: actions/checkout@v6\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n      - name: Publish\n        run: cargo publish --token ${{ secrets.CARGO_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\non:\n  push:\n    branches: [main]\n  pull_request:\n  workflow_dispatch:\nenv:\n  CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}\n  CARGO_INCREMENTAL: 0\n  CARGO_TERM_COLOR: always\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\npermissions:\n  contents: read\njobs:\n  ci:\n    name: ${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest]\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - uses: actions-rs/toolchain@v1\n        if: ${{ matrix.os == 'windows-latest' }}\n        with:\n          components: clippy\n          profile: minimal\n          toolchain: stable\n      - uses: actions-rs/toolchain@v1\n        if: ${{ matrix.os == 'windows-latest' }}\n        with:\n          components: rustfmt\n          profile: minimal\n          toolchain: nightly\n      - uses: cachix/install-nix-action@v31\n        if: ${{ matrix.os != 'windows-latest' }}\n        with:\n          nix_path: nixpkgs=channel:nixos-unstable\n      - uses: cachix/cachix-action@v16\n        if: ${{ matrix.os != 'windows-latest' && env.CACHIX_AUTH_TOKEN != '' }}\n        with:\n          authToken: ${{ env.CACHIX_AUTH_TOKEN }}\n          name: zoxide\n      - name: Setup cache\n        uses: Swatinem/rust-cache@v2.9.1\n        with:\n          key: ${{ matrix.os }}\n      - name: Install just\n        uses: taiki-e/install-action@v2\n        with:\n          tool: just\n      - name: Run lints + tests\n        run: just lint test\n"
  },
  {
    "path": ".github/workflows/no-response.yml",
    "content": "name: no-response\non:\n  issue_comment:\n    types: [created]\n  schedule:\n    - cron: \"0 0 * * *\" # daily at 00:00\njobs:\n  no-response:\n    if: github.repository == 'ajeetdsouza/zoxide'\n    permissions:\n      issues: write\n    runs-on: ubuntu-latest\n    steps:\n      - uses: lee-dohm/no-response@v0.5.0\n        with:\n          token: ${{ github.token }}\n          daysUntilClose: 30\n          responseRequiredLabel: waiting-for-response\n          closeComment: >\n            This issue has been automatically closed due to inactivity. If you feel this is still relevant, please comment here or create a fresh issue.\n\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: release\non:\n  push:\n    branches: [main]\n  pull_request:\n  workflow_dispatch:\nenv:\n  CARGO_INCREMENTAL: 0\npermissions:\n  contents: write\njobs:\n  release:\n    name: ${{ matrix.target }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: ubuntu-latest\n            target: x86_64-unknown-linux-musl\n            deb: true\n          - os: ubuntu-latest\n            target: arm-unknown-linux-musleabihf\n          - os: ubuntu-latest\n            target: armv7-unknown-linux-musleabihf\n            deb: true\n          - os: ubuntu-latest\n            target: aarch64-unknown-linux-musl\n            deb: true\n          - os: ubuntu-latest\n            target: i686-unknown-linux-musl\n            deb: true\n          - os: ubuntu-latest\n            target: aarch64-linux-android\n          - os: ubuntu-latest\n            target: armv7-linux-androideabi\n          - os: macos-latest\n            target: x86_64-apple-darwin\n          - os: macos-latest\n            target: aarch64-apple-darwin\n          - os: windows-latest\n            target: x86_64-pc-windows-msvc\n          - os: windows-latest\n            target: aarch64-pc-windows-msvc\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: Get version\n        id: get_version\n        uses: SebRollen/toml-action@v1.2.0\n        with:\n          file: Cargo.toml\n          field: package.version\n      - name: Install Rust\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          profile: minimal\n          override: true\n          target: ${{ matrix.target }}\n      - name: Setup cache\n        uses: Swatinem/rust-cache@v2.9.1\n        with:\n          key: ${{ matrix.target }}\n      - name: Install cross\n        if: ${{ runner.os == 'Linux' }}\n        uses: actions-rs/cargo@v1\n        with:\n          command: install\n          args: --color=always --git=https://github.com/cross-rs/cross.git --locked --rev=e281947ca900da425e4ecea7483cfde646c8a1ea --verbose cross\n      - name: Build binary\n        uses: actions-rs/cargo@v1\n        with:\n          command: build\n          args: --release --locked --target=${{ matrix.target }} --color=always --verbose\n          use-cross: ${{ runner.os == 'Linux' }}\n      - name: Install cargo-deb\n        if: ${{ matrix.deb == true }}\n        uses: actions-rs/install@v0.1\n        with:\n          crate: cargo-deb\n      - name: Build deb\n        if: ${{ matrix.deb == true }}\n        uses: actions-rs/cargo@v1\n        with:\n          command: deb\n          args: --no-build --no-strip --output=. --target=${{ matrix.target }}\n      - name: Package (*nix)\n        if: runner.os != 'Windows'\n        run: |\n          tar -cv CHANGELOG.md LICENSE README.md man/ \\\n            -C contrib/ completions/ -C ../ \\\n            -C target/${{ matrix.target }}/release/ zoxide |\n            gzip --best > \\\n            zoxide-${{ steps.get_version.outputs.value }}-${{ matrix.target }}.tar.gz\n      - name: Package (Windows)\n        if: runner.os == 'Windows'\n        run: |\n          7z a zoxide-${{ steps.get_version.outputs.value }}-${{ matrix.target }}.zip `\n            CHANGELOG.md LICENSE README.md ./man/ ./contrib/completions/ `\n            ./target/${{ matrix.target }}/release/zoxide.exe\n      - name: Upload artifact\n        uses: actions/upload-artifact@v6\n        with:\n          name: ${{ matrix.target }}\n          path: |\n            *.deb\n            *.tar.gz\n            *.zip\n      - name: Create release\n        if: |\n          github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'chore(release)')\n        uses: softprops/action-gh-release@v2\n        with:\n          draft: true\n          files: |\n            *.deb\n            *.tar.gz\n            *.zip\n          name: ${{ steps.get_version.outputs.value }}\n          tag_name: \"\"\n"
  },
  {
    "path": ".github/workflows/winget.yml",
    "content": "name: winget\non:\n  release:\n    types: [released]\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: vedantmgoyal2009/winget-releaser@v2\n        with:\n          identifier: ajeetdsouza.zoxide\n          installers-regex: '-pc-windows-msvc\\.zip$'\n          token: ${{ secrets.WINGET_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "### Custom ###\n.vscode/\n\n### Rust ###\n# Compiled files and executables\ndebug/\ntarget/\ntarget_nix/\n\n# Backup files generated by rustfmt\n**/*.rs.bk\n\n### Python ###\n.mypy_cache/\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "<!-- markdownlint-disable-file MD024 -->\n\n# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## Unreleased\n\n### Added\n\n- POSIX: support for non-Cygwin Windows environments (e.g. Busybox).\n\n### Fixed\n\n- Bash/Fish/POSIX/Zsh: resolve symlinks on Windows.\n\n## [0.9.9] - 2026-01-31\n\n### Added\n\n- Support for Android ARMv7.\n- Fish: support for v4.1.0+.\n\n### Fixed\n\n- Nushell: use sigil operator when calling external commands.\n- Zsh: support multiple digits in `z +N` and `z -N` dirstack commands.\n- Bash: avoid downcasting `$PROMPT_COMMAND` array into a string.\n- Bash: avoid overwriting `$PIPESTATUS`.\n- POSIX: remove non-POSIX compliant calls to `builtin`.\n- Fish: clear existing completions when defining `z` command.\n\n## [0.9.8] - 2025-05-27\n\n### Added\n\n- Support for Tcsh.\n- Added `--score` flag to `zoxide add`.\n- POSIX: add doctor to diagnose common issues.\n- Nushell: add CLI completions.\n\n### Changed\n\n- Bash: zoxide will now automatically `cd` when selecting Space-Tab completions.\n\n### Fixed\n\n- Bash: doctor now handles `$PROMPT_COMMAND` being an array.\n- Bash: doctor now handles Visual Studio Code's shell integration.\n- Bash: completions now work with `ble.sh`.\n- Nushell: stop ignoring symlinks when `cd`-ing into a directory.\n- Fzf: updated minimum supported version to v0.51.0.\n- PowerShell: avoid setting `$error` when defining `__zoxide_hooked`.\n- PowerShell: handle special characters in file paths when `cd`-ing into them.\n- Database corruption issue when the filesystem is 100% full.\n\n## [0.9.7] - 2025-02-10\n\n### Added\n\n- Nushell: support for v0.102.0.\n- Bash / Zsh: add doctor to diagnose common issues.\n\n### Fixed\n\n- ksh: alias to regular POSIX implementation for better compatibility.\n\n## [0.9.6] - 2024-09-19\n\n### Fixed\n\n- Fish: `builtin abbr` doesn't work on older versions.\n- Zsh: make `__zoxide_z_complete` available with `--no-cmd`.\n\n## [0.9.5] - 2024-09-13\n\n### Added\n\n- Zsh: improved `cd` completions.\n- Lazily delete excluded directories from the database.\n- Fish: detect infinite loop when using `alias cd=z`.\n- Installer: added flags for `--bin-dir`, `--man-dir`, `--arch`, and `--sudo`.\n- Nushell: support for v0.94.0+.\n- Bash/Fish/Zsh: support for `z -- dir` style queries.\n- Fish: improved Space-Tab completions.\n- Ksh: added support for the Korn shell.\n\n### Changed\n\n- fzf: removed `--select-1` from default options. The interactive selector will\n  now open up even if there is only one match.\n- Enforce that `$_ZO_DATA_DIR` is an absolute path.\n\n### Fixed\n\n- Zsh: Space-Tab completion repeating output multiple times when matching single\n  directory\n- Fish / Nushell / PowerShell: handle queries that look like args (e.g. `z -x`).\n- Elvish: `z -` now works as expected.\n- Fish: generated shell code avoids using aliased builtins.\n- Fish: `cd` command is now copied directly from\n  `$__fish_data_dir/functions/cd.fish`. This should minimize the chances of an\n  infinite loop when aliasing `cd=z`.\n- Symlinks not getting added to the database when `$_ZO_RESOLVE_SYMLINKS=0`.\n- Symlinked database files getting replaced instead of the actual files.\n\n## [0.9.4] - 2024-02-21\n\n### Changed\n\n- Zsh: improved Space-Tab completions.\n\n## [0.9.3] - 2024-02-13\n\n### Added\n\n- Nushell: support for v0.89.0.\n\n## [0.9.2] - 2023-08-04\n\n### Added\n\n- Short option `-a` for `zoxide query --all`.\n\n### Fixed\n\n- PowerShell: use `global` scope for variables / functions.\n\n## [0.9.1] - 2023-05-07\n\n### Added\n\n- Fish/Zsh: aliases on `__zoxide_z` will now use completions.\n- Nushell: support for v0.78.0.\n- Fish: plugin now works on older versions.\n- PowerShell: warn when PowerShell version is too old for `z -` and `z +`.\n- PowerShell: support for PWD hooks on all versions.\n\n### Fixed\n\n- Fish: not providing `cd` completions when there is a space in the path.\n- Bash/Fish/Zsh: providing `z` completions when the last argument starts with `z!`.\n- Bash/Fish/Zsh: attempting to `cd` when the last argument is `z!`.\n\n## [0.9.0] - 2023-01-08\n\n### Added\n\n- `edit` subcommand to adjust the scores of entries.\n\n### Fixed\n\n- Zsh: completions clashing with `zsh-autocomplete`.\n- Fzf: 'invalid option' on macOS.\n- PowerShell: handle UTF-8 encoding correctly.\n- Zsh: don't hide output from `chpwd` hooks.\n- Nushell: upgrade minimum supported version to v0.73.0.\n- Zsh: fix extra space in interactive completions when no match is found.\n- Fzf: various improvements, upgrade minimum supported version to v0.33.0.\n- Nushell: accidental redefinition of hooks when initialized twice.\n\n### Removed\n\n- `remove -i` subcommand: use `edit` instead.\n\n## [0.8.3] - 2022-09-02\n\n### Added\n\n- Nushell: support for `z -`.\n- Nushell: support for PWD hooks.\n\n### Changed\n\n- Fish: change fuzzy completion prefix to `z!`.\n- Zsh: allow `z` to navigate dirstack via `+n` and `-n`.\n- Fzf: improved preview window.\n\n### Fixed\n\n- Bash: double forward slash in completions.\n\n## [0.8.2] - 2022-06-26\n\n### Changed\n\n- Fzf: show preview window below results.\n\n### Fixed\n\n- Bash/Fish/POSIX/Zsh: paths on Cygwin.\n- Fish: completions not working on certain systems.\n- Bash: completions not escaping spaces correctly.\n\n## [0.8.1] - 2021-04-23\n\n### Changed\n\n- Manpages: moved to `man/man1/*.1`.\n- Replace `--no-aliases` with `--no-cmd`.\n- Elvish: upgrade minimum supported version to v0.18.0.\n- Nushell: upgrade minimum supported version to v0.61.0.\n\n### Fixed\n\n- Bash/Zsh: rename `_z` completion function to avoid conflicts with other shell\n  plugins.\n- Fzf: added `--keep-right` option by default, upgrade minimum supported version\n  to v0.21.0.\n- Bash: only enable completions on 4.4+.\n- Fzf: bypass `ls` alias in preview window.\n- Retain ownership of database file.\n- `zoxide query --interactive` should not conflict with `--score`.\n\n## [0.8.0] - 2021-12-25\n\n### Added\n\n- Zsh: completions for `z` command.\n\n### Changed\n\n- Fzf: better default options.\n- Fish: interactive completions are only triggered when the last argument is\n  empty.\n- PowerShell: installation instructions.\n\n### Fixed\n\n- PowerShell: use global scope for aliases.\n- Zsh: fix errors with `set -eu`.\n- Fzf: handle early selection.\n- PowerShell: correctly handle escape characters in paths.\n- Parse error on Cygwin/MSYS due to CRLF line endings.\n- Fzf: handle spaces correctly in preview window.\n- Bash: avoid initializing completions on older versions.\n- Fzf: avoid launching binary from current directory on Windows.\n\n## [0.7.9] - 2021-11-02\n\n### Changed\n\n- Bash/Fish: improved completions for `z` command.\n\n### Fixed\n\n- Fish: error erasing completions on older versions.\n- PowerShell: enable `--cmd cd` to replace the `cd` command.\n\n## [0.7.8] - 2021-10-21\n\n### Added\n\n- Auto-generated completions for [Fig](https://fig.io/).\n\n### Fixed\n\n- Compile error with `clap v3.0.0-beta.5`.\n\n## [0.7.7] - 2021-10-15\n\n### Fixed\n\n- PowerShell: hook not initializing correctly.\n\n## [0.7.6] - 2021-10-13\n\n### Changed\n\n- Nushell: upgrade minimum supported version to v0.37.0.\n\n### Fixed\n\n- Xonsh: error messages in `zi`.\n- Xonsh: configuration environment variables not being handled correctly.\n\n## [0.7.5] - 2021-09-09\n\n### Added\n\n- Bash/Elvish: completions for `z` command.\n\n### Changed\n\n- Nushell: upgrade minimum supported version to v0.36.0.\n- Nushell: easier installation instructions.\n\n### Fixed\n\n- Elvish: unable to `z` into directories by path.\n- Elvish: don't show traceback when `z` or `zi` fails.\n- Elvish: nested shells do not initialize correctly.\n\n## [0.7.4] - 2021-08-15\n\n### Fixed\n\n- Compile error with `clap v3.0.0-beta.4`.\n\n## [0.7.3] - 2021-08-05\n\n### Added\n\n- `zoxide add` and `zoxide remove` now accept multiple arguments.\n\n### Fixed\n\n- Nushell: errors on 0.33.0.\n- PowerShell: errors when initializing in `StrictMode`.\n- Bash/POSIX: remove conflicting alias definitions when initializing.\n- Bash: remove extra semicolon when setting `$PROMPT_COMMAND`.\n- Xonsh: use shell environment instead of `os.environ`.\n\n## [0.7.2] - 2021-06-10\n\n### Fixed\n\n- `zoxide -V` not printing version.\n\n## [0.7.1] - 2021-06-09\n\n### Added\n\n- Auto-generated shell completions.\n- `zoxide query --all` for listing deleted directories.\n- Lazy deletion for removed directories that have not been accessed in > 90\n  days.\n- Nushell: support for v0.32.0+.\n\n### Fixed\n\n- Nushell: avoid calling `__zoxide_hook` on non-filesystem subshells.\n- Fish: `alias cd=z` now works, but it must be done after calling `zoxide init`.\n- PowerShell: avoid calling `__zoxide_hook` on non-filesystem providers.\n- Fish: avoid calling `__zoxide_hook` in private mode.\n\n## [0.7.0] - 2021-05-02\n\n### Added\n\n- Manpages for all subcommands.\n- Default prompt for Nushell.\n\n### Changed\n\n- `zoxide remove -i` now accepts multiple selections.\n- `zoxide add` no longer accepts zero parameters.\n- `$_ZO_EXCLUDE_DIRS` now defaults to `\"$HOME\"`.\n- Binary releases now use `.zip` on Windows, `.tar.gz` otherwise.\n\n### Fixed\n\n- `cd -` on Fish shells.\n- `__zoxide_hook` no longer changes value of `$?` within `$PROMPT_COMMAND` on\n  Bash.\n\n### Removed\n\n- GitHub install script.\n- Release binaries built with `glibc`, use `musl` instead.\n\n## [0.6.0] - 2021-04-09\n\n### Added\n\n- Support for [Nushell](https://www.nushell.sh/).\n- Support for [Elvish](https://elv.sh/).\n\n### Changed\n\n- `z` now excludes the current directory from search results.\n\n### Fixed\n\n- Removed backtraces on Rust nightly.\n- Generated shell code avoids using aliased builtins.\n- Handle broken pipe errors gracefully when writing to streams.\n- NUL file appearing in working directory on Windows.\n- Accidental redefinition of hooks when initialized twice on some shells.\n- zoxide unable to find itself on Xonsh shells.\n\n### Removed\n\n- Aliases: `za`, `zq`, `zqi`, `zr`, `zri`. These are trivial aliases that can\n  easily be defined manually, and aren't very useful to most users.\n\n## [0.5.0] - 2020-10-30\n\n### Added\n\n- `$_ZO_EXCLUDE_DIRS` now supports globs.\n- `zoxide init` now defines `__zoxide_z*` functions that can be aliased as\n  needed.\n- Support for the [Xonsh](https://xon.sh/) shell.\n- `zoxide import` can now import from Autojump.\n\n### Changed\n\n- `zoxide init --no-aliases` no longer generates `z` or `zi`.\n\n### Fixed\n\n- Clobber conflicting alias definitions in Bash/Fish/Zsh/POSIX shells.\n\n### Removed\n\n- Deprecated PWD hooks for POSIX shells.\n- Lazy deletion for inaccessible directories.\n\n## [0.4.3] - 2020-07-04\n\n### Fixed\n\n- Bug in Fish init script.\n\n## [0.4.2] - 2020-07-03\n\n### Added\n\n- `$_ZO_FZF_OPTS` to specify custom options for `fzf`\n- `zoxide query --list` to list all matches\n- `zoxide query --score` to show score along with result\n\n### Changed\n\n- Increased default value of `$_ZO_MAXAGE` to `10000`.\n- Symlinks are treated as separate directories by default, this can be changed\n  by setting `_ZO_RESOLVE_SYMLINKS=1`.\n\n### Removed\n\n- Help menus for `z` and `zri`.\n- `zoxide remove -i` is replaced with `zri`.\n\n## [0.4.1] - 2020-05-25\n\n### Added\n\n- Support for PowerShell.\n\n### Removed\n\n- Backward compatibility with `v0.2.x` databases.\n- Support for paths with invalid UTF-8.\n\n## [0.4.0] - 2020-05-03\n\n### Added\n\n- Interactive mode for removing entries (`zoxide remove -i`).\n- Aliases for interactive `query` and `remove` (`zqi` and `zri` respectively).\n- PWD hooks for POSIX shells.\n\n### Changed\n\n- `zoxide remove` now throws an error if there was no match in the database.\n- Interactive mode in `zoxide` no longer errors out if `fzf` exits gracefully.\n- Canonicalize to regular paths instead of UNC paths on Windows.\n- `zoxide init` now uses PWD hooks by default for better performance.\n- `$_ZO_ECHO` now only works when set to `1`.\n- Using the `--z-cmd` flag now also renames the associated aliases.\n- The `--z-cmd` flag has been renamed to `--cmd`.\n- The `--no-define-aliases` flag has been renamed to `--no-aliases`.\n\n### Fixed\n\n- Fish no longer `cd`s to the user's home when no match is found.\n\n## [0.3.1] - 2020-04-03\n\n### Added\n\n- Version output displays `git` revision information.\n- `--z-cmd` flag for `zoxide init` to rename the `z` command to something else.\n\n### Changed\n\n- `zoxide query` output no longer has the `query:` prefix.\n\n### Fixed\n\n- Queries now also include checks for if the top level directory matches.\n\n## [0.3.0] - 2020-03-30\n\n### Added\n\n- Automatic migration from `v0.2.x` databases.\n- `$_ZO_EXCLUDE_DIRS` to prevent directories from being added to the database.\n- Support for POSIX-compliant shells.\n\n### Changed\n\n- Database location defaults to user's local data directory.\n- Database schema now includes a version number.\n- `migrate` subcommand renamed to `import`.\n\n### Fixed\n\n- Thread safety using unique tempfile names for each `zoxide` instance.\n- Incomprehensive \"could not allocate\" message on database corruption.\n\n## [0.2.2] - 2020-03-20\n\n### Fixed\n\n- Incorrect exit codes in `z` command on Fish.\n\n### Removed\n\n- File locks on database.\n\n## [0.2.1] - 2020-03-16\n\n### Added\n\n- `$_ZO_ECHO` to echo match before `cd`ing.\n- Minimal `ranger` plugin.\n- PWD hook to only update the database when the current directory is changed.\n- Support for Bash.\n- `migrate` subcommand to allow users to migrate from `z`.\n\n### Fixed\n\n- Interactive queries causing other open shells to hang.\n\n## [0.2.0] - 2020-03-11\n\n### Added\n\n- `init` subcommand to remove dependency on shell plugin managers.\n- Support for `z -` command to go to previous directory.\n- `Cargo.lock` for more reproducible builds.\n- Support for the Fish shell.\n\n### Fixed\n\n- `_zoxide_precmd` overriding other precmd hooks on Zsh.\n\n## [0.1.1] - 2020-03-08\n\n### Added\n\n- Install script for Linux/macOS users.\n- Aging algorithm to remove stale entries.\n\n### Changed\n\n- Database schema now uses `f64` values for rank instead of `i32`.\n\n### Fixed\n\n- Multiple hooks being added upon initializing `zoxide` multiple times.\n\n## [0.1.0] - 2020-03-05\n\n### Added\n\n- GitHub Actions pipeline to build and upload releases.\n- Add support for Zsh.\n\n[0.9.9]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.8...v0.9.9\n[0.9.8]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.7...v0.9.8\n[0.9.7]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.6...v0.9.7\n[0.9.6]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.5...v0.9.6\n[0.9.5]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.4...v0.9.5\n[0.9.4]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.3...v0.9.4\n[0.9.3]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.2...v0.9.3\n[0.9.2]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.1...v0.9.2\n[0.9.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.0...v0.9.1\n[0.9.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.8.3...v0.9.0\n[0.8.3]: https://github.com/ajeetdsouza/zoxide/compare/v0.8.2...v0.8.3\n[0.8.2]: https://github.com/ajeetdsouza/zoxide/compare/v0.8.1...v0.8.2\n[0.8.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.8.0...v0.8.1\n[0.8.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.9...v0.8.0\n[0.7.9]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.8...v0.7.9\n[0.7.8]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.7...v0.7.8\n[0.7.7]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.6...v0.7.7\n[0.7.6]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.5...v0.7.6\n[0.7.5]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.4...v0.7.5\n[0.7.4]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.3...v0.7.4\n[0.7.3]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.2...v0.7.3\n[0.7.2]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.1...v0.7.2\n[0.7.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.0...v0.7.1\n[0.7.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.6.0...v0.7.0\n[0.6.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.5.0...v0.6.0\n[0.5.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.4.3...v0.5.0\n[0.4.3]: https://github.com/ajeetdsouza/zoxide/compare/v0.4.2...v0.4.3\n[0.4.2]: https://github.com/ajeetdsouza/zoxide/compare/v0.4.1...v0.4.2\n[0.4.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.4.0...v0.4.1\n[0.4.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.3.1...v0.4.0\n[0.3.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.3.0...v0.3.1\n[0.3.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.2.2...v0.3.0\n[0.2.2]: https://github.com/ajeetdsouza/zoxide/compare/v0.2.1...v0.2.2\n[0.2.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.2.0...v0.2.1\n[0.2.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.1.1...v0.2.0\n[0.1.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.1.0...v0.1.1\n[0.1.0]: https://github.com/ajeetdsouza/zoxide/commits/v0.1.0\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nauthors = [\"Ajeet D'Souza <98ajeet@gmail.com>\"]\ncategories = [\"command-line-utilities\", \"filesystem\"]\ndescription = \"A smarter cd command for your terminal\"\nedition = \"2024\"\nhomepage = \"https://github.com/ajeetdsouza/zoxide\"\nkeywords = [\"cli\", \"filesystem\", \"shell\", \"tool\", \"utility\"]\nlicense = \"MIT\"\nname = \"zoxide\"\nreadme = \"README.md\"\nrepository = \"https://github.com/ajeetdsouza/zoxide\"\nrust-version = \"1.85.0\"\nversion = \"0.9.9\"\n\n[badges]\nmaintenance = { status = \"actively-developed\" }\n\n[dependencies]\nanyhow = \"1.0.32\"\naskama = { version = \"0.14.0\", default-features = false, features = [\n    \"derive\",\n    \"std\",\n] }\nbincode = \"1.3.1\"\nclap = { version = \"4.3.0\", features = [\"derive\"] }\ncolor-print = \"0.3.4\"\ndirs = \"6.0.0\"\ndunce = \"1.0.1\"\nfastrand = \"2.0.0\"\nglob = \"0.3.0\"\nouroboros = \"0.18.3\"\nserde = { version = \"1.0.116\", features = [\"derive\"] }\n\n[target.'cfg(unix)'.dependencies]\nnix = { version = \"0.30.1\", default-features = false, features = [\n    \"fs\",\n    \"user\",\n] }\n\n[target.'cfg(windows)'.dependencies]\nwhich = \"7.0.3\"\n\n[build-dependencies]\nclap = { version = \"4.3.0\", features = [\"derive\"] }\nclap_complete = \"4.5.50\"\nclap_complete_fig = \"4.5.2\"\nclap_complete_nushell = \"4.5.5\"\ncolor-print = \"0.3.4\"\n\n[dev-dependencies]\nassert_cmd = \"2.0.0\"\nrstest = { version = \"0.26.0\", default-features = false }\nrstest_reuse = \"0.7.0\"\ntempfile = \"3.15.0\"\n\n[features]\ndefault = []\nnix-dev = []\n\n[profile.release]\ncodegen-units = 1\ndebug = 0\nlto = true\nstrip = true\n\n[package.metadata.deb]\nassets = [\n    [\n        \"target/release/zoxide\",\n        \"usr/bin/\",\n        \"755\",\n    ],\n    [\n        \"contrib/completions/zoxide.bash\",\n        \"usr/share/bash-completion/completions/zoxide\",\n        \"644\",\n    ],\n    [\n        \"contrib/completions/zoxide.fish\",\n        \"usr/share/fish/vendor_completions.d/\",\n        \"664\",\n    ],\n    [\n        \"contrib/completions/_zoxide\",\n        \"usr/share/zsh/vendor-completions/\",\n        \"644\",\n    ],\n    [\n        \"man/man1/*\",\n        \"usr/share/man/man1/\",\n        \"644\",\n    ],\n    [\n        \"README.md\",\n        \"usr/share/doc/zoxide/\",\n        \"644\",\n    ],\n    [\n        \"CHANGELOG.md\",\n        \"usr/share/doc/zoxide/\",\n        \"644\",\n    ],\n    [\n        \"LICENSE\",\n        \"usr/share/doc/zoxide/\",\n        \"644\",\n    ],\n]\nextended-description = \"\"\"\\\nzoxide is a smarter cd command, inspired by z and autojump. It remembers which \\\ndirectories you use most frequently, so you can \"jump\" to them in just a few \\\nkeystrokes.\"\"\"\npriority = \"optional\"\nsection = \"utils\"\n"
  },
  {
    "path": "Cross.toml",
    "content": "[build.env]\npassthrough = [\"CARGO_INCREMENTAL\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2020 Ajeet D'Souza\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<!-- markdownlint-configure-file {\n  \"MD013\": {\n    \"code_blocks\": false,\n    \"tables\": false\n  },\n  \"MD033\": false,\n  \"MD041\": false\n} -->\n\n<div align=\"center\">\n\n<sup>Special thanks to:</sup>\n\n<table>\n  <tr>\n    <td align=\"center\">\n      <!-- markdownlint-disable-next-line MD013 -->\n      <a href=\"https://go.warp.dev/zoxide\"><img alt=\"Sponsored by Warp\" width=\"230\" src=\"https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-03.png\" /></a>\n      <div><sup><b>Warp, built for coding with multiple AI agents.</b></sup></div>\n      <div><sup>Available for macOS, Linux, and Windows.</sup></div>\n      <div><sup>\n        Visit\n        <a href=\"https://go.warp.dev/zoxide\"><u>warp.dev</u></a>\n        to learn more.\n      </sup></div>\n    </td>\n    <td align=\"center\">\n      <!-- markdownlint-disable-next-line MD013 -->\n      <a href=\"https://www.recall.ai/careers?ashby_jid=7b02811e-bc91-4ef2-925d-f56a5acac13b&utm_source=github&utm_medium=sponsorship&utm_campaign=zoxide\"><img alt=\"Sponsored by Recall.ai\" width=\"230\" src=\"https://github.com/user-attachments/assets/0c5cb177-561c-4637-bef6-cf584939c829\" /></a>\n      <div><sup>Processing over 3TB/s of video at peak load,</sup></div>\n      <!-- markdownlint-disable-next-line MD013 -->\n      <div><sup>now <a href=\"https://www.recall.ai/careers?ashby_jid=7b02811e-bc91-4ef2-925d-f56a5acac13b&utm_source=github&utm_medium=sponsorship&utm_campaign=zoxide\">hiring in SF.</a></sup></div>\n    </td>\n  </tr>\n</table>\n\n<hr />\n\n# zoxide\n\n[![crates.io][crates.io-badge]][crates.io]\n[![Downloads][downloads-badge]][releases]\n[![Built with Nix][builtwithnix-badge]][builtwithnix]\n\nzoxide is a **smarter cd command**, inspired by z and autojump.\n\nIt remembers which directories you use most frequently, so you can \"jump\" to\nthem in just a few keystrokes.<br />\nzoxide works on all major shells.\n\n[Getting started](#getting-started) •\n[Installation](#installation) •\n[Configuration](#configuration) •\n[Integrations](#third-party-integrations)\n\n</div>\n\n## Getting started\n\n![Tutorial][tutorial]\n\n```sh\nz foo              # cd into highest ranked directory matching foo\nz foo bar          # cd into highest ranked directory matching foo and bar\nz foo /            # cd into a subdirectory starting with foo\n\nz ~/foo            # z also works like a regular cd command\nz foo/             # cd into relative path\nz ..               # cd one level up\nz -                # cd into previous directory\n\nzi foo             # cd with interactive selection (using fzf)\n\nz foo<SPACE><TAB>  # show interactive completions (bash 4.4+/fish/zsh only)\n```\n\nRead more about the matching algorithm [here][algorithm-matching].\n\n## Installation\n\nzoxide can be installed in 4 easy steps:\n\n1. **Install binary**\n\n   zoxide runs on most major platforms. If your platform isn't listed below,\n   please [open an issue][issues].\n\n   <details>\n   <summary>Linux / WSL</summary>\n\n   > The recommended way to install zoxide is via the install script:\n   >\n   > ```sh\n   > curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh\n   > ```\n   >\n   > Or, you can use a package manager:\n   >\n   > | Distribution        | Repository                  | Instructions                                                                                          |\n   > | ------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------- |\n   > | **_Any_**           | **[crates.io]**             | `cargo install zoxide --locked`                                                                       |\n   > | _Any_               | [asdf]                      | `asdf plugin add zoxide https://github.com/nyrst/asdf-zoxide.git` <br /> `asdf install zoxide latest` |\n   > | _Any_               | [conda-forge]               | `conda install -c conda-forge zoxide`                                                                 |\n   > | _Any_               | [guix]                      | `guix install zoxide`                                                                                 |\n   > | _Any_               | [Linuxbrew]                 | `brew install zoxide`                                                                                 |\n   > | _Any_               | [nixpkgs]                   | `nix-env -iA nixpkgs.zoxide`                                                                          |\n   > | Alpine Linux 3.13+  | [Alpine Linux Packages]     | `apk add zoxide`                                                                                      |\n   > | Arch Linux          | [Arch Linux Extra]          | `pacman -S zoxide`                                                                                    |\n   > | ~Debian~[^1]    | ~[Debian Packages]~         | ~`apt install zoxide`~                                                                                    |\n   > | Devuan 4.0+         | [Devuan Packages]           | `apt install zoxide`                                                                                  |\n   > | Exherbo Linux       | [Exherbo packages]          | `cave resolve -x repository/rust` <br /> `cave resolve -x zoxide`                                     |\n   > | Fedora 32+          | [Fedora Packages]           | `dnf install zoxide`                                                                                  |\n   > | Gentoo              | [Gentoo Packages]           | `emerge app-shells/zoxide`                                                                            |\n   > | Manjaro             |                             | `pacman -S zoxide`                                                                                    |\n   > | openSUSE Tumbleweed | [openSUSE Factory]          | `zypper install zoxide`                                                                               |\n   > | ~Parrot OS~[^1]     |                             | ~`apt install zoxide`~                                                                                |\n   > | ~Raspbian~[^1]  | ~[Raspbian Packages]~       | ~`apt install zoxide`~                                                                                    |\n   > | Rhino Linux         | [Pacstall Packages]         | `pacstall -I zoxide-deb`                                                                              |\n   > | Slackware 15.0+     | [SlackBuilds]               | [Instructions][slackbuilds-howto]                                                                     |\n   > | Solus               | [Solus Packages]            | `eopkg install zoxide`                                                                                |\n   > | ~Ubuntu~[^1]        | ~[Ubuntu Packages]~         | ~`apt install zoxide`~                                                                                |\n   > | Void Linux          | [Void Linux Packages]       | `xbps-install -S zoxide`                                                                              |\n\n   </details>\n\n   <details>\n   <summary>macOS</summary>\n\n   > To install zoxide, use a package manager:\n   >\n   > | Repository      | Instructions                                                                                          |\n   > | --------------- | ----------------------------------------------------------------------------------------------------- |\n   > | **[crates.io]** | `cargo install zoxide --locked`                                                                       |\n   > | **[Homebrew]**  | `brew install zoxide`                                                                                 |\n   > | [asdf]          | `asdf plugin add zoxide https://github.com/nyrst/asdf-zoxide.git` <br /> `asdf install zoxide latest` |\n   > | [conda-forge]   | `conda install -c conda-forge zoxide`                                                                 |\n   > | [MacPorts]      | `port install zoxide`                                                                                 |\n   > | [nixpkgs]       | `nix-env -iA nixpkgs.zoxide`                                                                          |\n   >\n   > Or, run this command in your terminal:\n   >\n   > ```sh\n   > curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh\n   > ```\n\n   </details>\n\n   <details>\n   <summary>Windows</summary>\n\n   > zoxide works with PowerShell, as well as shells running in Cygwin, Git\n   > Bash, and MSYS2.\n   >\n   > The recommended way to install zoxide is via `winget`:\n   >\n   > ```sh\n   > winget install ajeetdsouza.zoxide\n   > ```\n   >\n   > Or, you can use an alternative package manager:\n   >\n   > | Repository      | Instructions                          |\n   > | --------------- | ------------------------------------- |\n   > | **[crates.io]** | `cargo install zoxide --locked`       |\n   > | [Chocolatey]    | `choco install zoxide`                |\n   > | [conda-forge]   | `conda install -c conda-forge zoxide` |\n   > | [Scoop]         | `scoop install zoxide`                |\n   >\n   > If you're using Cygwin, Git Bash, or MSYS2, you can also use the install script:\n   >\n   > ```sh\n   > curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh\n   > ```\n\n   </details>\n\n   <details>\n   <summary>BSD</summary>\n\n   > To install zoxide, use a package manager:\n   >\n   > | Distribution  | Repository      | Instructions                    |\n   > | ------------- | --------------- | ------------------------------- |\n   > | **_Any_**     | **[crates.io]** | `cargo install zoxide --locked` |\n   > | DragonFly BSD | [DPorts]        | `pkg install zoxide`            |\n   > | FreeBSD       | [FreshPorts]    | `pkg install zoxide`            |\n   > | NetBSD        | [pkgsrc]        | `pkgin install zoxide`          |\n   >\n   > Or, run this command in your terminal:\n   >\n   > ```sh\n   > curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash\n   > ```\n\n   </details>\n\n   <details>\n   <summary>Android</summary>\n\n   > To install zoxide, use a package manager:\n   >\n   > | Repository | Instructions         |\n   > | ---------- | -------------------- |\n   > | [Termux]   | `pkg install zoxide` |\n   >\n   > Or, run this command in your terminal:\n   >\n   > ```sh\n   > curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash\n   > ```\n\n   </details>\n\n2. **Setup zoxide on your shell**\n\n   To start using zoxide, add it to your shell.\n\n   <details>\n   <summary>Bash</summary>\n\n   > Add this to the <ins>**end**</ins> of your config file (usually `~/.bashrc`):\n   >\n   > ```sh\n   > eval \"$(zoxide init bash)\"\n   > ```\n\n   </details>\n\n   <details>\n   <summary>Elvish</summary>\n\n   > Add this to the <ins>**end**</ins> of your config file (usually `~/.elvish/rc.elv`):\n   >\n   > ```sh\n   > eval (zoxide init elvish | slurp)\n   > ```\n   >\n   > **Note:**\n   > zoxide only supports elvish v0.18.0 and above.\n\n   </details>\n\n   <details>\n   <summary>Fish</summary>\n\n   > Add this to the <ins>**end**</ins> of your config file (usually\n   > `~/.config/fish/config.fish`):\n   >\n   > ```sh\n   > zoxide init fish | source\n   > ```\n\n   </details>\n\n   <details>\n   <summary>Nushell</summary>\n\n   > Add this to the <ins>**end**</ins> of your env file (find it by running `$nu.env-path`\n   > in Nushell):\n   >\n   > ```sh\n   > zoxide init nushell | save -f ~/.zoxide.nu\n   > ```\n   >\n   > Now, add this to the <ins>**end**</ins> of your config file (find it by running\n   > `$nu.config-path` in Nushell):\n   >\n   > ```sh\n   > source ~/.zoxide.nu\n   > ```\n   >\n   > **Note:**\n   > zoxide only supports Nushell v0.89.0+.\n\n   </details>\n\n   <details>\n   <summary>PowerShell</summary>\n\n   > Add this to the <ins>**end**</ins> of your config file (find it by running\n   > `echo $profile` in PowerShell):\n   >\n   > ```powershell\n   > Invoke-Expression (& { (zoxide init powershell | Out-String) })\n   > ```\n\n   </details>\n\n   <details>\n   <summary>Tcsh</summary>\n\n   > Add this to the <ins>**end**</ins> of your config file (usually `~/.tcshrc`):\n   >\n   > ```sh\n   > zoxide init tcsh > ~/.zoxide.tcsh\n   > source ~/.zoxide.tcsh\n   > ```\n\n   </details>\n\n   <details>\n   <summary>Xonsh</summary>\n\n   > Add this to the <ins>**end**</ins> of your config file (usually `~/.xonshrc`):\n   >\n   > ```python\n   > execx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')\n   > ```\n\n   </details>\n\n   <details>\n   <summary>Zsh</summary>\n\n   > Add this to the <ins>**end**</ins> of your config file (usually `~/.zshrc`):\n   >\n   > ```sh\n   > eval \"$(zoxide init zsh)\"\n   > ```\n   >\n   > For completions to work, the above line must be added _after_ `compinit` is\n   > called. You may have to rebuild your completions cache by running\n   > `rm ~/.zcompdump*; compinit`.\n\n   </details>\n\n   <details>\n   <summary>Any POSIX shell</summary>\n\n   > Add this to the <ins>**end**</ins> of your config file:\n   >\n   > ```sh\n   > eval \"$(zoxide init posix --hook prompt)\"\n   > ```\n\n   </details>\n\n   > **Note:**\n   > [Warp] provides its own completions, so `Space+Tab` completions are not\n   > supported there.\n\n3. **Install fzf** <sup>(optional)</sup>\n\n   [fzf] is a command-line fuzzy finder, used by zoxide for completions /\n   interactive selection. It can be installed from [here][fzf-installation].\n\n   > **Note:**\n   > The minimum supported fzf version is v0.51.0.\n\n4. **Import your data** <sup>(optional)</sup>\n\n   If you currently use any of these plugins, you may want to import your data\n   into zoxide:\n\n   <details>\n   <summary>autojump</summary>\n\n   > Run this command in your terminal:\n   >\n   > ```sh\n   > zoxide import --from=autojump \"/path/to/autojump/db\"\n   > ```\n   >\n   > The path usually varies according to your system:\n   >\n   > | OS      | Path                                                                                 | Example                                                |\n   > | ------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------ |\n   > | Linux   | `$XDG_DATA_HOME/autojump/autojump.txt` or `$HOME/.local/share/autojump/autojump.txt` | `/home/alice/.local/share/autojump/autojump.txt`       |\n   > | macOS   | `$HOME/Library/autojump/autojump.txt`                                                | `/Users/Alice/Library/autojump/autojump.txt`           |\n   > | Windows | `%APPDATA%\\autojump\\autojump.txt`                                                    | `C:\\Users\\Alice\\AppData\\Roaming\\autojump\\autojump.txt` |\n\n   </details>\n\n   <details>\n   <summary>fasd, z, z.lua, zsh-z</summary>\n\n   > Run this command in your terminal:\n   >\n   > ```sh\n   > zoxide import --from=z \"path/to/z/db\"\n   > ```\n   >\n   > The path usually varies according to your system:\n   >\n   > | Plugin           | Path                                                                                |\n   > | ---------------- | ----------------------------------------------------------------------------------- |\n   > | fasd             | `$_FASD_DATA` or `$HOME/.fasd`                                                      |\n   > | z (bash/zsh)     | `$_Z_DATA` or `$HOME/.z`                                                            |\n   > | z (fish)         | `$Z_DATA` or `$XDG_DATA_HOME/z/data` or `$HOME/.local/share/z/data`                 |\n   > | z.lua (bash/zsh) | `$_ZL_DATA` or `$HOME/.zlua`                                                        |\n   > | z.lua (fish)     | `$XDG_DATA_HOME/zlua/zlua.txt` or `$HOME/.local/share/zlua/zlua.txt` or `$_ZL_DATA` |\n   > | zsh-z            | `$ZSHZ_DATA` or `$_Z_DATA` or `$HOME/.z`                                            |\n\n   </details>\n\n   <details>\n   <summary>ZLocation</summary>\n\n   > Run this command in PowerShell:\n   >\n   > ```powershell\n   > $db = New-TemporaryFile\n   > (Get-ZLocation).GetEnumerator() | ForEach-Object { Write-Output ($_.Name+'|'+$_.Value+'|0') } | Out-File $db\n   > zoxide import --from=z $db\n   > ```\n\n   </details>\n\n## Configuration\n\n### Flags\n\nWhen calling `zoxide init`, the following flags are available:\n\n- `--cmd`\n  - Changes the prefix of the `z` and `zi` commands.\n  - `--cmd j` would change the commands to (`j`, `ji`).\n  - `--cmd cd` would replace the `cd` command.\n- `--hook <HOOK>`\n  - Changes how often zoxide increments a directory's score:\n\n    | Hook            | Description                       |\n    | --------------- | --------------------------------- |\n    | `none`          | Never                             |\n    | `prompt`        | At every shell prompt             |\n    | `pwd` (default) | Whenever the directory is changed |\n\n- `--no-cmd`\n  - Prevents zoxide from defining the `z` and `zi` commands.\n  - These functions will still be available in your shell as `__zoxide_z` and\n    `__zoxide_zi`, should you choose to redefine them.\n\n### Environment variables\n\nEnvironment variables[^2] can be used for configuration. They must be set before\n`zoxide init` is called.\n\n- `_ZO_DATA_DIR`\n  - Specifies the directory in which the database is stored.\n  - The default value varies across OSes:\n\n    | OS          | Path                                     | Example                                    |\n    | ----------- | ---------------------------------------- | ------------------------------------------ |\n    | Linux / BSD | `$XDG_DATA_HOME` or `$HOME/.local/share` | `/home/alice/.local/share`                 |\n    | macOS       | `$HOME/Library/Application Support`      | `/Users/Alice/Library/Application Support` |\n    | Windows     | `%LOCALAPPDATA%`                         | `C:\\Users\\Alice\\AppData\\Local`             |\n\n- `_ZO_ECHO`\n  - When set to 1, `z` will print the matched directory before navigating to\n    it.\n- `_ZO_EXCLUDE_DIRS`\n  - Excludes the specified directories from the database.\n  - This is provided as a list of [globs][glob], separated by OS-specific\n    characters:\n\n    | OS                  | Separator | Example                 |\n    | ------------------- | --------- | ----------------------- |\n    | Linux / macOS / BSD | `:`       | `$HOME:$HOME/private/*` |\n    | Windows             | `;`       | `$HOME;$HOME/private/*` |\n\n  - By default, this is set to `\"$HOME\"`.\n- `_ZO_FZF_OPTS`\n  - Custom options to pass to [fzf] during interactive selection. See\n    [`man fzf`][fzf-man] for the list of options.\n- `_ZO_MAXAGE`\n  - Configures the [aging algorithm][algorithm-aging], which limits the maximum\n    number of entries in the database.\n  - By default, this is set to 10000.\n- `_ZO_RESOLVE_SYMLINKS`\n  - When set to 1, `z` will resolve symlinks before adding directories to the\n    database.\n\n## Third-party integrations\n\n| Application           | Description                                  | Plugin                     |\n| --------------------- | -------------------------------------------- | -------------------------- |\n| [aerc]                | Email client                                 | Natively supported         |\n| [alfred]              | macOS launcher                               | [alfred-zoxide]            |\n| [clink]               | Improved cmd.exe for Windows                 | [clink-zoxide]             |\n| [emacs]               | Text editor                                  | [zoxide.el]                |\n| [felix]               | File manager                                 | Natively supported         |\n| [joshuto]             | File manager                                 | Natively supported         |\n| [lf]                  | File manager                                 | See the [wiki][lf-wiki]    |\n| [nnn]                 | File manager                                 | [nnn-autojump]             |\n| [ranger]              | File manager                                 | [ranger-zoxide]            |\n| [raycast]             | macOS launcher                               | [raycast-zoxide]           |\n| [rfm]                 | File manager                                 | Natively supported         |\n| [sesh]                | `tmux` session manager                       | Natively supported         |\n| [telescope.nvim]      | Fuzzy finder for Neovim                      | [telescope-zoxide]         |\n| [tmux-session-wizard] | `tmux` session manager                       | Natively supported         |\n| [tmux-sessionx]       | `tmux` session manager                       | Natively supported         |\n| [vim] / [neovim]      | Text editor                                  | [zoxide.vim]               |\n| [xplr]                | File manager                                 | [zoxide.xplr]              |\n| [xxh]                 | Transports shell configuration over SSH      | [xxh-plugin-prerun-zoxide] |\n| [yazi]                | File manager                                 | Natively supported         |\n| [zabb]                | Finds the shortest possible query for a path | Natively supported         |\n| [zesh]                | `zellij` session manager                     | Natively supported         |\n| [zsh-autocomplete]    | Realtime completions for zsh                 | Natively supported         |\n\n[^1]:\n    Debian / Ubuntu derivatives update their packages very slowly. If you're\n    using one of these distributions, consider using the install script instead.\n\n[^2]:\n    If you're not sure how to set an environment variable on your shell, check\n    out the [wiki][wiki-env].\n\n[aerc]: https://github.com/rjarry/aerc\n[alfred]: https://www.alfredapp.com/\n[alfred-zoxide]: https://github.com/yihou/alfred-zoxide\n[algorithm-aging]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#aging\n[algorithm-matching]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#matching\n[alpine linux packages]: https://pkgs.alpinelinux.org/packages?name=zoxide\n[arch linux extra]: https://archlinux.org/packages/extra/x86_64/zoxide/\n[asdf]: https://github.com/asdf-vm/asdf\n[builtwithnix-badge]: https://img.shields.io/badge/builtwith-nix-7d81f7?logo=nixos&logoColor=white&style=flat-square\n[builtwithnix]: https://builtwithnix.org/\n[chocolatey]: https://community.chocolatey.org/packages/zoxide\n[clink-zoxide]: https://github.com/shunsambongi/clink-zoxide\n[clink]: https://github.com/mridgers/clink\n[conda-forge]: https://anaconda.org/conda-forge/zoxide\n[crates.io-badge]: https://img.shields.io/crates/v/zoxide?logo=rust&logoColor=white&style=flat-square\n[crates.io]: https://crates.io/crates/zoxide\n[debian packages]: https://packages.debian.org/stable/admin/zoxide\n[exherbo packages]: https://gitlab.exherbo.org/exherbo/rust/-/tree/master/packages/sys-apps/zoxide\n[devuan packages]: https://pkginfo.devuan.org/cgi-bin/package-query.html?c=package&q=zoxide\n[downloads-badge]: https://img.shields.io/github/downloads/ajeetdsouza/zoxide/total?logo=github&logoColor=white&style=flat-square\n[dports]: https://github.com/DragonFlyBSD/DPorts/tree/master/sysutils/zoxide\n[emacs]: https://www.gnu.org/software/emacs/\n[fedora packages]: https://src.fedoraproject.org/rpms/rust-zoxide\n[felix]: https://github.com/kyoheiu/felix\n[freshports]: https://www.freshports.org/sysutils/zoxide/\n[fzf-installation]: https://github.com/junegunn/fzf#installation\n[fzf-man]: https://manpages.ubuntu.com/manpages/en/man1/fzf.1.html\n[fzf]: https://github.com/junegunn/fzf\n[gentoo packages]: https://packages.gentoo.org/packages/app-shells/zoxide\n[glob]: https://man7.org/linux/man-pages/man7/glob.7.html\n[guix]: https://packages.guix.gnu.org/packages/zoxide/\n[homebrew]: https://formulae.brew.sh/formula/zoxide\n[issues]: https://github.com/ajeetdsouza/zoxide/issues/new\n[joshuto]: https://github.com/kamiyaa/joshuto\n[lf]: https://github.com/gokcehan/lf\n[lf-wiki]: https://github.com/gokcehan/lf/wiki/Integrations#zoxide\n[linuxbrew]: https://formulae.brew.sh/formula-linux/zoxide\n[macports]: https://ports.macports.org/port/zoxide/summary\n[neovim]: https://github.com/neovim/neovim\n[nixpkgs]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/zo/zoxide/package.nix\n[nnn-autojump]: https://github.com/jarun/nnn/blob/master/plugins/autojump\n[nnn]: https://github.com/jarun/nnn\n[opensuse factory]: https://build.opensuse.org/package/show/openSUSE:Factory/zoxide\n[pacstall packages]: https://pacstall.dev/packages/zoxide-deb\n[pkgsrc]: https://pkgsrc.se/sysutils/zoxide\n[ranger-zoxide]: https://github.com/jchook/ranger-zoxide\n[ranger]: https://github.com/ranger/ranger\n[raspbian packages]: https://archive.raspbian.org/raspbian/pool/main/r/rust-zoxide/\n[raycast]: https://www.raycast.com/\n[raycast-zoxide]: https://www.raycast.com/mrpunkin/raycast-zoxide\n[releases]: https://github.com/ajeetdsouza/zoxide/releases\n[rfm]: https://github.com/dsxmachina/rfm\n[scoop]: https://github.com/ScoopInstaller/Main/tree/master/bucket/zoxide.json\n[sesh]: https://github.com/joshmedeski/sesh\n[slackbuilds]: https://slackbuilds.org/repository/15.0/system/zoxide/\n[slackbuilds-howto]: https://slackbuilds.org/howto/\n[solus packages]: https://github.com/getsolus/packages/tree/main/packages/z/zoxide/\n[telescope-zoxide]: https://github.com/jvgrootveld/telescope-zoxide\n[telescope.nvim]: https://github.com/nvim-telescope/telescope.nvim\n[termux]: https://github.com/termux/termux-packages/tree/master/packages/zoxide\n[tmux-session-wizard]: https://github.com/27medkamal/tmux-session-wizard\n[tmux-sessionx]: https://github.com/omerxx/tmux-sessionx\n[tutorial]: contrib/tutorial.webp\n[ubuntu packages]: https://packages.ubuntu.com/jammy/zoxide\n[vim]: https://github.com/vim/vim\n[void linux packages]: https://github.com/void-linux/void-packages/tree/master/srcpkgs/zoxide\n[warp]: https://www.warp.dev\n[wiki-env]: https://github.com/ajeetdsouza/zoxide/wiki/HOWTO:-set-environment-variables \"HOWTO: set environment variables\"\n[xplr]: https://github.com/sayanarijit/xplr\n[xxh-plugin-prerun-zoxide]: https://github.com/xxh/xxh-plugin-prerun-zoxide\n[xxh]: https://github.com/xxh/xxh\n[yazi]: https://github.com/sxyazi/yazi\n[zabb]: https://github.com/Mellbourn/zabb\n[zesh]: https://github.com/roberte777/zesh\n[zoxide.el]: https://gitlab.com/Vonfry/zoxide.el\n[zoxide.vim]: https://github.com/nanotee/zoxide.vim\n[zoxide.xplr]: https://github.com/sayanarijit/zoxide.xplr\n[zsh-autocomplete]: https://github.com/marlonrichert/zsh-autocomplete\n"
  },
  {
    "path": "build.rs",
    "content": "#[path = \"src/cmd/cmd.rs\"]\nmod cmd;\n\nuse std::{env, io};\n\nuse clap::CommandFactory as _;\nuse clap_complete::shells::{Bash, Elvish, Fish, PowerShell, Zsh};\nuse clap_complete_fig::Fig;\nuse clap_complete_nushell::Nushell;\nuse cmd::Cmd;\n\nfn main() -> io::Result<()> {\n    // Since we are generating completions in the package directory, we need to\n    // set this so that Cargo doesn't rebuild every time.\n    println!(\"cargo:rerun-if-changed=build.rs\");\n    println!(\"cargo:rerun-if-changed=src/\");\n    println!(\"cargo:rerun-if-changed=templates/\");\n    println!(\"cargo:rerun-if-changed=tests/\");\n    generate_completions()\n}\n\nfn generate_completions() -> io::Result<()> {\n    const BIN_NAME: &str = env!(\"CARGO_PKG_NAME\");\n    const OUT_DIR: &str = \"contrib/completions\";\n    let cmd = &mut Cmd::command();\n\n    clap_complete::generate_to(Bash, cmd, BIN_NAME, OUT_DIR)?;\n    clap_complete::generate_to(Elvish, cmd, BIN_NAME, OUT_DIR)?;\n    clap_complete::generate_to(Fig, cmd, BIN_NAME, OUT_DIR)?;\n    clap_complete::generate_to(Fish, cmd, BIN_NAME, OUT_DIR)?;\n    clap_complete::generate_to(Nushell, cmd, BIN_NAME, OUT_DIR)?;\n    clap_complete::generate_to(PowerShell, cmd, BIN_NAME, OUT_DIR)?;\n    clap_complete::generate_to(Zsh, cmd, BIN_NAME, OUT_DIR)?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "contrib/completions/README.md",
    "content": "# completions\n\nShell completions for zoxide, generated by [clap]. Since clap is in beta, these\ncompletions should not be treated as stable.\n\n[clap]: https://github.com/clap-rs/clap\n"
  },
  {
    "path": "contrib/completions/_zoxide",
    "content": "#compdef zoxide\n\nautoload -U is-at-least\n\n_zoxide() {\n    typeset -A opt_args\n    typeset -a _arguments_options\n    local ret=1\n\n    if is-at-least 5.2; then\n        _arguments_options=(-s -S -C)\n    else\n        _arguments_options=(-s -C)\n    fi\n\n    local context curcontext=\"$curcontext\" state line\n    _arguments \"${_arguments_options[@]}\" : \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n\":: :_zoxide_commands\" \\\n\"*::: :->zoxide\" \\\n&& ret=0\n    case $state in\n    (zoxide)\n        words=($line[1] \"${words[@]}\")\n        (( CURRENT += 1 ))\n        curcontext=\"${curcontext%:*:*}:zoxide-command-$line[1]:\"\n        case $line[1] in\n            (add)\n_arguments \"${_arguments_options[@]}\" : \\\n'-s+[The rank to increment the entry if it exists or initialize it with if it doesn'\\''t]:SCORE:_default' \\\n'--score=[The rank to increment the entry if it exists or initialize it with if it doesn'\\''t]:SCORE:_default' \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n'*::paths:_files -/' \\\n&& ret=0\n;;\n(edit)\n_arguments \"${_arguments_options[@]}\" : \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n\":: :_zoxide__edit_commands\" \\\n\"*::: :->edit\" \\\n&& ret=0\n\n    case $state in\n    (edit)\n        words=($line[1] \"${words[@]}\")\n        (( CURRENT += 1 ))\n        curcontext=\"${curcontext%:*:*}:zoxide-edit-command-$line[1]:\"\n        case $line[1] in\n            (decrement)\n_arguments \"${_arguments_options[@]}\" : \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n':path:_default' \\\n&& ret=0\n;;\n(delete)\n_arguments \"${_arguments_options[@]}\" : \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n':path:_default' \\\n&& ret=0\n;;\n(increment)\n_arguments \"${_arguments_options[@]}\" : \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n':path:_default' \\\n&& ret=0\n;;\n(reload)\n_arguments \"${_arguments_options[@]}\" : \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n&& ret=0\n;;\n        esac\n    ;;\nesac\n;;\n(import)\n_arguments \"${_arguments_options[@]}\" : \\\n'--from=[Application to import from]:FROM:(autojump z)' \\\n'--merge[Merge into existing database]' \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n':path:_files' \\\n&& ret=0\n;;\n(init)\n_arguments \"${_arguments_options[@]}\" : \\\n'--cmd=[Changes the prefix of the \\`z\\` and \\`zi\\` commands]:CMD:_default' \\\n'--hook=[Changes how often zoxide increments a directory'\\''s score]:HOOK:(none prompt pwd)' \\\n'--no-cmd[Prevents zoxide from defining the \\`z\\` and \\`zi\\` commands]' \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n':shell:(bash elvish fish nushell posix powershell tcsh xonsh zsh)' \\\n&& ret=0\n;;\n(query)\n_arguments \"${_arguments_options[@]}\" : \\\n'--exclude=[Exclude the current directory]:path:_files -/' \\\n'--base-dir=[Only search within this directory]:path:_files -/' \\\n'-a[Show unavailable directories]' \\\n'--all[Show unavailable directories]' \\\n'(-l --list)-i[Use interactive selection]' \\\n'(-l --list)--interactive[Use interactive selection]' \\\n'(-i --interactive)-l[List all matching directories]' \\\n'(-i --interactive)--list[List all matching directories]' \\\n'-s[Print score with results]' \\\n'--score[Print score with results]' \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n'*::keywords:_default' \\\n&& ret=0\n;;\n(remove)\n_arguments \"${_arguments_options[@]}\" : \\\n'-h[Print help]' \\\n'--help[Print help]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n'*::paths:_files -/' \\\n&& ret=0\n;;\n        esac\n    ;;\nesac\n}\n\n(( $+functions[_zoxide_commands] )) ||\n_zoxide_commands() {\n    local commands; commands=(\n'add:Add a new directory or increment its rank' \\\n'edit:Edit the database' \\\n'import:Import entries from another application' \\\n'init:Generate shell configuration' \\\n'query:Search for a directory in the database' \\\n'remove:Remove a directory from the database' \\\n    )\n    _describe -t commands 'zoxide commands' commands \"$@\"\n}\n(( $+functions[_zoxide__add_commands] )) ||\n_zoxide__add_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide add commands' commands \"$@\"\n}\n(( $+functions[_zoxide__edit_commands] )) ||\n_zoxide__edit_commands() {\n    local commands; commands=(\n'decrement:' \\\n'delete:' \\\n'increment:' \\\n'reload:' \\\n    )\n    _describe -t commands 'zoxide edit commands' commands \"$@\"\n}\n(( $+functions[_zoxide__edit__decrement_commands] )) ||\n_zoxide__edit__decrement_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide edit decrement commands' commands \"$@\"\n}\n(( $+functions[_zoxide__edit__delete_commands] )) ||\n_zoxide__edit__delete_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide edit delete commands' commands \"$@\"\n}\n(( $+functions[_zoxide__edit__increment_commands] )) ||\n_zoxide__edit__increment_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide edit increment commands' commands \"$@\"\n}\n(( $+functions[_zoxide__edit__reload_commands] )) ||\n_zoxide__edit__reload_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide edit reload commands' commands \"$@\"\n}\n(( $+functions[_zoxide__import_commands] )) ||\n_zoxide__import_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide import commands' commands \"$@\"\n}\n(( $+functions[_zoxide__init_commands] )) ||\n_zoxide__init_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide init commands' commands \"$@\"\n}\n(( $+functions[_zoxide__query_commands] )) ||\n_zoxide__query_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide query commands' commands \"$@\"\n}\n(( $+functions[_zoxide__remove_commands] )) ||\n_zoxide__remove_commands() {\n    local commands; commands=()\n    _describe -t commands 'zoxide remove commands' commands \"$@\"\n}\n\nif [ \"$funcstack[1]\" = \"_zoxide\" ]; then\n    _zoxide \"$@\"\nelse\n    compdef _zoxide zoxide\nfi\n"
  },
  {
    "path": "contrib/completions/_zoxide.ps1",
    "content": "\nusing namespace System.Management.Automation\nusing namespace System.Management.Automation.Language\n\nRegister-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {\n    param($wordToComplete, $commandAst, $cursorPosition)\n\n    $commandElements = $commandAst.CommandElements\n    $command = @(\n        'zoxide'\n        for ($i = 1; $i -lt $commandElements.Count; $i++) {\n            $element = $commandElements[$i]\n            if ($element -isnot [StringConstantExpressionAst] -or\n                $element.StringConstantType -ne [StringConstantType]::BareWord -or\n                $element.Value.StartsWith('-') -or\n                $element.Value -eq $wordToComplete) {\n                break\n        }\n        $element.Value\n    }) -join ';'\n\n    $completions = @(switch ($command) {\n        'zoxide' {\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Add a new directory or increment its rank')\n            [CompletionResult]::new('edit', 'edit', [CompletionResultType]::ParameterValue, 'Edit the database')\n            [CompletionResult]::new('import', 'import', [CompletionResultType]::ParameterValue, 'Import entries from another application')\n            [CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Generate shell configuration')\n            [CompletionResult]::new('query', 'query', [CompletionResultType]::ParameterValue, 'Search for a directory in the database')\n            [CompletionResult]::new('remove', 'remove', [CompletionResultType]::ParameterValue, 'Remove a directory from the database')\n            break\n        }\n        'zoxide;add' {\n            [CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'The rank to increment the entry if it exists or initialize it with if it doesn''t')\n            [CompletionResult]::new('--score', '--score', [CompletionResultType]::ParameterName, 'The rank to increment the entry if it exists or initialize it with if it doesn''t')\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n        'zoxide;edit' {\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('decrement', 'decrement', [CompletionResultType]::ParameterValue, 'decrement')\n            [CompletionResult]::new('delete', 'delete', [CompletionResultType]::ParameterValue, 'delete')\n            [CompletionResult]::new('increment', 'increment', [CompletionResultType]::ParameterValue, 'increment')\n            [CompletionResult]::new('reload', 'reload', [CompletionResultType]::ParameterValue, 'reload')\n            break\n        }\n        'zoxide;edit;decrement' {\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n        'zoxide;edit;delete' {\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n        'zoxide;edit;increment' {\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n        'zoxide;edit;reload' {\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n        'zoxide;import' {\n            [CompletionResult]::new('--from', '--from', [CompletionResultType]::ParameterName, 'Application to import from')\n            [CompletionResult]::new('--merge', '--merge', [CompletionResultType]::ParameterName, 'Merge into existing database')\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n        'zoxide;init' {\n            [CompletionResult]::new('--cmd', '--cmd', [CompletionResultType]::ParameterName, 'Changes the prefix of the `z` and `zi` commands')\n            [CompletionResult]::new('--hook', '--hook', [CompletionResultType]::ParameterName, 'Changes how often zoxide increments a directory''s score')\n            [CompletionResult]::new('--no-cmd', '--no-cmd', [CompletionResultType]::ParameterName, 'Prevents zoxide from defining the `z` and `zi` commands')\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n        'zoxide;query' {\n            [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Exclude the current directory')\n            [CompletionResult]::new('--base-dir', '--base-dir', [CompletionResultType]::ParameterName, 'Only search within this directory')\n            [CompletionResult]::new('-a', '-a', [CompletionResultType]::ParameterName, 'Show unavailable directories')\n            [CompletionResult]::new('--all', '--all', [CompletionResultType]::ParameterName, 'Show unavailable directories')\n            [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'Use interactive selection')\n            [CompletionResult]::new('--interactive', '--interactive', [CompletionResultType]::ParameterName, 'Use interactive selection')\n            [CompletionResult]::new('-l', '-l', [CompletionResultType]::ParameterName, 'List all matching directories')\n            [CompletionResult]::new('--list', '--list', [CompletionResultType]::ParameterName, 'List all matching directories')\n            [CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'Print score with results')\n            [CompletionResult]::new('--score', '--score', [CompletionResultType]::ParameterName, 'Print score with results')\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n        'zoxide;remove' {\n            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')\n            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')\n            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')\n            break\n        }\n    })\n\n    $completions.Where{ $_.CompletionText -like \"$wordToComplete*\" } |\n        Sort-Object -Property ListItemText\n}\n"
  },
  {
    "path": "contrib/completions/zoxide.bash",
    "content": "_zoxide() {\n    local i cur prev opts cmd\n    COMPREPLY=()\n    if [[ \"${BASH_VERSINFO[0]}\" -ge 4 ]]; then\n        cur=\"$2\"\n    else\n        cur=\"${COMP_WORDS[COMP_CWORD]}\"\n    fi\n    prev=\"$3\"\n    cmd=\"\"\n    opts=\"\"\n\n    for i in \"${COMP_WORDS[@]:0:COMP_CWORD}\"\n    do\n        case \"${cmd},${i}\" in\n            \",$1\")\n                cmd=\"zoxide\"\n                ;;\n            zoxide,add)\n                cmd=\"zoxide__add\"\n                ;;\n            zoxide,edit)\n                cmd=\"zoxide__edit\"\n                ;;\n            zoxide,import)\n                cmd=\"zoxide__import\"\n                ;;\n            zoxide,init)\n                cmd=\"zoxide__init\"\n                ;;\n            zoxide,query)\n                cmd=\"zoxide__query\"\n                ;;\n            zoxide,remove)\n                cmd=\"zoxide__remove\"\n                ;;\n            zoxide__edit,decrement)\n                cmd=\"zoxide__edit__decrement\"\n                ;;\n            zoxide__edit,delete)\n                cmd=\"zoxide__edit__delete\"\n                ;;\n            zoxide__edit,increment)\n                cmd=\"zoxide__edit__increment\"\n                ;;\n            zoxide__edit,reload)\n                cmd=\"zoxide__edit__reload\"\n                ;;\n            *)\n                ;;\n        esac\n    done\n\n    case \"${cmd}\" in\n        zoxide)\n            opts=\"-h -V --help --version add edit import init query remove\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__add)\n            opts=\"-s -h -V --score --help --version <PATHS>...\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                --score)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -s)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__edit)\n            opts=\"-h -V --help --version decrement delete increment reload\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__edit__decrement)\n            opts=\"-h -V --help --version <PATH>\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__edit__delete)\n            opts=\"-h -V --help --version <PATH>\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__edit__increment)\n            opts=\"-h -V --help --version <PATH>\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__edit__reload)\n            opts=\"-h -V --help --version\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__import)\n            opts=\"-h -V --from --merge --help --version <PATH>\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                --from)\n                    COMPREPLY=($(compgen -W \"autojump z\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__init)\n            opts=\"-h -V --no-cmd --cmd --hook --help --version bash elvish fish nushell posix powershell tcsh xonsh zsh\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                --cmd)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --hook)\n                    COMPREPLY=($(compgen -W \"none prompt pwd\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__query)\n            opts=\"-a -i -l -s -h -V --all --interactive --list --score --exclude --base-dir --help --version [KEYWORDS]...\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                --exclude)\n                    COMPREPLY=()\n                    if [[ \"${BASH_VERSINFO[0]}\" -ge 4 ]]; then\n                        compopt -o plusdirs\n                    fi\n                    return 0\n                    ;;\n                --base-dir)\n                    COMPREPLY=()\n                    if [[ \"${BASH_VERSINFO[0]}\" -ge 4 ]]; then\n                        compopt -o plusdirs\n                    fi\n                    return 0\n                    ;;\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n        zoxide__remove)\n            opts=\"-h -V --help --version [PATHS]...\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n    esac\n}\n\nif [[ \"${BASH_VERSINFO[0]}\" -eq 4 && \"${BASH_VERSINFO[1]}\" -ge 4 || \"${BASH_VERSINFO[0]}\" -gt 4 ]]; then\n    complete -F _zoxide -o nosort -o bashdefault -o default zoxide\nelse\n    complete -F _zoxide -o bashdefault -o default zoxide\nfi\n"
  },
  {
    "path": "contrib/completions/zoxide.elv",
    "content": "\nuse builtin;\nuse str;\n\nset edit:completion:arg-completer[zoxide] = {|@words|\n    fn spaces {|n|\n        builtin:repeat $n ' ' | str:join ''\n    }\n    fn cand {|text desc|\n        edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc\n    }\n    var command = 'zoxide'\n    for word $words[1..-1] {\n        if (str:has-prefix $word '-') {\n            break\n        }\n        set command = $command';'$word\n    }\n    var completions = [\n        &'zoxide'= {\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n            cand add 'Add a new directory or increment its rank'\n            cand edit 'Edit the database'\n            cand import 'Import entries from another application'\n            cand init 'Generate shell configuration'\n            cand query 'Search for a directory in the database'\n            cand remove 'Remove a directory from the database'\n        }\n        &'zoxide;add'= {\n            cand -s 'The rank to increment the entry if it exists or initialize it with if it doesn''t'\n            cand --score 'The rank to increment the entry if it exists or initialize it with if it doesn''t'\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n        &'zoxide;edit'= {\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n            cand decrement 'decrement'\n            cand delete 'delete'\n            cand increment 'increment'\n            cand reload 'reload'\n        }\n        &'zoxide;edit;decrement'= {\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n        &'zoxide;edit;delete'= {\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n        &'zoxide;edit;increment'= {\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n        &'zoxide;edit;reload'= {\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n        &'zoxide;import'= {\n            cand --from 'Application to import from'\n            cand --merge 'Merge into existing database'\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n        &'zoxide;init'= {\n            cand --cmd 'Changes the prefix of the `z` and `zi` commands'\n            cand --hook 'Changes how often zoxide increments a directory''s score'\n            cand --no-cmd 'Prevents zoxide from defining the `z` and `zi` commands'\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n        &'zoxide;query'= {\n            cand --exclude 'Exclude the current directory'\n            cand --base-dir 'Only search within this directory'\n            cand -a 'Show unavailable directories'\n            cand --all 'Show unavailable directories'\n            cand -i 'Use interactive selection'\n            cand --interactive 'Use interactive selection'\n            cand -l 'List all matching directories'\n            cand --list 'List all matching directories'\n            cand -s 'Print score with results'\n            cand --score 'Print score with results'\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n        &'zoxide;remove'= {\n            cand -h 'Print help'\n            cand --help 'Print help'\n            cand -V 'Print version'\n            cand --version 'Print version'\n        }\n    ]\n    $completions[$command]\n}\n"
  },
  {
    "path": "contrib/completions/zoxide.fish",
    "content": "# Print an optspec for argparse to handle cmd's options that are independent of any subcommand.\nfunction __fish_zoxide_global_optspecs\n\tstring join \\n h/help V/version\nend\n\nfunction __fish_zoxide_needs_command\n\t# Figure out if the current invocation already has a command.\n\tset -l cmd (commandline -opc)\n\tset -e cmd[1]\n\targparse -s (__fish_zoxide_global_optspecs) -- $cmd 2>/dev/null\n\tor return\n\tif set -q argv[1]\n\t\t# Also print the command, so this can be used to figure out what it is.\n\t\techo $argv[1]\n\t\treturn 1\n\tend\n\treturn 0\nend\n\nfunction __fish_zoxide_using_subcommand\n\tset -l cmd (__fish_zoxide_needs_command)\n\ttest -z \"$cmd\"\n\tand return 1\n\tcontains -- $cmd[1] $argv\nend\n\ncomplete -c zoxide -n \"__fish_zoxide_needs_command\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_needs_command\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_needs_command\" -f -a \"add\" -d 'Add a new directory or increment its rank'\ncomplete -c zoxide -n \"__fish_zoxide_needs_command\" -f -a \"edit\" -d 'Edit the database'\ncomplete -c zoxide -n \"__fish_zoxide_needs_command\" -f -a \"import\" -d 'Import entries from another application'\ncomplete -c zoxide -n \"__fish_zoxide_needs_command\" -f -a \"init\" -d 'Generate shell configuration'\ncomplete -c zoxide -n \"__fish_zoxide_needs_command\" -f -a \"query\" -d 'Search for a directory in the database'\ncomplete -c zoxide -n \"__fish_zoxide_needs_command\" -f -a \"remove\" -d 'Remove a directory from the database'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand add\" -s s -l score -d 'The rank to increment the entry if it exists or initialize it with if it doesn\\'t' -r\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand add\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand add\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and not __fish_seen_subcommand_from decrement delete increment reload\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and not __fish_seen_subcommand_from decrement delete increment reload\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and not __fish_seen_subcommand_from decrement delete increment reload\" -f -a \"decrement\"\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and not __fish_seen_subcommand_from decrement delete increment reload\" -f -a \"delete\"\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and not __fish_seen_subcommand_from decrement delete increment reload\" -f -a \"increment\"\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and not __fish_seen_subcommand_from decrement delete increment reload\" -f -a \"reload\"\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from decrement\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from decrement\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from delete\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from delete\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from increment\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from increment\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from reload\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from reload\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand import\" -l from -d 'Application to import from' -r -f -a \"autojump\\t''\nz\\t''\"\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand import\" -l merge -d 'Merge into existing database'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand import\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand import\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand init\" -l cmd -d 'Changes the prefix of the `z` and `zi` commands' -r\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand init\" -l hook -d 'Changes how often zoxide increments a directory\\'s score' -r -f -a \"none\\t''\nprompt\\t''\npwd\\t''\"\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand init\" -l no-cmd -d 'Prevents zoxide from defining the `z` and `zi` commands'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand init\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand init\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand query\" -l exclude -d 'Exclude the current directory' -r -f -a \"(__fish_complete_directories)\"\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand query\" -l base-dir -d 'Only search within this directory' -r -f -a \"(__fish_complete_directories)\"\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand query\" -s a -l all -d 'Show unavailable directories'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand query\" -s i -l interactive -d 'Use interactive selection'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand query\" -s l -l list -d 'List all matching directories'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand query\" -s s -l score -d 'Print score with results'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand query\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand query\" -s V -l version -d 'Print version'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand remove\" -s h -l help -d 'Print help'\ncomplete -c zoxide -n \"__fish_zoxide_using_subcommand remove\" -s V -l version -d 'Print version'\n"
  },
  {
    "path": "contrib/completions/zoxide.nu",
    "content": "module completions {\n\n  # A smarter cd command for your terminal\n  export extern zoxide [\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  # Add a new directory or increment its rank\n  export extern \"zoxide add\" [\n    ...paths: path\n    --score(-s): string       # The rank to increment the entry if it exists or initialize it with if it doesn't\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  # Edit the database\n  export extern \"zoxide edit\" [\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  export extern \"zoxide edit decrement\" [\n    path: string\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  export extern \"zoxide edit delete\" [\n    path: string\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  export extern \"zoxide edit increment\" [\n    path: string\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  export extern \"zoxide edit reload\" [\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  def \"nu-complete zoxide import from\" [] {\n    [ \"autojump\" \"z\" ]\n  }\n\n  # Import entries from another application\n  export extern \"zoxide import\" [\n    path: path\n    --from: string@\"nu-complete zoxide import from\" # Application to import from\n    --merge                   # Merge into existing database\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  def \"nu-complete zoxide init shell\" [] {\n    [ \"bash\" \"elvish\" \"fish\" \"nushell\" \"posix\" \"powershell\" \"tcsh\" \"xonsh\" \"zsh\" ]\n  }\n\n  def \"nu-complete zoxide init hook\" [] {\n    [ \"none\" \"prompt\" \"pwd\" ]\n  }\n\n  # Generate shell configuration\n  export extern \"zoxide init\" [\n    shell: string@\"nu-complete zoxide init shell\"\n    --no-cmd                  # Prevents zoxide from defining the `z` and `zi` commands\n    --cmd: string             # Changes the prefix of the `z` and `zi` commands\n    --hook: string@\"nu-complete zoxide init hook\" # Changes how often zoxide increments a directory's score\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  # Search for a directory in the database\n  export extern \"zoxide query\" [\n    ...keywords: string\n    --all(-a)                 # Show unavailable directories\n    --interactive(-i)         # Use interactive selection\n    --list(-l)                # List all matching directories\n    --score(-s)               # Print score with results\n    --exclude: path           # Exclude the current directory\n    --base-dir: path          # Only search within this directory\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n  # Remove a directory from the database\n  export extern \"zoxide remove\" [\n    ...paths: path\n    --help(-h)                # Print help\n    --version(-V)             # Print version\n  ]\n\n}\n\nexport use completions *\n"
  },
  {
    "path": "contrib/completions/zoxide.ts",
    "content": "const completion: Fig.Spec = {\n  name: \"zoxide\",\n  description: \"A smarter cd command for your terminal\",\n  subcommands: [\n    {\n      name: \"add\",\n      description: \"Add a new directory or increment its rank\",\n      options: [\n        {\n          name: [\"-s\", \"--score\"],\n          description: \"The rank to increment the entry if it exists or initialize it with if it doesn't\",\n          isRepeatable: true,\n          args: {\n            name: \"score\",\n            isOptional: true,\n          },\n        },\n        {\n          name: [\"-h\", \"--help\"],\n          description: \"Print help\",\n        },\n        {\n          name: [\"-V\", \"--version\"],\n          description: \"Print version\",\n        },\n      ],\n      args: {\n        name: \"paths\",\n        isVariadic: true,\n        template: \"folders\",\n      },\n    },\n    {\n      name: \"edit\",\n      description: \"Edit the database\",\n      subcommands: [\n        {\n          name: \"decrement\",\n          hidden: true,\n          options: [\n            {\n              name: [\"-h\", \"--help\"],\n              description: \"Print help\",\n            },\n            {\n              name: [\"-V\", \"--version\"],\n              description: \"Print version\",\n            },\n          ],\n          args: {\n            name: \"path\",\n          },\n        },\n        {\n          name: \"delete\",\n          hidden: true,\n          options: [\n            {\n              name: [\"-h\", \"--help\"],\n              description: \"Print help\",\n            },\n            {\n              name: [\"-V\", \"--version\"],\n              description: \"Print version\",\n            },\n          ],\n          args: {\n            name: \"path\",\n          },\n        },\n        {\n          name: \"increment\",\n          hidden: true,\n          options: [\n            {\n              name: [\"-h\", \"--help\"],\n              description: \"Print help\",\n            },\n            {\n              name: [\"-V\", \"--version\"],\n              description: \"Print version\",\n            },\n          ],\n          args: {\n            name: \"path\",\n          },\n        },\n        {\n          name: \"reload\",\n          hidden: true,\n          options: [\n            {\n              name: [\"-h\", \"--help\"],\n              description: \"Print help\",\n            },\n            {\n              name: [\"-V\", \"--version\"],\n              description: \"Print version\",\n            },\n          ],\n        },\n      ],\n      options: [\n        {\n          name: [\"-h\", \"--help\"],\n          description: \"Print help\",\n        },\n        {\n          name: [\"-V\", \"--version\"],\n          description: \"Print version\",\n        },\n      ],\n    },\n    {\n      name: \"import\",\n      description: \"Import entries from another application\",\n      options: [\n        {\n          name: \"--from\",\n          description: \"Application to import from\",\n          isRepeatable: true,\n          args: {\n            name: \"from\",\n            suggestions: [\n              \"autojump\",\n              \"z\",\n            ],\n          },\n        },\n        {\n          name: \"--merge\",\n          description: \"Merge into existing database\",\n        },\n        {\n          name: [\"-h\", \"--help\"],\n          description: \"Print help\",\n        },\n        {\n          name: [\"-V\", \"--version\"],\n          description: \"Print version\",\n        },\n      ],\n      args: {\n        name: \"path\",\n        template: \"filepaths\",\n      },\n    },\n    {\n      name: \"init\",\n      description: \"Generate shell configuration\",\n      options: [\n        {\n          name: \"--cmd\",\n          description: \"Changes the prefix of the `z` and `zi` commands\",\n          isRepeatable: true,\n          args: {\n            name: \"cmd\",\n            isOptional: true,\n          },\n        },\n        {\n          name: \"--hook\",\n          description: \"Changes how often zoxide increments a directory's score\",\n          isRepeatable: true,\n          args: {\n            name: \"hook\",\n            isOptional: true,\n            suggestions: [\n              \"none\",\n              \"prompt\",\n              \"pwd\",\n            ],\n          },\n        },\n        {\n          name: \"--no-cmd\",\n          description: \"Prevents zoxide from defining the `z` and `zi` commands\",\n        },\n        {\n          name: [\"-h\", \"--help\"],\n          description: \"Print help\",\n        },\n        {\n          name: [\"-V\", \"--version\"],\n          description: \"Print version\",\n        },\n      ],\n      args: {\n        name: \"shell\",\n        suggestions: [\n          \"bash\",\n          \"elvish\",\n          \"fish\",\n          \"nushell\",\n          \"posix\",\n          \"powershell\",\n          \"tcsh\",\n          \"xonsh\",\n          \"zsh\",\n        ],\n      },\n    },\n    {\n      name: \"query\",\n      description: \"Search for a directory in the database\",\n      options: [\n        {\n          name: \"--exclude\",\n          description: \"Exclude the current directory\",\n          isRepeatable: true,\n          args: {\n            name: \"exclude\",\n            isOptional: true,\n            template: \"folders\",\n          },\n        },\n        {\n          name: \"--base-dir\",\n          description: \"Only search within this directory\",\n          isRepeatable: true,\n          args: {\n            name: \"base_dir\",\n            isOptional: true,\n            template: \"folders\",\n          },\n        },\n        {\n          name: [\"-a\", \"--all\"],\n          description: \"Show unavailable directories\",\n        },\n        {\n          name: [\"-i\", \"--interactive\"],\n          description: \"Use interactive selection\",\n          exclusiveOn: [\n            \"-l\",\n            \"--list\",\n          ],\n        },\n        {\n          name: [\"-l\", \"--list\"],\n          description: \"List all matching directories\",\n          exclusiveOn: [\n            \"-i\",\n            \"--interactive\",\n          ],\n        },\n        {\n          name: [\"-s\", \"--score\"],\n          description: \"Print score with results\",\n        },\n        {\n          name: [\"-h\", \"--help\"],\n          description: \"Print help\",\n        },\n        {\n          name: [\"-V\", \"--version\"],\n          description: \"Print version\",\n        },\n      ],\n      args: {\n        name: \"keywords\",\n        isVariadic: true,\n        isOptional: true,\n      },\n    },\n    {\n      name: \"remove\",\n      description: \"Remove a directory from the database\",\n      options: [\n        {\n          name: [\"-h\", \"--help\"],\n          description: \"Print help\",\n        },\n        {\n          name: [\"-V\", \"--version\"],\n          description: \"Print version\",\n        },\n      ],\n      args: {\n        name: \"paths\",\n        isVariadic: true,\n        isOptional: true,\n        template: \"folders\",\n      },\n    },\n  ],\n  options: [\n    {\n      name: [\"-h\", \"--help\"],\n      description: \"Print help\",\n    },\n    {\n      name: [\"-V\", \"--version\"],\n      description: \"Print version\",\n    },\n  ],\n};\n\nexport default completion;\n"
  },
  {
    "path": "init.fish",
    "content": "if command -sq zoxide\n    zoxide init fish | source\nelse\n    echo 'zoxide: command not found, please install it from https://github.com/ajeetdsouza/zoxide'\nend\n"
  },
  {
    "path": "install.sh",
    "content": "#!/bin/sh\n# shellcheck shell=dash\n# shellcheck disable=SC3043 # Assume `local` extension\n\n# The official zoxide installer.\n#\n# It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local`\n# extension. Note: Most shells limit `local` to 1 var per line, contra bash.\n\nmain() {\n    # The version of ksh93 that ships with many illumos systems does not support the \"local\"\n    # extension. Print a message rather than fail in subtle ways later on:\n    if [ \"${KSH_VERSION-}\" = 'Version JM 93t+ 2010-03-05' ]; then\n        err 'the installer does not work with this ksh93 version; please try bash'\n    fi\n\n    set -u\n\n    parse_args \"$@\"\n\n    local _arch\n    _arch=\"${_ZOXIDE_ARCH:-$(get_architecture)}\" || exit $?\n    assert_nz \"${_arch}\" \"arch\"\n    echo \"Detected architecture: ${_arch}\"\n\n    local _bin_name\n    case \"${_arch}\" in\n    *windows*) _bin_name=\"zoxide.exe\" ;;\n    *) _bin_name=\"zoxide\" ;;\n    esac\n\n    # Create and enter a temporary directory.\n    local _tmp_dir\n    _tmp_dir=\"$(mktemp -d /tmp/zoxide_XXXXXX)\" || err \"mktemp: could not create temporary directory\"\n    cd \"${_tmp_dir}\" || err \"cd: failed to enter directory: ${_tmp_dir}\"\n\n    # Download and extract zoxide.\n    local _package\n    _package=\"$(download_zoxide \"${_arch}\")\" || exit $?\n    assert_nz \"${_package}\" \"package\"\n    echo \"Downloaded package: ${_package}\"\n    case \"${_package}\" in\n    *.tar.gz)\n        need_cmd tar\n        ensure tar -xf \"${_package}\"\n        ;;\n    *.zip)\n        need_cmd unzip\n        ensure unzip -oq \"${_package}\"\n        ;;\n    *)\n        err \"unsupported package format: ${_package}\"\n        ;;\n    esac\n\n    # Install binary.\n    ensure try_sudo mkdir -p -- \"${_ZOXIDE_BIN_DIR}\"\n    ensure try_sudo cp -- \"${_bin_name}\" \"${_ZOXIDE_BIN_DIR}/${_bin_name}\"\n    ensure try_sudo chmod +x \"${_ZOXIDE_BIN_DIR}/${_bin_name}\"\n    echo \"Installed zoxide to ${_ZOXIDE_BIN_DIR}\"\n\n    # Install manpages.\n    ensure try_sudo mkdir -p -- \"${_ZOXIDE_MAN_DIR}/man1\"\n    ensure try_sudo cp -- \"man/man1/\"* \"${_ZOXIDE_MAN_DIR}/man1/\"\n    echo \"Installed manpages to ${_ZOXIDE_MAN_DIR}\"\n\n    # Print success message and check $PATH.\n    echo \"\"\n    echo \"zoxide is installed!\"\n    if ! echo \":${PATH}:\" | grep -Fq \":${_ZOXIDE_BIN_DIR}:\"; then\n        echo \"Note: ${_ZOXIDE_BIN_DIR} is not on your \\$PATH. zoxide will not work unless it is added to \\$PATH.\"\n    fi\n}\n\n# Parse the arguments passed and set variables accordingly.\nparse_args() {\n    _ZOXIDE_BIN_DIR_DEFAULT=\"${HOME}/.local/bin\"\n    _ZOXIDE_MAN_DIR_DEFAULT=\"${HOME}/.local/share/man\"\n    _ZOXIDE_SUDO_DEFAULT=\"sudo\"\n\n    _ZOXIDE_BIN_DIR=\"${_ZOXIDE_BIN_DIR_DEFAULT}\"\n    _ZOXIDE_MAN_DIR=\"${_ZOXIDE_MAN_DIR_DEFAULT}\"\n    _ZOXIDE_SUDO=\"${_ZOXIDE_SUDO_DEFAULT}\"\n\n    while [ \"$#\" -gt 0 ]; do\n        case \"$1\" in\n        --arch) _ZOXIDE_ARCH=\"$2\" && shift 2 ;;\n        --arch=*) _ZOXIDE_ARCH=\"${1#*=}\" && shift 1 ;;\n        --bin-dir) _ZOXIDE_BIN_DIR=\"$2\" && shift 2 ;;\n        --bin-dir=*) _ZOXIDE_BIN_DIR=\"${1#*=}\" && shift 1 ;;\n        --man-dir) _ZOXIDE_MAN_DIR=\"$2\" && shift 2 ;;\n        --man-dir=*) _ZOXIDE_MAN_DIR=\"${1#*=}\" && shift 1 ;;\n        --sudo) _ZOXIDE_SUDO=\"$2\" && shift 2 ;;\n        --sudo=*) _ZOXIDE_SUDO=\"${1#*=}\" && shift 1 ;;\n        -h | --help) usage && exit 0 ;;\n        *) err \"Unknown option: $1\" ;;\n        esac\n    done\n}\n\nusage() {\n    # heredocs are not defined in POSIX.\n    local _text_heading _text_reset\n    _text_heading=\"$(tput bold || true 2>/dev/null)$(tput smul || true 2>/dev/null)\"\n    _text_reset=\"$(tput sgr0 || true 2>/dev/null)\"\n\n    local _arch\n    _arch=\"$(get_architecture || true)\"\n\n    echo \"\\\n${_text_heading}zoxide installer${_text_reset}\nAjeet D'Souza <98ajeet@gmail.com>\nhttps://github.com/ajeetdsouza/zoxide\n\nFetches and installs zoxide. If zoxide is already installed, it will be updated to the latest version.\n\n${_text_heading}Usage:${_text_reset}\n  install.sh [OPTIONS]\n\n${_text_heading}Options:${_text_reset}\n      --arch     Override the architecture identified by the installer [current: ${_arch}]\n      --bin-dir  Override the installation directory [default: ${_ZOXIDE_BIN_DIR_DEFAULT}]\n      --man-dir  Override the manpage installation directory [default: ${_ZOXIDE_MAN_DIR_DEFAULT}]\n      --sudo     Override the command used to elevate to root privileges [default: ${_ZOXIDE_SUDO_DEFAULT}]\n  -h, --help     Print help\"\n}\n\ndownload_zoxide() {\n    local _arch=\"$1\"\n\n    if check_cmd curl; then\n        _dld=curl\n    elif check_cmd wget; then\n        _dld=wget\n    else\n        need_cmd 'curl or wget'\n    fi\n    need_cmd grep\n\n    local _releases_url=\"https://api.github.com/repos/ajeetdsouza/zoxide/releases/latest\"\n    local _releases\n    case \"${_dld}\" in\n    curl) _releases=\"$(curl -sL \"${_releases_url}\")\" ||\n        err \"curl: failed to download ${_releases_url}\" ;;\n    wget) _releases=\"$(wget -qO- \"${_releases_url}\")\" ||\n        err \"wget: failed to download ${_releases_url}\" ;;\n    *) err \"unsupported downloader: ${_dld}\" ;;\n    esac\n    (echo \"${_releases}\" | grep -q 'API rate limit exceeded') &&\n        err \"you have exceeded GitHub's API rate limit. Please try again later, or use a different installation method: https://github.com/ajeetdsouza/zoxide/#installation\"\n\n    local _package_url\n    _package_url=\"$(echo \"${_releases}\" | grep \"browser_download_url\" | cut -d '\"' -f 4 | grep -- \"${_arch}\")\" ||\n        err \"zoxide has not yet been packaged for your architecture (${_arch}), please file an issue: https://github.com/ajeetdsouza/zoxide/issues\"\n\n    local _ext\n    case \"${_package_url}\" in\n    *.tar.gz) _ext=\"tar.gz\" ;;\n    *.zip) _ext=\"zip\" ;;\n    *) err \"unsupported package format: ${_package_url}\" ;;\n    esac\n\n    local _package=\"zoxide.${_ext}\"\n    case \"${_dld}\" in\n    curl) _releases=\"$(curl -sLo \"${_package}\" \"${_package_url}\")\" || err \"curl: failed to download ${_package_url}\" ;;\n    wget) _releases=\"$(wget -qO \"${_package}\" \"${_package_url}\")\" || err \"wget: failed to download ${_package_url}\" ;;\n    *) err \"unsupported downloader: ${_dld}\" ;;\n    esac\n\n    echo \"${_package}\"\n}\n\ntry_sudo() {\n    if \"$@\" >/dev/null 2>&1; then\n        return 0\n    fi\n\n    need_sudo\n    \"${_ZOXIDE_SUDO}\" \"$@\"\n}\n\nneed_sudo() {\n    if ! check_cmd \"${_ZOXIDE_SUDO}\"; then\n        err \"\\\ncould not find the command \\`${_ZOXIDE_SUDO}\\` needed to get permissions for install.\n\nIf you are on Windows, please run your shell as an administrator, then rerun this script.\nOtherwise, please run this script as root, or install \\`sudo\\`.\"\n    fi\n\n    if ! \"${_ZOXIDE_SUDO}\" -v; then\n        err \"sudo permissions not granted, aborting installation\"\n    fi\n}\n\n# The below functions have been extracted with minor modifications from the\n# Rustup install script:\n#\n#   https://github.com/rust-lang/rustup/blob/4c1289b2c3f3702783900934a38d7c5f912af787/rustup-init.sh\n\nget_architecture() {\n    local _ostype _cputype _bitness _arch _clibtype\n    _ostype=\"$(uname -s)\"\n    _cputype=\"$(uname -m)\"\n    _clibtype=\"musl\"\n\n    if [ \"${_ostype}\" = Linux ]; then\n        if [ \"$(uname -o || true)\" = Android ]; then\n            _ostype=Android\n        fi\n    fi\n\n    if [ \"${_ostype}\" = Darwin ] && [ \"${_cputype}\" = i386 ]; then\n        # Darwin `uname -m` lies\n        if sysctl hw.optional.x86_64 | grep -q ': 1'; then\n            _cputype=x86_64\n        fi\n    fi\n\n    if [ \"${_ostype}\" = SunOS ]; then\n        # Both Solaris and illumos presently announce as \"SunOS\" in \"uname -s\"\n        # so use \"uname -o\" to disambiguate.  We use the full path to the\n        # system uname in case the user has coreutils uname first in PATH,\n        # which has historically sometimes printed the wrong value here.\n        if [ \"$(/usr/bin/uname -o || true)\" = illumos ]; then\n            _ostype=illumos\n        fi\n\n        # illumos systems have multi-arch userlands, and \"uname -m\" reports the\n        # machine hardware name; e.g., \"i86pc\" on both 32- and 64-bit x86\n        # systems.  Check for the native (widest) instruction set on the\n        # running kernel:\n        if [ \"${_cputype}\" = i86pc ]; then\n            _cputype=\"$(isainfo -n)\"\n        fi\n    fi\n\n    case \"${_ostype}\" in\n    Android)\n        _ostype=linux-android\n        ;;\n    Linux)\n        check_proc\n        _ostype=unknown-linux-${_clibtype}\n        _bitness=$(get_bitness)\n        ;;\n    FreeBSD)\n        _ostype=unknown-freebsd\n        ;;\n    NetBSD)\n        _ostype=unknown-netbsd\n        ;;\n    DragonFly)\n        _ostype=unknown-dragonfly\n        ;;\n    Darwin)\n        _ostype=apple-darwin\n        ;;\n    illumos)\n        _ostype=unknown-illumos\n        ;;\n    MINGW* | MSYS* | CYGWIN* | Windows_NT)\n        _ostype=pc-windows-msvc\n        ;;\n    *)\n        err \"unrecognized OS type: ${_ostype}\"\n        ;;\n    esac\n\n    case \"${_cputype}\" in\n    i386 | i486 | i686 | i786 | x86)\n        _cputype=i686\n        ;;\n    xscale | arm)\n        _cputype=arm\n        if [ \"${_ostype}\" = \"linux-android\" ]; then\n            _ostype=linux-androideabi\n        fi\n        ;;\n    armv6l)\n        _cputype=arm\n        if [ \"${_ostype}\" = \"linux-android\" ]; then\n            _ostype=linux-androideabi\n        else\n            _ostype=\"${_ostype}eabihf\"\n        fi\n        ;;\n    armv7l | armv8l)\n        _cputype=armv7\n        if [ \"${_ostype}\" = \"linux-android\" ]; then\n            _ostype=linux-androideabi\n        else\n            _ostype=\"${_ostype}eabihf\"\n        fi\n        ;;\n    aarch64 | arm64)\n        _cputype=aarch64\n        ;;\n    x86_64 | x86-64 | x64 | amd64)\n        _cputype=x86_64\n        ;;\n    mips)\n        _cputype=$(get_endianness mips '' el)\n        ;;\n    mips64)\n        if [ \"${_bitness}\" -eq 64 ]; then\n            # only n64 ABI is supported for now\n            _ostype=\"${_ostype}abi64\"\n            _cputype=$(get_endianness mips64 '' el)\n        fi\n        ;;\n    ppc)\n        _cputype=powerpc\n        ;;\n    ppc64)\n        _cputype=powerpc64\n        ;;\n    ppc64le)\n        _cputype=powerpc64le\n        ;;\n    s390x)\n        _cputype=s390x\n        ;;\n    riscv64)\n        _cputype=riscv64gc\n        ;;\n    *)\n        err \"unknown CPU type: ${_cputype}\"\n        ;;\n    esac\n\n    # Detect 64-bit linux with 32-bit userland\n    if [ \"${_ostype}\" = unknown-linux-musl ] && [ \"${_bitness}\" -eq 32 ]; then\n        case ${_cputype} in\n        x86_64)\n            # 32-bit executable for amd64 = x32\n            if is_host_amd64_elf; then {\n                err \"x32 userland is unsupported\"\n            }; else\n                _cputype=i686\n            fi\n            ;;\n        mips64)\n            _cputype=$(get_endianness mips '' el)\n            ;;\n        powerpc64)\n            _cputype=powerpc\n            ;;\n        aarch64)\n            _cputype=armv7\n            if [ \"${_ostype}\" = \"linux-android\" ]; then\n                _ostype=linux-androideabi\n            else\n                _ostype=\"${_ostype}eabihf\"\n            fi\n            ;;\n        riscv64gc)\n            err \"riscv64 with 32-bit userland unsupported\"\n            ;;\n        *) ;;\n        esac\n    fi\n\n    # Detect armv7 but without the CPU features Rust needs in that build,\n    # and fall back to arm.\n    # See https://github.com/rust-lang/rustup.rs/issues/587.\n    if [ \"${_ostype}\" = \"unknown-linux-musleabihf\" ] && [ \"${_cputype}\" = armv7 ]; then\n        if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then\n            # At least one processor does not have NEON.\n            _cputype=arm\n        fi\n    fi\n\n    _arch=\"${_cputype}-${_ostype}\"\n    echo \"${_arch}\"\n}\n\nget_bitness() {\n    need_cmd head\n    # Architecture detection without dependencies beyond coreutils.\n    # ELF files start out \"\\x7fELF\", and the following byte is\n    #   0x01 for 32-bit and\n    #   0x02 for 64-bit.\n    # The printf builtin on some shells like dash only supports octal\n    # escape sequences, so we use those.\n    local _current_exe_head\n    _current_exe_head=$(head -c 5 /proc/self/exe)\n    if [ \"${_current_exe_head}\" = \"$(printf '\\177ELF\\001')\" ]; then\n        echo 32\n    elif [ \"${_current_exe_head}\" = \"$(printf '\\177ELF\\002')\" ]; then\n        echo 64\n    else\n        err \"unknown platform bitness\"\n    fi\n}\n\nget_endianness() {\n    local cputype=\"$1\"\n    local suffix_eb=\"$2\"\n    local suffix_el=\"$3\"\n\n    # detect endianness without od/hexdump, like get_bitness() does.\n    need_cmd head\n    need_cmd tail\n\n    local _current_exe_endianness\n    _current_exe_endianness=\"$(head -c 6 /proc/self/exe | tail -c 1)\"\n    if [ \"${_current_exe_endianness}\" = \"$(printf '\\001')\" ]; then\n        echo \"${cputype}${suffix_el}\"\n    elif [ \"${_current_exe_endianness}\" = \"$(printf '\\002')\" ]; then\n        echo \"${cputype}${suffix_eb}\"\n    else\n        err \"unknown platform endianness\"\n    fi\n}\n\nis_host_amd64_elf() {\n    need_cmd head\n    need_cmd tail\n    # ELF e_machine detection without dependencies beyond coreutils.\n    # Two-byte field at offset 0x12 indicates the CPU,\n    # but we're interested in it being 0x3E to indicate amd64, or not that.\n    local _current_exe_machine\n    _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1)\n    [ \"${_current_exe_machine}\" = \"$(printf '\\076')\" ]\n}\n\ncheck_proc() {\n    # Check for /proc by looking for the /proc/self/exe link.\n    # This is only run on Linux.\n    if ! test -L /proc/self/exe; then\n        err \"unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc.\"\n    fi\n}\n\nneed_cmd() {\n    if ! check_cmd \"$1\"; then\n        err \"need '$1' (command not found)\"\n    fi\n}\n\ncheck_cmd() {\n    command -v -- \"$1\" >/dev/null 2>&1\n}\n\n# Run a command that should never fail. If the command fails execution\n# will immediately terminate with an error showing the failing\n# command.\nensure() {\n    if ! \"$@\"; then err \"command failed: $*\"; fi\n}\n\nassert_nz() {\n    if [ -z \"$1\" ]; then err \"found empty string: $2\"; fi\n}\n\nerr() {\n    echo \"Error: $1\" >&2\n    exit 1\n}\n\n# This is put in braces to ensure that the script does not run until it is\n# downloaded completely.\n{\n    main \"$@\" || exit 1\n}\n"
  },
  {
    "path": "justfile",
    "content": "default:\n    @just --list\n\n[unix]\nfmt:\n    nix-shell --cores 0 --pure --run 'cargo-fmt --all'\n    nix-shell --cores 0 --pure --run 'nixfmt -- *.nix'\n    nix-shell --cores 0 --pure --run 'shfmt --indent=4 --language-dialect=posix --simplify --write *.sh'\n    nix-shell --cores 0 --pure --run 'yamlfmt -- .github/workflows/*.yml'\n\n[windows]\nfmt:\n    cargo +nightly fmt --all\n\n[unix]\nlint:\n    nix-shell --cores 0 --pure --run 'cargo-fmt --all --check'\n    nix-shell --cores 0 --pure --run 'cargo clippy --all-features --all-targets -- -Dwarnings'\n    nix-shell --cores 0 --pure --run 'cargo msrv verify'\n    nix-shell --cores 0 --pure --run 'cargo udeps --all-features --all-targets --workspace'\n    nix-shell --cores 0 --pure --run 'mandoc -man -Wall -Tlint -- man/man1/*.1'\n    nix-shell --cores 0 --pure --run 'markdownlint *.md'\n    nix-shell --cores 0 --pure --run 'nixfmt --check -- *.nix'\n    nix-shell --cores 0 --pure --run 'shellcheck --enable all *.sh'\n    nix-shell --cores 0 --pure --run 'shfmt --diff --indent=4 --language-dialect=posix --simplify *.sh'\n    nix-shell --cores 0 --pure --run 'yamlfmt -lint -- .github/workflows/*.yml'\n\n[windows]\nlint:\n    cargo +nightly fmt --all --check\n    cargo +stable clippy --all-features --all-targets -- -Dwarnings\n\n[unix]\ntest *args:\n    nix-shell --cores 0 --pure --run 'cargo nextest run --all-features --no-fail-fast --workspace {{args}}'\n\n[windows]\ntest *args:\n    cargo +stable test --no-fail-fast --workspace {{args}}\n"
  },
  {
    "path": "man/man1/zoxide-add.1",
    "content": ".TH \"ZOXIDE\" \"1\" \"2021-04-12\" \"\" \"zoxide\"\n.SH NAME\n\\fBzoxide-add\\fR - add a new directory or increment its rank\n.SH SYNOPSIS\n.B zoxide add [PATHS]\n.SH DESCRIPTION\nIf the directory is not already in the database, this command creates a new\nentry for it with a default score of 1, otherwise, it increments the existing\nscore by 1. It then sets the last updated field of the entry to the current\ntime. After this, it runs the \\fBAGING\\fR algorithm on the database. See\n\\fBzoxide\\fR(1) for more about the algorithm.\n.sp\nIf you'd like to prevent a directory from being added to the database, see the\n\\fB_ZO_EXCLUDE_DIRS\\fR environment variable in \\fBzoxide\\fR(1).\n.SH OPTIONS\n.TP\n.B -h, --help\nPrint help information.\n.SH REPORTING BUGS\nFor any issues, feature requests, or questions, please visit:\n.sp\n\\fBhttps://github.com/ajeetdsouza/zoxide/issues\\fR\n.SH AUTHOR\nAjeet D'Souza \\fB<98ajeet@gmail.com>\\fR\n"
  },
  {
    "path": "man/man1/zoxide-import.1",
    "content": ".TH \"ZOXIDE\" \"1\" \"2021-04-12\" \"\" \"zoxide\"\n.SH NAME\n\\fBzoxide-import\\fR - import data from other tools\n.SH SYNOPSIS\n.B zoxide import PATH --from FORMAT [OPTIONS]\n.SH OPTIONS\n.TP\n.B --from FORMAT\nThe format of the database being imported:\n.TS\ntab(|);\nl l.\n    \\fBautojump\\fR\n    \\fBz\\fR|(for \\fBfasd\\fR, \\fBz\\fR, \\fBz.lua\\fR, or \\fBzsh-z\\fR)\n.TE\n.sp\nNote: zoxide only imports paths from autojump, since its matching\nalgorithm is too different to import the scores.\n.TP\n.B -h, --help\nPrint help information.\n.TP\n.B --merge\nBy default, the import fails if the current database is not already empty. This\noption merges imported data into the existing database.\n.SH REPORTING BUGS\nFor any issues, feature requests, or questions, please visit:\n.sp\n\\fBhttps://github.com/ajeetdsouza/zoxide/issues\\fR\n.SH AUTHOR\nAjeet D'Souza \\fB<98ajeet@gmail.com>\\fR\n"
  },
  {
    "path": "man/man1/zoxide-init.1",
    "content": ".TH \"ZOXIDE\" \"1\" \"2021-04-12\" \"\" \"zoxide\"\n.SH NAME\n\\fBzoxide-init\\fR - generate shell configuration for zoxide\n.SH SYNOPSIS\n.B zoxide init SHELL [OPTIONS]\n.SH DESCRIPTION\nTo initialize zoxide on your shell:\n.TP\n.B bash\nAdd this to the \\fBend\\fR of your config file (usually \\fB~/.bashrc\\fR):\n.sp\n.nf\n    \\fBeval \"$(zoxide init bash)\"\\fR\n.fi\n.TP\n.B elvish\nAdd this to the \\fBend\\fR of your config file (usually \\fB~/.elvish/rc.elv\\fR):\n.sp\n.nf\n    \\fBeval $(zoxide init elvish | slurp)\\fR\n.fi\n.sp\nNote: zoxide only supports elvish v0.18.0 and above.\n.TP\n.B fish\nAdd this to the \\fBend\\fR of your config file (usually\n\\fB~/.config/fish/config.fish\\fR):\n.sp\n.nf\n    \\fBzoxide init fish | source\\fR\n.fi\n.TP\n.B nushell\nAdd this to the \\fBend\\fR of your env file (find it by running\n\\fB$nu.env-path\\fR in Nushell):\n.sp\n.nf\n    \\fBzoxide init nushell | save -f ~/.zoxide.nu\\fR\n.fi\n.sp\nNow, add this to the \\fBend\\fR of your config file (find it by running\n\\fB$nu.config-path\\fR in Nushell):\n.sp\n.nf\n    \\fBsource ~/.zoxide.nu\\fR\n.fi\n.sp\nNote: zoxide only supports Nushell v0.89.0+.\n.TP\n.B powershell\nAdd this to the \\fBend\\fR of your config file (find it by running \\fBecho\n$profile\\fR in PowerShell):\n.sp\n.nf\n    \\fBInvoke-Expression (& { (zoxide init powershell | Out-String) })\\fR\n.fi\n.TP\n.B tcsh\nAdd this to the \\fBend\\fR of your config file (usually \\fB~/.tcshrc\\fR):\n.sp\n.nf\n    \\fBzoxide init tcsh > ~/.zoxide.tcsh\\fR\n    \\fBsource ~/.zoxide.tcsh\\fR\n.fi\n.TP\n.B xonsh\nAdd this to the \\fBend\\fR of your config file (usually \\fB~/.xonshrc\\fR):\n.sp\n.nf\n    \\fBexecx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')\\fR\n.fi\n.TP\n.B zsh\nAdd this to the \\fBend\\fR of your config file (usually \\fB~/.zshrc\\fR):\n.sp\n.nf\n    \\fBeval \"$(zoxide init zsh)\"\\fR\n.fi\n.TP\n.B any POSIX shell\n.sp\nAdd this to the \\fBend\\fR of your config file:\n.sp\n.nf\n    \\fBeval \"$(zoxide init posix --hook prompt)\"\\fR\n.fi\n.SH OPTIONS\n.TP\n.B --cmd\nChanges the prefix of the \\fBz\\fR and \\fBzi\\fR commands.\n.br\n\\fB--cmd j\\fR would change the commands to (\\fBj\\fR, \\fBji\\fR).\n.br\n\\fB--cmd cd\\fR would replace the \\fBcd\\fR command (doesn't work on Nushell /\nPOSIX shells).\n.TP\n.B -h, --help\nPrint help information.\n.TP\n.B --hook HOOK\nChanges how often zoxide increments a directory's score:\n.TS\ntab(|);\nl l.\n    \\fBnone\\fR|Never\n    \\fBprompt\\fR|At every shell prompt\n    \\fBpwd\\fR|Whenever the directory is changed\n.TE\n.TP\n.B --no-cmd\nPrevents zoxide from defining the \\fBz\\fR and \\fBzi\\fR commands. These functions\nwill still be available in your shell as \\fB__zoxide_z\\fR and \\fB__zoxide_zi\\fR,\nshould you choose to redefine them.\n.SH REPORTING BUGS\nFor any issues, feature requests, or questions, please visit:\n.sp\n\\fBhttps://github.com/ajeetdsouza/zoxide/issues\\fR\n.SH AUTHOR\nAjeet D'Souza \\fB<98ajeet@gmail.com>\\fR\n"
  },
  {
    "path": "man/man1/zoxide-query.1",
    "content": ".TH \"ZOXIDE\" \"1\" \"2021-04-12\" \"\" \"zoxide\"\n.SH NAME\n\\fBzoxide-query\\fR - search for a directory in the database\n.SH SYNOPSIS\n.B zoxide query [KEYWORDS] [OPTIONS]\n.SH DESCRIPTION\nQuery the database for paths matching the keywords. The exact \\fBMATCHING\\fR\nalgorithm is described in \\fBzoxide\\fR(1).\n.SH OPTIONS\n.TP\n.B --all\nShow deleted directories.\n.TP\n.B --exclude PATH\nExclude a path from query results.\n.TP\n.B -h, --help\nPrint help information.\n.TP\n.B -i, --interactive\nUse interactive selection. This option requires \\fBfzf\\fR(1).\n.TP\n.B -l, --list\nList all results, rather than just the one with the highest frecency.\n.TP\n.B -s, --score\nPrint the calculated score as well as the matched path.\n.SH REPORTING BUGS\nFor any issues, feature requests, or questions, please visit:\n.sp\n\\fBhttps://github.com/ajeetdsouza/zoxide/issues\\fR\n.SH AUTHOR\nAjeet D'Souza \\fB<98ajeet@gmail.com>\\fR\n"
  },
  {
    "path": "man/man1/zoxide-remove.1",
    "content": ".TH \"ZOXIDE\" \"1\" \"2021-04-12\" \"\" \"zoxide\"\n.SH NAME\n\\fBzoxide-remove\\fR - remove a directory from the database\n.SH SYNOPSIS\n.B zoxide remove [PATHS] [OPTIONS]\n.SH DESCRIPTION\nIf you'd like to permanently exclude a directory from the database, see the\n\\fB_ZO_EXCLUDE_DIRS\\fR environment variable in \\fBzoxide\\fR(1).\n.SH OPTIONS\n.TP\n.B -h, --help\nPrint help information.\n.SH REPORTING BUGS\nFor any issues, feature requests, or questions, please visit:\n.sp\n\\fBhttps://github.com/ajeetdsouza/zoxide/issues\\fR\n.SH AUTHOR\nAjeet D'Souza \\fB<98ajeet@gmail.com>\\fR\n"
  },
  {
    "path": "man/man1/zoxide.1",
    "content": ".TH \"ZOXIDE\" \"1\" \"2021-04-12\" \"\" \"zoxide\"\n.SH NAME\n\\fBzoxide\\fR - a smarter cd command\n.SH SYNOPSIS\n.B zoxide SUBCOMMAND [OPTIONS]\n.SH DESCRIPTION\nzoxide is a smarter cd command for your terminal. It keeps track of the\ndirectories you use most frequently, and uses a ranking algorithm to navigate\nto the best match.\n.SH USAGE\n.nf\nz foo              # cd into highest ranked directory matching foo\nz foo bar          # cd into highest ranked directory matching foo and bar\nz foo /            # cd into a subdirectory starting with foo\n.sp\nz ~/foo            # z also works like a regular cd command\nz foo/             # cd into relative path\nz ..               # cd one level up\nz -                # cd into previous directory\n.sp\nzi foo             # cd with interactive selection (using fzf)\n.sp\nz foo<SPACE><TAB>  # show interactive completions (bash 4.4+/fish/zsh only)\n.fi\n.SH SUBCOMMANDS\n.TP\n\\fBzoxide-add\\fR(1)\nAdd a new directory to the database, or increment its rank.\n.TP\n\\fBzoxide-import\\fR(1)\nImport entries from another application.\n.TP\n\\fBzoxide-init\\fR(1)\nGenerate shell configuration.\n.TP\n\\fBzoxide-query\\fR(1)\nSearch for a directory in the database.\n.TP\n\\fBzoxide-remove\\fR(1)\nRemove a directory from the database.\n.SH OPTIONS\n.TP\n.B -h, --help\nPrint help information.\n.TP\n.B -V, --version\nPrint version information.\n.SH ENVIRONMENT VARIABLES\nEnvironment variables can be used for configuration. They must be set before\n\\fBzoxide-init\\fR(1) is called.\n.TP\n.B _ZO_DATA_DIR\nSpecifies the directory in which the database is stored. The default value\nvaries across OSes:\n.TS\ntab(|);\nl l.\n    \\fBOS|Path\\fR\n    \\fBLinux/BSD\\fR|T{\n\\fB$XDG_DATA_HOME\\fR or \\fB$HOME/.local/share\\fR, eg.\n\\fB/home/alice/.local/share\\fR\nT}\n    \\fBmacOS\\fR|T{\n\\fB$HOME/Library/Application Support\\fR, eg.\n\\fB/Users/Alice/Library/Application Support\\fR\nT}\n    \\fBWindows\\fR|T{\n\\fB%LOCALAPPDATA%\\fR, eg. \\fBC:\\\\Users\\\\Alice\\\\AppData\\\\Local\\fR\nT}\n.TE\n.TP\n.B _ZO_ECHO\nWhen set to 1, \\fBz\\fR will print the matched directory before navigating to it.\n.TP\n.B _ZO_EXCLUDE_DIRS\nPrevents the specified directories from being added to the database. This is\nprovided as a list of globs, separated by OS-specific characters:\n.TS\ntab(|);\nl l.\n    \\fBOS|Separator\\fR\n    \\fBLinux/macOS/BSD\\fR|T{\n\\fB:\\fR, eg. \\fB$HOME:$HOME/private/*\\fR\nT}\n    \\fBWindows\\fR|\\fB;\\fR, eg. \\fB$HOME;$HOME/private/*\\fR\n.TE\n.sp\nBy default, this is set to \\fB$HOME\\fR. After setting this up, you might need\nto use \\fBzoxide-remove\\fR(1) to remove any existing entries from the database.\n.TP\n.B _ZO_FZF_OPTS\nCustom options to pass to \\fBfzf\\fR(1) during interactive selection. See the\nmanpage for the full list of options.\n.TP\n.B _ZO_MAXAGE\nConfigures the aging algorithm, which limits the maximum number of entries in\nthe database. By default, this is set to 10000.\n.TP\n.B _ZO_RESOLVE_SYMLINKS\nWhen set to 1, \\fBz\\fR will resolve symlinks before adding directories to\nthe database.\n.SH ALGORITHM\n.TP\n.B AGING\nzoxide uses a parameter called \\fB_ZO_MAXAGE\\fR to limit the number of entries\nin the database based on usage patterns. If the total \\fBFRECENCY\\fR of the\ndirectories in the database exceeds this value, we divide each directory's\nscore by a factor \\fBk\\fR - such that the new total becomes ~90% of\n\\fB_ZO_MAXAGE\\fR. Thereafter, if the new score of any directory falls below\n1, it is removed from the database.\n.sp\nTheoretically, the maximum number of directories in the database is\n\\fB4 * _ZO_MAXAGE\\fR, although it is lower in practice.\n.TP\n.B FRECENCY\nEach directory in zoxide is given a score, starting with 1 the first time\nit is accessed. Every subsequent access increases the score by 1. When a\nquery is made, we calculate frecency based on the last time the directory was\naccessed:\n.TS\ntab(|);\nl l.\n    \\fBLast access time\\fR|\\fBFrecency\\fR\n    Within the last hour|score * 4\n    Within the last day|score * 2\n    Within the last week|score / 2\n    Otherwise|score / 4\n.TE\n.SH REPORTING BUGS\nFor any issues, feature requests, or questions, please visit:\n.sp\n\\fBhttps://github.com/ajeetdsouza/zoxide/issues\\fR\n.SH AUTHOR\nAjeet D'Souza \\fB<98ajeet@gmail.com>\\fR\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "group_imports = \"StdExternalCrate\"\nimports_granularity = \"Module\"\nnewline_style = \"Native\"\nuse_field_init_shorthand = true\nuse_small_heuristics = \"Max\"\nuse_try_shorthand = true\nwrap_comments = true\nstyle_edition = \"2024\"\n"
  },
  {
    "path": "shell.nix",
    "content": "let\n  pkgs = import (builtins.fetchTarball\n    \"https://github.com/NixOS/nixpkgs/archive/ec9ef366451af88284d7dfd18ee017b7e86a0710.tar.gz\") {\n      overlays = [ rust ];\n    };\n  rust = import (builtins.fetchTarball\n    \"https://github.com/oxalica/rust-overlay/archive/026e8fedefd6b167d92ed04b195c658d95ffc7a5.tar.gz\");\n\n  rust-nightly =\n    pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal);\n  cargo-udeps = pkgs.writeShellScriptBin \"cargo-udeps\" ''\n    export RUSTC=\"${rust-nightly}/bin/rustc\";\n    export CARGO=\"${rust-nightly}/bin/cargo\";\n    exec \"${pkgs.cargo-udeps}/bin/cargo-udeps\" \"$@\"\n  '';\nin pkgs.mkShell {\n  buildInputs = [\n    # Rust\n    (pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.rustfmt))\n    pkgs.rust-bin.stable.latest.default\n\n    # Shells\n    pkgs.bash\n    pkgs.dash\n    pkgs.elvish\n    pkgs.fish\n    pkgs.ksh\n    pkgs.nushell\n    pkgs.powershell\n    pkgs.tcsh\n    pkgs.xonsh\n    pkgs.zsh\n\n    # Tools\n    cargo-udeps\n    pkgs.cargo-msrv\n    pkgs.cargo-nextest\n    pkgs.cargo-udeps\n    pkgs.just\n    pkgs.mandoc\n    pkgs.nixfmt\n    pkgs.nodePackages.markdownlint-cli\n    pkgs.python3Packages.black\n    pkgs.python3Packages.mypy\n    pkgs.python3Packages.pylint\n    pkgs.shellcheck\n    pkgs.shfmt\n    pkgs.yamlfmt\n\n    # Dependencies\n    pkgs.cacert\n    pkgs.fzf\n    pkgs.git\n    pkgs.libiconv\n  ];\n\n  CARGO_TARGET_DIR = \"target_nix\";\n}\n"
  },
  {
    "path": "src/cmd/add.rs",
    "content": "use std::path::Path;\n\nuse anyhow::{Result, bail};\n\nuse crate::cmd::{Add, Run};\nuse crate::db::Database;\nuse crate::{config, util};\n\nimpl Run for Add {\n    fn run(&self) -> Result<()> {\n        // These characters can't be printed cleanly to a single line, so they can cause\n        // confusion when writing to stdout.\n        const EXCLUDE_CHARS: &[char] = &['\\n', '\\r'];\n\n        let exclude_dirs = config::exclude_dirs()?;\n        let max_age = config::maxage()?;\n        let now = util::current_time()?;\n\n        let mut db = Database::open()?;\n\n        for path in &self.paths {\n            let path =\n                if config::resolve_symlinks() { util::canonicalize } else { util::resolve_path }(\n                    path,\n                )?;\n            let path = util::path_to_str(&path)?;\n\n            // Ignore path if it contains unsupported characters, or if it's in the exclude\n            // list.\n            if path.contains(EXCLUDE_CHARS) || exclude_dirs.iter().any(|glob| glob.matches(path)) {\n                continue;\n            }\n            if !Path::new(path).is_dir() {\n                bail!(\"not a directory: {path}\");\n            }\n\n            let by = self.score.unwrap_or(1.0);\n            db.add_update(path, by, now);\n        }\n\n        if db.dirty() {\n            db.age(max_age);\n        }\n        db.save()\n    }\n}\n"
  },
  {
    "path": "src/cmd/cmd.rs",
    "content": "#![allow(clippy::module_inception)]\n\nuse std::path::PathBuf;\n\nuse clap::builder::{IntoResettable, Resettable, StyledStr};\nuse clap::{Parser, Subcommand, ValueEnum, ValueHint};\n\nstruct HelpTemplate;\n\nimpl IntoResettable<StyledStr> for HelpTemplate {\n    fn into_resettable(self) -> Resettable<StyledStr> {\n        color_print::cstr!(\"\\\n{before-help}<bold><underline>{name} {version}</underline></bold>\n{author}\nhttps://github.com/ajeetdsouza/zoxide\n\n{about}\n\n{usage-heading}\n{tab}{usage}\n\n{all-args}{after-help}\n\n<bold><underline>Environment variables:</underline></bold>\n{tab}<bold>_ZO_DATA_DIR</bold>        {tab}Path for zoxide data files\n{tab}<bold>_ZO_ECHO</bold>            {tab}Print the matched directory before navigating to it when set to 1\n{tab}<bold>_ZO_EXCLUDE_DIRS</bold>    {tab}List of directory globs to be excluded\n{tab}<bold>_ZO_FZF_OPTS</bold>        {tab}Custom flags to pass to fzf\n{tab}<bold>_ZO_MAXAGE</bold>          {tab}Maximum total age after which entries start getting deleted\n{tab}<bold>_ZO_RESOLVE_SYMLINKS</bold>{tab}Resolve symlinks when storing paths\").into_resettable()\n    }\n}\n\n#[derive(Debug, Parser)]\n#[clap(\n    about,\n    author,\n    help_template = HelpTemplate,\n    disable_help_subcommand = true,\n    propagate_version = true,\n    version,\n)]\npub enum Cmd {\n    Add(Add),\n    Edit(Edit),\n    Import(Import),\n    Init(Init),\n    Query(Query),\n    Remove(Remove),\n}\n\n/// Add a new directory or increment its rank\n#[derive(Debug, Parser)]\n#[clap(\n    author,\n    help_template = HelpTemplate,\n)]\npub struct Add {\n    #[clap(num_args = 1.., required = true, value_hint = ValueHint::DirPath)]\n    pub paths: Vec<PathBuf>,\n\n    /// The rank to increment the entry if it exists or initialize it with if it\n    /// doesn't\n    #[clap(short, long)]\n    pub score: Option<f64>,\n}\n\n/// Edit the database\n#[derive(Debug, Parser)]\n#[clap(\n    author,\n    help_template = HelpTemplate,\n)]\npub struct Edit {\n    #[clap(subcommand)]\n    pub cmd: Option<EditCommand>,\n}\n\n#[derive(Clone, Debug, Subcommand)]\npub enum EditCommand {\n    #[clap(hide = true)]\n    Decrement { path: String },\n    #[clap(hide = true)]\n    Delete { path: String },\n    #[clap(hide = true)]\n    Increment { path: String },\n    #[clap(hide = true)]\n    Reload,\n}\n\n/// Import entries from another application\n#[derive(Debug, Parser)]\n#[clap(\n    author,\n    help_template = HelpTemplate,\n)]\npub struct Import {\n    #[clap(value_hint = ValueHint::FilePath)]\n    pub path: PathBuf,\n\n    /// Application to import from\n    #[clap(value_enum, long)]\n    pub from: ImportFrom,\n\n    /// Merge into existing database\n    #[clap(long)]\n    pub merge: bool,\n}\n\n#[derive(ValueEnum, Clone, Debug)]\npub enum ImportFrom {\n    Autojump,\n    #[clap(alias = \"fasd\")]\n    Z,\n}\n\n/// Generate shell configuration\n#[derive(Debug, Parser)]\n#[clap(\n    author,\n    help_template = HelpTemplate,\n)]\npub struct Init {\n    #[clap(value_enum)]\n    pub shell: InitShell,\n\n    /// Prevents zoxide from defining the `z` and `zi` commands\n    #[clap(long, alias = \"no-aliases\")]\n    pub no_cmd: bool,\n\n    /// Changes the prefix of the `z` and `zi` commands\n    #[clap(long, default_value = \"z\")]\n    pub cmd: String,\n\n    /// Changes how often zoxide increments a directory's score\n    #[clap(value_enum, long, default_value = \"pwd\")]\n    pub hook: InitHook,\n}\n\n#[derive(ValueEnum, Clone, Copy, Debug, Eq, PartialEq)]\npub enum InitHook {\n    None,\n    Prompt,\n    Pwd,\n}\n\n#[derive(ValueEnum, Clone, Debug)]\npub enum InitShell {\n    Bash,\n    Elvish,\n    Fish,\n    Nushell,\n    #[clap(alias = \"ksh\")]\n    Posix,\n    Powershell,\n    Tcsh,\n    Xonsh,\n    Zsh,\n}\n\n/// Search for a directory in the database\n#[derive(Debug, Parser)]\n#[clap(\n    author,\n    help_template = HelpTemplate,\n)]\npub struct Query {\n    pub keywords: Vec<String>,\n\n    /// Show unavailable directories\n    #[clap(long, short)]\n    pub all: bool,\n\n    /// Use interactive selection\n    #[clap(long, short, conflicts_with = \"list\")]\n    pub interactive: bool,\n\n    /// List all matching directories\n    #[clap(long, short, conflicts_with = \"interactive\")]\n    pub list: bool,\n\n    /// Print score with results\n    #[clap(long, short)]\n    pub score: bool,\n\n    /// Exclude the current directory\n    #[clap(long, value_hint = ValueHint::DirPath, value_name = \"path\")]\n    pub exclude: Option<String>,\n\n    /// Only search within this directory\n    #[clap(long, value_hint = ValueHint::DirPath, value_name = \"path\")]\n    pub base_dir: Option<String>,\n}\n\n/// Remove a directory from the database\n#[derive(Debug, Parser)]\n#[clap(\n    author,\n    help_template = HelpTemplate,\n)]\npub struct Remove {\n    #[clap(value_hint = ValueHint::DirPath)]\n    pub paths: Vec<String>,\n}\n"
  },
  {
    "path": "src/cmd/edit.rs",
    "content": "use std::io::{self, Write};\n\nuse anyhow::Result;\n\nuse crate::cmd::{Edit, EditCommand, Run};\nuse crate::db::Database;\nuse crate::error::BrokenPipeHandler;\nuse crate::util::{self, Fzf, FzfChild};\n\nimpl Run for Edit {\n    fn run(&self) -> Result<()> {\n        let now = util::current_time()?;\n        let db = &mut Database::open()?;\n\n        match &self.cmd {\n            Some(cmd) => {\n                match cmd {\n                    EditCommand::Decrement { path } => db.add(path, -1.0, now),\n                    EditCommand::Delete { path } => {\n                        db.remove(path);\n                    }\n                    EditCommand::Increment { path } => db.add(path, 1.0, now),\n                    EditCommand::Reload => {}\n                }\n                db.save()?;\n\n                let stdout = &mut io::stdout().lock();\n                for dir in db.dirs().iter().rev() {\n                    write!(stdout, \"{}\\0\", dir.display().with_score(now).with_separator('\\t'))\n                        .pipe_exit(\"fzf\")?;\n                }\n                Ok(())\n            }\n            None => {\n                db.sort_by_score(now);\n                db.save()?;\n                Self::get_fzf()?.wait()?;\n                Ok(())\n            }\n        }\n    }\n}\n\nimpl Edit {\n    fn get_fzf() -> Result<FzfChild> {\n        Fzf::new()?\n            .args([\n                // Search mode\n                \"--exact\",\n                // Search result\n                \"--no-sort\",\n                // Interface\n                \"--bind=\\\nbtab:up,\\\nctrl-r:reload(zoxide edit reload),\\\nctrl-d:reload(zoxide edit delete {2..}),\\\nctrl-w:reload(zoxide edit increment {2..}),\\\nctrl-s:reload(zoxide edit decrement {2..}),\\\nctrl-z:ignore,\\\ndouble-click:ignore,\\\nenter:abort,\\\nstart:reload(zoxide edit reload),\\\ntab:down\",\n                \"--cycle\",\n                \"--keep-right\",\n                // Layout\n                \"--border=sharp\",\n                \"--border-label=  zoxide-edit  \",\n                \"--header=\\\nctrl-r:reload   \\tctrl-d:delete\nctrl-w:increment\\tctrl-s:decrement\n\n SCORE\\tPATH\",\n                \"--info=inline\",\n                \"--layout=reverse\",\n                \"--padding=1,0,0,0\",\n                // Display\n                \"--color=label:bold\",\n                \"--tabstop=1\",\n            ])\n            .enable_preview()\n            .spawn()\n    }\n}\n"
  },
  {
    "path": "src/cmd/import.rs",
    "content": "use std::fs;\n\nuse anyhow::{Context, Result, bail};\n\nuse crate::cmd::{Import, ImportFrom, Run};\nuse crate::db::Database;\n\nimpl Run for Import {\n    fn run(&self) -> Result<()> {\n        let buffer = fs::read_to_string(&self.path).with_context(|| {\n            format!(\"could not open database for importing: {}\", &self.path.display())\n        })?;\n\n        let mut db = Database::open()?;\n        if !self.merge && !db.dirs().is_empty() {\n            bail!(\"current database is not empty, specify --merge to continue anyway\");\n        }\n\n        match self.from {\n            ImportFrom::Autojump => import_autojump(&mut db, &buffer),\n            ImportFrom::Z => import_z(&mut db, &buffer),\n        }\n        .context(\"import error\")?;\n\n        db.save()\n    }\n}\n\nfn import_autojump(db: &mut Database, buffer: &str) -> Result<()> {\n    for line in buffer.lines() {\n        if line.is_empty() {\n            continue;\n        }\n        let (rank, path) =\n            line.split_once('\\t').with_context(|| format!(\"invalid entry: {line}\"))?;\n\n        let mut rank = rank.parse::<f64>().with_context(|| format!(\"invalid rank: {rank}\"))?;\n        // Normalize the rank using a sigmoid function. Don't import actual ranks from\n        // autojump, since its scoring algorithm is very different and might\n        // take a while to normalize.\n        rank = sigmoid(rank);\n\n        db.add_unchecked(path, rank, 0);\n    }\n\n    if db.dirty() {\n        db.dedup();\n    }\n    Ok(())\n}\n\nfn import_z(db: &mut Database, buffer: &str) -> Result<()> {\n    for line in buffer.lines() {\n        if line.is_empty() {\n            continue;\n        }\n        let mut split = line.rsplitn(3, '|');\n\n        let last_accessed = split.next().with_context(|| format!(\"invalid entry: {line}\"))?;\n        let last_accessed =\n            last_accessed.parse().with_context(|| format!(\"invalid epoch: {last_accessed}\"))?;\n\n        let rank = split.next().with_context(|| format!(\"invalid entry: {line}\"))?;\n        let rank = rank.parse().with_context(|| format!(\"invalid rank: {rank}\"))?;\n\n        let path = split.next().with_context(|| format!(\"invalid entry: {line}\"))?;\n\n        db.add_unchecked(path, rank, last_accessed);\n    }\n\n    if db.dirty() {\n        db.dedup();\n    }\n    Ok(())\n}\n\nfn sigmoid(x: f64) -> f64 {\n    1.0 / (1.0 + (-x).exp())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::db::Dir;\n\n    #[test]\n    fn from_autojump() {\n        let data_dir = tempfile::tempdir().unwrap();\n        let mut db = Database::open_dir(data_dir.path()).unwrap();\n        for (path, rank, last_accessed) in [\n            (\"/quux/quuz\", 1.0, 100),\n            (\"/corge/grault/garply\", 6.0, 600),\n            (\"/waldo/fred/plugh\", 3.0, 300),\n            (\"/xyzzy/thud\", 8.0, 800),\n            (\"/foo/bar\", 9.0, 900),\n        ] {\n            db.add_unchecked(path, rank, last_accessed);\n        }\n\n        let buffer = \"\\\n7.0\t/baz\n2.0\t/foo/bar\n5.0\t/quux/quuz\";\n        import_autojump(&mut db, buffer).unwrap();\n\n        db.sort_by_path();\n        println!(\"got: {:?}\", &db.dirs());\n\n        let exp = [\n            Dir { path: \"/baz\".into(), rank: sigmoid(7.0), last_accessed: 0 },\n            Dir { path: \"/corge/grault/garply\".into(), rank: 6.0, last_accessed: 600 },\n            Dir { path: \"/foo/bar\".into(), rank: 9.0 + sigmoid(2.0), last_accessed: 900 },\n            Dir { path: \"/quux/quuz\".into(), rank: 1.0 + sigmoid(5.0), last_accessed: 100 },\n            Dir { path: \"/waldo/fred/plugh\".into(), rank: 3.0, last_accessed: 300 },\n            Dir { path: \"/xyzzy/thud\".into(), rank: 8.0, last_accessed: 800 },\n        ];\n        println!(\"exp: {exp:?}\");\n\n        for (dir1, dir2) in db.dirs().iter().zip(exp) {\n            assert_eq!(dir1.path, dir2.path);\n            assert!((dir1.rank - dir2.rank).abs() < 0.01);\n            assert_eq!(dir1.last_accessed, dir2.last_accessed);\n        }\n    }\n\n    #[test]\n    fn from_z() {\n        let data_dir = tempfile::tempdir().unwrap();\n        let mut db = Database::open_dir(data_dir.path()).unwrap();\n        for (path, rank, last_accessed) in [\n            (\"/quux/quuz\", 1.0, 100),\n            (\"/corge/grault/garply\", 6.0, 600),\n            (\"/waldo/fred/plugh\", 3.0, 300),\n            (\"/xyzzy/thud\", 8.0, 800),\n            (\"/foo/bar\", 9.0, 900),\n        ] {\n            db.add_unchecked(path, rank, last_accessed);\n        }\n\n        let buffer = \"\\\n/baz|7|700\n/quux/quuz|4|400\n/foo/bar|2|200\n/quux/quuz|5|500\";\n        import_z(&mut db, buffer).unwrap();\n\n        db.sort_by_path();\n        println!(\"got: {:?}\", &db.dirs());\n\n        let exp = [\n            Dir { path: \"/baz\".into(), rank: 7.0, last_accessed: 700 },\n            Dir { path: \"/corge/grault/garply\".into(), rank: 6.0, last_accessed: 600 },\n            Dir { path: \"/foo/bar\".into(), rank: 11.0, last_accessed: 900 },\n            Dir { path: \"/quux/quuz\".into(), rank: 10.0, last_accessed: 500 },\n            Dir { path: \"/waldo/fred/plugh\".into(), rank: 3.0, last_accessed: 300 },\n            Dir { path: \"/xyzzy/thud\".into(), rank: 8.0, last_accessed: 800 },\n        ];\n        println!(\"exp: {exp:?}\");\n\n        for (dir1, dir2) in db.dirs().iter().zip(exp) {\n            assert_eq!(dir1.path, dir2.path);\n            assert!((dir1.rank - dir2.rank).abs() < 0.01);\n            assert_eq!(dir1.last_accessed, dir2.last_accessed);\n        }\n    }\n}\n"
  },
  {
    "path": "src/cmd/init.rs",
    "content": "use std::io::{self, Write};\n\nuse anyhow::{Context, Result};\nuse askama::Template;\n\nuse crate::cmd::{Init, InitShell, Run};\nuse crate::config;\nuse crate::error::BrokenPipeHandler;\nuse crate::shell::{Bash, Elvish, Fish, Nushell, Opts, Posix, Powershell, Tcsh, Xonsh, Zsh};\n\nimpl Run for Init {\n    fn run(&self) -> Result<()> {\n        let cmd = if self.no_cmd { None } else { Some(self.cmd.as_str()) };\n        let echo = config::echo();\n        let resolve_symlinks = config::resolve_symlinks();\n        let opts = &Opts { cmd, hook: self.hook, echo, resolve_symlinks };\n\n        let source = match self.shell {\n            InitShell::Bash => Bash(opts).render(),\n            InitShell::Elvish => Elvish(opts).render(),\n            InitShell::Fish => Fish(opts).render(),\n            InitShell::Nushell => Nushell(opts).render(),\n            InitShell::Posix => Posix(opts).render(),\n            InitShell::Powershell => Powershell(opts).render(),\n            InitShell::Tcsh => Tcsh(opts).render(),\n            InitShell::Xonsh => Xonsh(opts).render(),\n            InitShell::Zsh => Zsh(opts).render(),\n        }\n        .context(\"could not render template\")?;\n        writeln!(io::stdout(), \"{source}\").pipe_exit(\"stdout\")\n    }\n}\n"
  },
  {
    "path": "src/cmd/mod.rs",
    "content": "mod add;\nmod cmd;\nmod edit;\nmod import;\nmod init;\nmod query;\nmod remove;\n\nuse anyhow::Result;\n\npub use crate::cmd::cmd::*;\n\npub trait Run {\n    fn run(&self) -> Result<()>;\n}\n\nimpl Run for Cmd {\n    fn run(&self) -> Result<()> {\n        match self {\n            Cmd::Add(cmd) => cmd.run(),\n            Cmd::Edit(cmd) => cmd.run(),\n            Cmd::Import(cmd) => cmd.run(),\n            Cmd::Init(cmd) => cmd.run(),\n            Cmd::Query(cmd) => cmd.run(),\n            Cmd::Remove(cmd) => cmd.run(),\n        }\n    }\n}\n"
  },
  {
    "path": "src/cmd/query.rs",
    "content": "use std::io::{self, Write};\n\nuse anyhow::{Context, Result};\n\nuse crate::cmd::{Query, Run};\nuse crate::config;\nuse crate::db::{Database, Epoch, Stream, StreamOptions};\nuse crate::error::BrokenPipeHandler;\nuse crate::util::{self, Fzf, FzfChild};\n\nimpl Run for Query {\n    fn run(&self) -> Result<()> {\n        let mut db = crate::db::Database::open()?;\n        self.query(&mut db).and(db.save())\n    }\n}\n\nimpl Query {\n    fn query(&self, db: &mut Database) -> Result<()> {\n        let now = util::current_time()?;\n        let mut stream = self.get_stream(db, now)?;\n\n        if self.interactive {\n            self.query_interactive(&mut stream, now)\n        } else if self.list {\n            self.query_list(&mut stream, now)\n        } else {\n            self.query_first(&mut stream, now)\n        }\n    }\n\n    fn query_interactive(&self, stream: &mut Stream, now: Epoch) -> Result<()> {\n        let mut fzf = Self::get_fzf()?;\n        let selection = loop {\n            match stream.next() {\n                Some(dir) if Some(dir.path.as_ref()) == self.exclude.as_deref() => continue,\n                Some(dir) => {\n                    if let Some(selection) = fzf.write(dir, now)? {\n                        break selection;\n                    }\n                }\n                None => break fzf.wait()?,\n            }\n        };\n\n        if self.score {\n            print!(\"{selection}\");\n        } else {\n            let path = selection.get(7..).context(\"could not read selection from fzf\")?;\n            print!(\"{path}\");\n        }\n        Ok(())\n    }\n\n    fn query_list(&self, stream: &mut Stream, now: Epoch) -> Result<()> {\n        let handle = &mut io::stdout().lock();\n        while let Some(dir) = stream.next() {\n            if Some(dir.path.as_ref()) == self.exclude.as_deref() {\n                continue;\n            }\n            let dir = if self.score { dir.display().with_score(now) } else { dir.display() };\n            writeln!(handle, \"{dir}\").pipe_exit(\"stdout\")?;\n        }\n        Ok(())\n    }\n\n    fn query_first(&self, stream: &mut Stream, now: Epoch) -> Result<()> {\n        let handle = &mut io::stdout();\n\n        let mut dir = stream.next().context(\"no match found\")?;\n        while Some(dir.path.as_ref()) == self.exclude.as_deref() {\n            dir = stream.next().context(\"you are already in the only match\")?;\n        }\n\n        let dir = if self.score { dir.display().with_score(now) } else { dir.display() };\n        writeln!(handle, \"{dir}\").pipe_exit(\"stdout\")\n    }\n\n    fn get_stream<'a>(&self, db: &'a mut Database, now: Epoch) -> Result<Stream<'a>> {\n        let mut options = StreamOptions::new(now)\n            .with_keywords(self.keywords.iter().map(|s| s.as_str()))\n            .with_exclude(config::exclude_dirs()?)\n            .with_base_dir(self.base_dir.clone());\n        if !self.all {\n            let resolve_symlinks = config::resolve_symlinks();\n            options = options.with_exists(true).with_resolve_symlinks(resolve_symlinks);\n        }\n\n        let stream = Stream::new(db, options);\n        Ok(stream)\n    }\n\n    fn get_fzf() -> Result<FzfChild> {\n        let mut fzf = Fzf::new()?;\n        if let Some(fzf_opts) = config::fzf_opts() {\n            fzf.env(\"FZF_DEFAULT_OPTS\", fzf_opts)\n        } else {\n            fzf.args([\n                // Search mode\n                \"--exact\",\n                // Search result\n                \"--no-sort\",\n                // Interface\n                \"--bind=ctrl-z:ignore,btab:up,tab:down\",\n                \"--cycle\",\n                \"--keep-right\",\n                // Layout\n                \"--border=sharp\", // rounded edges don't display correctly on some terminals\n                \"--height=45%\",\n                \"--info=inline\",\n                \"--layout=reverse\",\n                // Display\n                \"--tabstop=1\",\n                // Scripting\n                \"--exit-0\",\n            ])\n            .enable_preview()\n        }\n        .spawn()\n    }\n}\n"
  },
  {
    "path": "src/cmd/remove.rs",
    "content": "use anyhow::{Result, bail};\n\nuse crate::cmd::{Remove, Run};\nuse crate::db::Database;\nuse crate::util;\n\nimpl Run for Remove {\n    fn run(&self) -> Result<()> {\n        let mut db = Database::open()?;\n\n        for path in &self.paths {\n            if !db.remove(path) {\n                let path_abs = util::resolve_path(path)?;\n                let path_abs = util::path_to_str(&path_abs)?;\n                if path_abs == path || !db.remove(path_abs) {\n                    bail!(\"path not found in database: {path}\")\n                }\n            }\n        }\n\n        db.save()\n    }\n}\n"
  },
  {
    "path": "src/config.rs",
    "content": "use std::env;\nuse std::ffi::OsString;\nuse std::path::PathBuf;\n\nuse anyhow::{Context, Result, ensure};\nuse glob::Pattern;\n\nuse crate::db::Rank;\n\npub fn data_dir() -> Result<PathBuf> {\n    let dir = match env::var_os(\"_ZO_DATA_DIR\") {\n        Some(path) => PathBuf::from(path),\n        None => dirs::data_local_dir()\n            .context(\"could not find data directory, please set _ZO_DATA_DIR manually\")?\n            .join(\"zoxide\"),\n    };\n\n    ensure!(dir.is_absolute(), \"_ZO_DATA_DIR must be an absolute path\");\n    Ok(dir)\n}\n\npub fn echo() -> bool {\n    env::var_os(\"_ZO_ECHO\").is_some_and(|var| var == \"1\")\n}\n\npub fn exclude_dirs() -> Result<Vec<Pattern>> {\n    match env::var_os(\"_ZO_EXCLUDE_DIRS\") {\n        Some(paths) => env::split_paths(&paths)\n            .map(|path| {\n                let pattern = path.to_str().context(\"invalid unicode in _ZO_EXCLUDE_DIRS\")?;\n                Pattern::new(pattern)\n                    .with_context(|| format!(\"invalid glob in _ZO_EXCLUDE_DIRS: {pattern}\"))\n            })\n            .collect(),\n        None => {\n            let pattern = (|| {\n                let home = dirs::home_dir()?;\n                let home = Pattern::escape(home.to_str()?);\n                Pattern::new(&home).ok()\n            })();\n            Ok(pattern.into_iter().collect())\n        }\n    }\n}\n\npub fn fzf_opts() -> Option<OsString> {\n    env::var_os(\"_ZO_FZF_OPTS\")\n}\n\npub fn maxage() -> Result<Rank> {\n    env::var_os(\"_ZO_MAXAGE\").map_or(Ok(10_000.0), |maxage| {\n        let maxage = maxage.to_str().context(\"invalid unicode in _ZO_MAXAGE\")?;\n        let maxage = maxage\n            .parse::<u32>()\n            .with_context(|| format!(\"unable to parse _ZO_MAXAGE as integer: {maxage}\"))?;\n        Ok(maxage as Rank)\n    })\n}\n\npub fn resolve_symlinks() -> bool {\n    env::var_os(\"_ZO_RESOLVE_SYMLINKS\").is_some_and(|var| var == \"1\")\n}\n"
  },
  {
    "path": "src/db/dir.rs",
    "content": "use std::borrow::Cow;\nuse std::fmt::{self, Display, Formatter};\n\nuse serde::{Deserialize, Serialize};\n\nuse crate::util::{DAY, HOUR, WEEK};\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub struct Dir<'a> {\n    #[serde(borrow)]\n    pub path: Cow<'a, str>,\n    pub rank: Rank,\n    pub last_accessed: Epoch,\n}\n\nimpl Dir<'_> {\n    pub fn display(&self) -> DirDisplay<'_> {\n        DirDisplay::new(self)\n    }\n\n    pub fn score(&self, now: Epoch) -> Rank {\n        // The older the entry, the lesser its importance.\n        let duration = now.saturating_sub(self.last_accessed);\n        if duration < HOUR {\n            self.rank * 4.0\n        } else if duration < DAY {\n            self.rank * 2.0\n        } else if duration < WEEK {\n            self.rank * 0.5\n        } else {\n            self.rank * 0.25\n        }\n    }\n}\n\npub struct DirDisplay<'a> {\n    dir: &'a Dir<'a>,\n    now: Option<Epoch>,\n    separator: char,\n}\n\nimpl<'a> DirDisplay<'a> {\n    fn new(dir: &'a Dir) -> Self {\n        Self { dir, separator: ' ', now: None }\n    }\n\n    pub fn with_score(mut self, now: Epoch) -> Self {\n        self.now = Some(now);\n        self\n    }\n\n    pub fn with_separator(mut self, separator: char) -> Self {\n        self.separator = separator;\n        self\n    }\n}\n\nimpl Display for DirDisplay<'_> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        if let Some(now) = self.now {\n            let score = self.dir.score(now).clamp(0.0, 9999.0);\n            write!(f, \"{score:>6.1}{}\", self.separator)?;\n        }\n        write!(f, \"{}\", self.dir.path)\n    }\n}\n\npub type Rank = f64;\npub type Epoch = u64;\n"
  },
  {
    "path": "src/db/mod.rs",
    "content": "mod dir;\nmod stream;\n\nuse std::path::{Path, PathBuf};\nuse std::{fs, io};\n\nuse anyhow::{Context, Result, bail};\nuse bincode::Options;\nuse ouroboros::self_referencing;\n\npub use crate::db::dir::{Dir, Epoch, Rank};\npub use crate::db::stream::{Stream, StreamOptions};\nuse crate::{config, util};\n\n#[self_referencing]\npub struct Database {\n    path: PathBuf,\n    bytes: Vec<u8>,\n    #[borrows(bytes)]\n    #[covariant]\n    pub dirs: Vec<Dir<'this>>,\n    dirty: bool,\n}\n\nimpl Database {\n    const VERSION: u32 = 3;\n\n    pub fn open() -> Result<Self> {\n        let data_dir = config::data_dir()?;\n        Self::open_dir(data_dir)\n    }\n\n    pub fn open_dir(data_dir: impl AsRef<Path>) -> Result<Self> {\n        let data_dir = data_dir.as_ref();\n        let path = data_dir.join(\"db.zo\");\n        let path = fs::canonicalize(&path).unwrap_or(path);\n\n        match fs::read(&path) {\n            Ok(bytes) => Self::try_new(path, bytes, |bytes| Self::deserialize(bytes), false),\n            Err(e) if e.kind() == io::ErrorKind::NotFound => {\n                // Create data directory, but don't create any file yet. The file will be\n                // created later by [`Database::save`] if any data is modified.\n                fs::create_dir_all(data_dir).with_context(|| {\n                    format!(\"unable to create data directory: {}\", data_dir.display())\n                })?;\n                Ok(Self::new(path, Vec::new(), |_| Vec::new(), false))\n            }\n            Err(e) => {\n                Err(e).with_context(|| format!(\"could not read from database: {}\", path.display()))\n            }\n        }\n    }\n\n    pub fn save(&mut self) -> Result<()> {\n        // Only write to disk if the database is modified.\n        if !self.dirty() {\n            return Ok(());\n        }\n\n        let bytes = Self::serialize(self.dirs())?;\n        util::write(self.borrow_path(), bytes).context(\"could not write to database\")?;\n        self.with_dirty_mut(|dirty| *dirty = false);\n\n        Ok(())\n    }\n\n    /// Increments the rank of a directory, or creates it if it does not exist.\n    pub fn add(&mut self, path: impl AsRef<str> + Into<String>, by: Rank, now: Epoch) {\n        self.with_dirs_mut(|dirs| match dirs.iter_mut().find(|dir| dir.path == path.as_ref()) {\n            Some(dir) => dir.rank = (dir.rank + by).max(0.0),\n            None => {\n                dirs.push(Dir { path: path.into().into(), rank: by.max(0.0), last_accessed: now })\n            }\n        });\n        self.with_dirty_mut(|dirty| *dirty = true);\n    }\n\n    /// Creates a new directory. This will create a duplicate entry if this\n    /// directory is already in the database, it is expected that the user\n    /// either does a check before calling this, or calls `dedup()`\n    /// afterward.\n    pub fn add_unchecked(&mut self, path: impl AsRef<str> + Into<String>, rank: Rank, now: Epoch) {\n        self.with_dirs_mut(|dirs| {\n            dirs.push(Dir { path: path.into().into(), rank, last_accessed: now })\n        });\n        self.with_dirty_mut(|dirty| *dirty = true);\n    }\n\n    /// Increments the rank and updates the last_accessed of a directory, or\n    /// creates it if it does not exist.\n    pub fn add_update(&mut self, path: impl AsRef<str> + Into<String>, by: Rank, now: Epoch) {\n        self.with_dirs_mut(|dirs| match dirs.iter_mut().find(|dir| dir.path == path.as_ref()) {\n            Some(dir) => {\n                dir.rank = (dir.rank + by).max(0.0);\n                dir.last_accessed = now;\n            }\n            None => {\n                dirs.push(Dir { path: path.into().into(), rank: by.max(0.0), last_accessed: now })\n            }\n        });\n        self.with_dirty_mut(|dirty| *dirty = true);\n    }\n\n    /// Removes the directory with `path` from the store. This does not preserve\n    /// ordering, but is O(1).\n    pub fn remove(&mut self, path: impl AsRef<str>) -> bool {\n        match self.dirs().iter().position(|dir| dir.path == path.as_ref()) {\n            Some(idx) => {\n                self.swap_remove(idx);\n                true\n            }\n            None => false,\n        }\n    }\n\n    pub fn swap_remove(&mut self, idx: usize) {\n        self.with_dirs_mut(|dirs| dirs.swap_remove(idx));\n        self.with_dirty_mut(|dirty| *dirty = true);\n    }\n\n    pub fn age(&mut self, max_age: Rank) {\n        let mut dirty = false;\n        self.with_dirs_mut(|dirs| {\n            let total_age = dirs.iter().map(|dir| dir.rank).sum::<Rank>();\n            if total_age > max_age {\n                let factor = 0.9 * max_age / total_age;\n                for idx in (0..dirs.len()).rev() {\n                    let dir = &mut dirs[idx];\n                    dir.rank *= factor;\n                    if dir.rank < 1.0 {\n                        dirs.swap_remove(idx);\n                    }\n                }\n                dirty = true;\n            }\n        });\n        self.with_dirty_mut(|dirty_prev| *dirty_prev |= dirty);\n    }\n\n    pub fn dedup(&mut self) {\n        // Sort by path, so that equal paths are next to each other.\n        self.sort_by_path();\n\n        let mut dirty = false;\n        self.with_dirs_mut(|dirs| {\n            for idx in (1..dirs.len()).rev() {\n                // Check if curr_dir and next_dir have equal paths.\n                let curr_dir = &dirs[idx];\n                let next_dir = &dirs[idx - 1];\n                if next_dir.path != curr_dir.path {\n                    continue;\n                }\n\n                // Merge curr_dir's rank and last_accessed into next_dir.\n                let rank = curr_dir.rank;\n                let last_accessed = curr_dir.last_accessed;\n                let next_dir = &mut dirs[idx - 1];\n                next_dir.last_accessed = next_dir.last_accessed.max(last_accessed);\n                next_dir.rank += rank;\n\n                // Delete curr_dir.\n                dirs.swap_remove(idx);\n                dirty = true;\n            }\n        });\n        self.with_dirty_mut(|dirty_prev| *dirty_prev |= dirty);\n    }\n\n    pub fn sort_by_path(&mut self) {\n        self.with_dirs_mut(|dirs| dirs.sort_unstable_by(|dir1, dir2| dir1.path.cmp(&dir2.path)));\n        self.with_dirty_mut(|dirty| *dirty = true);\n    }\n\n    pub fn sort_by_score(&mut self, now: Epoch) {\n        self.with_dirs_mut(|dirs| {\n            dirs.sort_unstable_by(|dir1: &Dir, dir2: &Dir| {\n                dir1.score(now).total_cmp(&dir2.score(now))\n            })\n        });\n        self.with_dirty_mut(|dirty| *dirty = true);\n    }\n\n    pub fn dirty(&self) -> bool {\n        *self.borrow_dirty()\n    }\n\n    pub fn dirs(&self) -> &[Dir<'_>] {\n        self.borrow_dirs()\n    }\n\n    fn serialize(dirs: &[Dir<'_>]) -> Result<Vec<u8>> {\n        (|| -> bincode::Result<_> {\n            // Preallocate buffer with combined size of sections.\n            let buffer_size =\n                bincode::serialized_size(&Self::VERSION)? + bincode::serialized_size(&dirs)?;\n            let mut buffer = Vec::with_capacity(buffer_size as usize);\n\n            // Serialize sections into buffer.\n            bincode::serialize_into(&mut buffer, &Self::VERSION)?;\n            bincode::serialize_into(&mut buffer, &dirs)?;\n\n            Ok(buffer)\n        })()\n        .context(\"could not serialize database\")\n    }\n\n    fn deserialize(bytes: &[u8]) -> Result<Vec<Dir<'_>>> {\n        // Assume a maximum size for the database. This prevents bincode from throwing\n        // strange errors when it encounters invalid data.\n        const MAX_SIZE: u64 = 32 << 20; // 32 MiB\n        let deserializer = &mut bincode::options().with_fixint_encoding().with_limit(MAX_SIZE);\n\n        // Split bytes into sections.\n        let version_size = deserializer.serialized_size(&Self::VERSION).unwrap() as _;\n        if bytes.len() < version_size {\n            bail!(\"could not deserialize database: corrupted data\");\n        }\n        let (bytes_version, bytes_dirs) = bytes.split_at(version_size);\n\n        // Deserialize sections.\n        let version = deserializer.deserialize(bytes_version)?;\n        let dirs = match version {\n            Self::VERSION => {\n                deserializer.deserialize(bytes_dirs).context(\"could not deserialize database\")?\n            }\n            version => {\n                bail!(\"unsupported version (got {version}, supports {})\", Self::VERSION)\n            }\n        };\n\n        Ok(dirs)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn add() {\n        let data_dir = tempfile::tempdir().unwrap();\n        let path = if cfg!(windows) { r\"C:\\foo\\bar\" } else { \"/foo/bar\" };\n        let now = 946684800;\n\n        {\n            let mut db = Database::open_dir(data_dir.path()).unwrap();\n            db.add(path, 1.0, now);\n            db.add(path, 1.0, now);\n            db.save().unwrap();\n        }\n\n        {\n            let db = Database::open_dir(data_dir.path()).unwrap();\n            assert_eq!(db.dirs().len(), 1);\n\n            let dir = &db.dirs()[0];\n            assert_eq!(dir.path, path);\n            assert!((dir.rank - 2.0).abs() < 0.01);\n            assert_eq!(dir.last_accessed, now);\n        }\n    }\n\n    #[test]\n    fn remove() {\n        let data_dir = tempfile::tempdir().unwrap();\n        let path = if cfg!(windows) { r\"C:\\foo\\bar\" } else { \"/foo/bar\" };\n        let now = 946684800;\n\n        {\n            let mut db = Database::open_dir(data_dir.path()).unwrap();\n            db.add(path, 1.0, now);\n            db.save().unwrap();\n        }\n\n        {\n            let mut db = Database::open_dir(data_dir.path()).unwrap();\n            assert!(db.remove(path));\n            db.save().unwrap();\n        }\n\n        {\n            let mut db = Database::open_dir(data_dir.path()).unwrap();\n            assert!(db.dirs().is_empty());\n            assert!(!db.remove(path));\n            db.save().unwrap();\n        }\n    }\n}\n"
  },
  {
    "path": "src/db/stream.rs",
    "content": "use std::iter::Rev;\nuse std::ops::Range;\nuse std::path::Path;\nuse std::{fs, path};\n\nuse glob::Pattern;\n\nuse crate::db::{Database, Dir, Epoch};\nuse crate::util::{self, MONTH};\n\npub struct Stream<'a> {\n    db: &'a mut Database,\n    idxs: Rev<Range<usize>>,\n    options: StreamOptions,\n}\n\nimpl<'a> Stream<'a> {\n    pub fn new(db: &'a mut Database, options: StreamOptions) -> Self {\n        db.sort_by_score(options.now);\n        let idxs = (0..db.dirs().len()).rev();\n        Stream { db, idxs, options }\n    }\n\n    pub fn next(&mut self) -> Option<&Dir<'_>> {\n        while let Some(idx) = self.idxs.next() {\n            let dir = &self.db.dirs()[idx];\n\n            if !self.filter_by_keywords(&dir.path) {\n                continue;\n            }\n\n            if !self.filter_by_base_dir(&dir.path) {\n                continue;\n            }\n\n            if !self.filter_by_exclude(&dir.path) {\n                self.db.swap_remove(idx);\n                continue;\n            }\n\n            // Exists queries are slow, this should always be checked last.\n            if !self.filter_by_exists(&dir.path) {\n                if dir.last_accessed < self.options.ttl {\n                    self.db.swap_remove(idx);\n                }\n                continue;\n            }\n\n            let dir = &self.db.dirs()[idx];\n            return Some(dir);\n        }\n\n        None\n    }\n\n    fn filter_by_base_dir(&self, path: &str) -> bool {\n        match &self.options.base_dir {\n            Some(base_dir) => Path::new(path).starts_with(base_dir),\n            None => true,\n        }\n    }\n\n    fn filter_by_exclude(&self, path: &str) -> bool {\n        !self.options.exclude.iter().any(|pattern| pattern.matches(path))\n    }\n\n    fn filter_by_exists(&self, path: &str) -> bool {\n        if !self.options.exists {\n            return true;\n        }\n\n        // The logic here is reversed - if we resolve symlinks when adding entries to\n        // the database, we should not return symlinks when querying from\n        // the database.\n        let resolver =\n            if self.options.resolve_symlinks { fs::symlink_metadata } else { fs::metadata };\n        resolver(path).map(|metadata| metadata.is_dir()).unwrap_or_default()\n    }\n\n    fn filter_by_keywords(&self, path: &str) -> bool {\n        let (keywords_last, keywords) = match self.options.keywords.split_last() {\n            Some(split) => split,\n            None => return true,\n        };\n\n        let path = util::to_lowercase(path);\n        let mut path = path.as_str();\n        match path.rfind(keywords_last) {\n            Some(idx) => {\n                if path[idx + keywords_last.len()..].contains(path::is_separator) {\n                    return false;\n                }\n                path = &path[..idx];\n            }\n            None => return false,\n        }\n\n        for keyword in keywords.iter().rev() {\n            match path.rfind(keyword) {\n                Some(idx) => path = &path[..idx],\n                None => return false,\n            }\n        }\n\n        true\n    }\n}\n\npub struct StreamOptions {\n    /// The current time.\n    now: Epoch,\n\n    /// Only directories matching these keywords will be returned.\n    keywords: Vec<String>,\n\n    /// Directories that match any of these globs will be lazily removed.\n    exclude: Vec<Pattern>,\n\n    /// Directories will only be returned if they exist on the filesystem.\n    exists: bool,\n\n    /// Whether to resolve symlinks when checking if a directory exists.\n    resolve_symlinks: bool,\n\n    /// Directories that do not exist and haven't been accessed since TTL will\n    /// be lazily removed.\n    ttl: Epoch,\n\n    /// Only return directories within this parent directory\n    /// Does not check if the path exists\n    base_dir: Option<String>,\n}\n\nimpl StreamOptions {\n    pub fn new(now: Epoch) -> Self {\n        StreamOptions {\n            now,\n            keywords: Vec::new(),\n            exclude: Vec::new(),\n            exists: false,\n            resolve_symlinks: false,\n            ttl: now.saturating_sub(3 * MONTH),\n            base_dir: None,\n        }\n    }\n\n    pub fn with_keywords<I>(mut self, keywords: I) -> Self\n    where\n        I: IntoIterator,\n        I::Item: AsRef<str>,\n    {\n        self.keywords = keywords.into_iter().map(util::to_lowercase).collect();\n        self\n    }\n\n    pub fn with_exclude(mut self, exclude: Vec<Pattern>) -> Self {\n        self.exclude = exclude;\n        self\n    }\n\n    pub fn with_exists(mut self, exists: bool) -> Self {\n        self.exists = exists;\n        self\n    }\n\n    pub fn with_resolve_symlinks(mut self, resolve_symlinks: bool) -> Self {\n        self.resolve_symlinks = resolve_symlinks;\n        self\n    }\n\n    pub fn with_base_dir(mut self, base_dir: Option<String>) -> Self {\n        self.base_dir = base_dir;\n        self\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::path::PathBuf;\n\n    use rstest::rstest;\n\n    use super::*;\n\n    #[rstest]\n    // Case normalization\n    #[case(&[\"fOo\", \"bAr\"], \"/foo/bar\", true)]\n    // Last component\n    #[case(&[\"ba\"], \"/foo/bar\", true)]\n    #[case(&[\"fo\"], \"/foo/bar\", false)]\n    // Slash as suffix\n    #[case(&[\"foo/\"], \"/foo\", false)]\n    #[case(&[\"foo/\"], \"/foo/bar\", true)]\n    #[case(&[\"foo/\"], \"/foo/bar/baz\", false)]\n    #[case(&[\"foo\", \"/\"], \"/foo\", false)]\n    #[case(&[\"foo\", \"/\"], \"/foo/bar\", true)]\n    #[case(&[\"foo\", \"/\"], \"/foo/bar/baz\", true)]\n    // Split components\n    #[case(&[\"/\", \"fo\", \"/\", \"ar\"], \"/foo/bar\", true)]\n    #[case(&[\"oo/ba\"], \"/foo/bar\", true)]\n    // Overlap\n    #[case(&[\"foo\", \"o\", \"bar\"], \"/foo/bar\", false)]\n    #[case(&[\"/foo/\", \"/bar\"], \"/foo/bar\", false)]\n    #[case(&[\"/foo/\", \"/bar\"], \"/foo/baz/bar\", true)]\n    fn query(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) {\n        let db = &mut Database::new(PathBuf::new(), Vec::new(), |_| Vec::new(), false);\n        let options = StreamOptions::new(0).with_keywords(keywords.iter());\n        let stream = Stream::new(db, options);\n        assert_eq!(is_match, stream.filter_by_keywords(path));\n    }\n}\n"
  },
  {
    "path": "src/error.rs",
    "content": "use std::fmt::{self, Display, Formatter};\nuse std::io;\n\nuse anyhow::{Context, Result, bail};\n\n/// Custom error type for early exit.\n#[derive(Debug)]\npub struct SilentExit {\n    pub code: u8,\n}\n\nimpl Display for SilentExit {\n    fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {\n        Ok(())\n    }\n}\n\npub trait BrokenPipeHandler {\n    fn pipe_exit(self, device: &str) -> Result<()>;\n}\n\nimpl BrokenPipeHandler for io::Result<()> {\n    fn pipe_exit(self, device: &str) -> Result<()> {\n        match self {\n            Err(e) if e.kind() == io::ErrorKind::BrokenPipe => bail!(SilentExit { code: 0 }),\n            result => result.with_context(|| format!(\"could not write to {device}\")),\n        }\n    }\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "#![allow(clippy::single_component_path_imports)]\n\nmod cmd;\nmod config;\nmod db;\nmod error;\nmod shell;\nmod util;\n\nuse std::env;\nuse std::io::{self, Write};\nuse std::process::ExitCode;\n\nuse clap::Parser;\n\nuse crate::cmd::{Cmd, Run};\nuse crate::error::SilentExit;\n\npub fn main() -> ExitCode {\n    // Forcibly disable backtraces.\n    unsafe { env::remove_var(\"RUST_LIB_BACKTRACE\") };\n    unsafe { env::remove_var(\"RUST_BACKTRACE\") };\n\n    match Cmd::parse().run() {\n        Ok(()) => ExitCode::SUCCESS,\n        Err(e) => match e.downcast::<SilentExit>() {\n            Ok(SilentExit { code }) => code.into(),\n            Err(e) => {\n                _ = writeln!(io::stderr(), \"zoxide: {e:?}\");\n                ExitCode::FAILURE\n            }\n        },\n    }\n}\n"
  },
  {
    "path": "src/shell.rs",
    "content": "use crate::cmd::InitHook;\n\n#[derive(Debug, Eq, PartialEq)]\npub struct Opts<'a> {\n    pub cmd: Option<&'a str>,\n    pub hook: InitHook,\n    pub echo: bool,\n    pub resolve_symlinks: bool,\n}\n\nmacro_rules! make_template {\n    ($name:ident, $path:expr) => {\n        #[derive(::std::fmt::Debug, ::askama::Template)]\n        #[template(path = $path)]\n        pub struct $name<'a>(pub &'a self::Opts<'a>);\n\n        impl<'a> ::std::ops::Deref for $name<'a> {\n            type Target = self::Opts<'a>;\n            fn deref(&self) -> &Self::Target {\n                self.0\n            }\n        }\n    };\n}\n\nmake_template!(Bash, \"bash.txt\");\nmake_template!(Elvish, \"elvish.txt\");\nmake_template!(Fish, \"fish.txt\");\nmake_template!(Nushell, \"nushell.txt\");\nmake_template!(Posix, \"posix.txt\");\nmake_template!(Powershell, \"powershell.txt\");\nmake_template!(Tcsh, \"tcsh.txt\");\nmake_template!(Xonsh, \"xonsh.txt\");\nmake_template!(Zsh, \"zsh.txt\");\n\n#[cfg(feature = \"nix-dev\")]\n#[cfg(test)]\nmod tests {\n    use askama::Template;\n    use assert_cmd::Command;\n    use rstest::rstest;\n    use rstest_reuse::{apply, template};\n\n    use super::*;\n\n    #[template]\n    #[rstest]\n    fn opts(\n        #[values(None, Some(\"z\"))] cmd: Option<&str>,\n        #[values(InitHook::None, InitHook::Prompt, InitHook::Pwd)] hook: InitHook,\n        #[values(false, true)] echo: bool,\n        #[values(false, true)] resolve_symlinks: bool,\n    ) {\n    }\n\n    #[apply(opts)]\n    fn bash_bash(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Bash(&opts).render().unwrap();\n        Command::new(\"bash\")\n            .args([\"--noprofile\", \"--norc\", \"-e\", \"-u\", \"-o\", \"pipefail\", \"-c\", &source])\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn bash_shellcheck(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Bash(&opts).render().unwrap();\n\n        Command::new(\"shellcheck\")\n            .args([\"--enable=all\", \"-\"])\n            .write_stdin(source)\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn bash_shfmt(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let mut source = Bash(&opts).render().unwrap();\n        source.push('\\n');\n\n        Command::new(\"shfmt\")\n            .args([\"--diff\", \"--indent=4\", \"--language-dialect=bash\", \"--simplify\", \"-\"])\n            .write_stdin(source)\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn elvish_elvish(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let mut source = String::new();\n\n        // Filter out lines using edit:*, since those functions are only available in\n        // interactive editor mode.\n        for line in Elvish(&opts).render().unwrap().lines().filter(|line| !line.contains(\"edit:\")) {\n            source.push_str(line);\n            source.push('\\n');\n        }\n\n        Command::new(\"elvish\")\n            .args([\"-c\", &source, \"-norc\"])\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn fish_no_builtin_abbr(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Fish(&opts).render().unwrap();\n        assert!(\n            !source.contains(\"builtin abbr\"),\n            \"`builtin abbr` does not work on older versions of Fish\"\n        );\n    }\n\n    #[apply(opts)]\n    fn fish_fish(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Fish(&opts).render().unwrap();\n\n        let tempdir = tempfile::tempdir().unwrap();\n        let tempdir = tempdir.path().to_str().unwrap();\n\n        Command::new(\"fish\")\n            .env(\"HOME\", tempdir)\n            .args([\"--command\", &source, \"--no-config\", \"--private\"])\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn fish_fishindent(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let mut source = Fish(&opts).render().unwrap();\n        source.push('\\n');\n\n        let tempdir = tempfile::tempdir().unwrap();\n        let tempdir = tempdir.path().to_str().unwrap();\n\n        Command::new(\"fish_indent\")\n            .env(\"HOME\", tempdir)\n            .write_stdin(source.to_string())\n            .assert()\n            .success()\n            .stdout(source)\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn nushell_nushell(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Nushell(&opts).render().unwrap();\n\n        let tempdir = tempfile::tempdir().unwrap();\n        let tempdir = tempdir.path();\n\n        let assert = Command::new(\"nu\")\n            .env(\"HOME\", tempdir)\n            .args([\"--commands\", &source])\n            .assert()\n            .success()\n            .stderr(\"\");\n\n        if opts.hook != InitHook::Pwd {\n            assert.stdout(\"\");\n        }\n    }\n\n    #[apply(opts)]\n    fn posix_bash(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Posix(&opts).render().unwrap();\n\n        let assert = Command::new(\"bash\")\n            .args([\"--posix\", \"--noprofile\", \"--norc\", \"-e\", \"-u\", \"-o\", \"pipefail\", \"-c\", &source])\n            .assert()\n            .success()\n            .stderr(\"\");\n        if opts.hook != InitHook::Pwd {\n            assert.stdout(\"\");\n        }\n    }\n\n    #[apply(opts)]\n    fn posix_dash(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Posix(&opts).render().unwrap();\n\n        let assert =\n            Command::new(\"dash\").args([\"-e\", \"-u\", \"-c\", &source]).assert().success().stderr(\"\");\n        if opts.hook != InitHook::Pwd {\n            assert.stdout(\"\");\n        }\n    }\n\n    #[apply(opts)]\n    fn posix_shellcheck(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Posix(&opts).render().unwrap();\n\n        Command::new(\"shellcheck\")\n            .args([\"--enable=all\", \"-\"])\n            .write_stdin(source)\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn posix_shfmt(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let mut source = Posix(&opts).render().unwrap();\n        source.push('\\n');\n\n        Command::new(\"shfmt\")\n            .args([\"--diff\", \"--indent=4\", \"--language-dialect=posix\", \"--simplify\", \"-\"])\n            .write_stdin(source)\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn powershell_pwsh(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let mut source = \"Set-StrictMode -Version latest\\n\".to_string();\n        Powershell(&opts).render_into(&mut source).unwrap();\n\n        Command::new(\"pwsh\")\n            .args([\"-NoLogo\", \"-NonInteractive\", \"-NoProfile\", \"-Command\", &source])\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn tcsh_tcsh(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Tcsh(&opts).render().unwrap();\n\n        Command::new(\"tcsh\")\n            .args([\"-e\", \"-f\", \"-s\"])\n            .write_stdin(source)\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn xonsh_black(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let mut source = Xonsh(&opts).render().unwrap();\n        source.push('\\n');\n\n        Command::new(\"black\")\n            .args([\"--check\", \"--diff\", \"-\"])\n            .write_stdin(source)\n            .assert()\n            .success()\n            .stdout(\"\");\n    }\n\n    #[apply(opts)]\n    fn xonsh_mypy(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Xonsh(&opts).render().unwrap();\n\n        Command::new(\"mypy\").args([\"--command\", &source, \"--strict\"]).assert().success().stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn xonsh_pylint(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let mut source = Xonsh(&opts).render().unwrap();\n        source.push('\\n');\n\n        Command::new(\"pylint\")\n            .args([\"--from-stdin\", \"--persistent=n\", \"zoxide\"])\n            .write_stdin(source)\n            .assert()\n            .success()\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn xonsh_xonsh(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Xonsh(&opts).render().unwrap();\n\n        let tempdir = tempfile::tempdir().unwrap();\n        let tempdir = tempdir.path().to_str().unwrap();\n\n        Command::new(\"xonsh\")\n            .args([\"-c\", &source, \"--no-rc\"])\n            .env(\"HOME\", tempdir)\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn zsh_shellcheck(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Zsh(&opts).render().unwrap();\n\n        // ShellCheck doesn't support zsh yet: https://github.com/koalaman/shellcheck/issues/809\n        Command::new(\"shellcheck\")\n            .args([\"--enable=all\", \"-\"])\n            .write_stdin(source)\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n\n    #[apply(opts)]\n    fn zsh_zsh(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {\n        let opts = Opts { cmd, hook, echo, resolve_symlinks };\n        let source = Zsh(&opts).render().unwrap();\n\n        Command::new(\"zsh\")\n            .args([\"-e\", \"-u\", \"-o\", \"pipefail\", \"--no-globalrcs\", \"--no-rcs\", \"-c\", &source])\n            .assert()\n            .success()\n            .stdout(\"\")\n            .stderr(\"\");\n    }\n}\n"
  },
  {
    "path": "src/util.rs",
    "content": "use std::ffi::OsStr;\nuse std::fs::{self, File, OpenOptions};\nuse std::io::{self, Read, Write};\nuse std::path::{Component, Path, PathBuf};\nuse std::process::{Child, Command, Stdio};\nuse std::time::SystemTime;\nuse std::{env, mem};\n\n#[cfg(windows)]\nuse anyhow::anyhow;\nuse anyhow::{Context, Result, bail};\n\nuse crate::db::{Dir, Epoch};\nuse crate::error::SilentExit;\n\npub const SECOND: Epoch = 1;\npub const MINUTE: Epoch = 60 * SECOND;\npub const HOUR: Epoch = 60 * MINUTE;\npub const DAY: Epoch = 24 * HOUR;\npub const WEEK: Epoch = 7 * DAY;\npub const MONTH: Epoch = 30 * DAY;\n\npub struct Fzf(Command);\n\nimpl Fzf {\n    const ERR_FZF_NOT_FOUND: &'static str = \"could not find fzf, is it installed?\";\n\n    pub fn new() -> Result<Self> {\n        // On Windows, CreateProcess implicitly searches the current working\n        // directory for the executable, which is a potential security issue.\n        // Instead, we resolve the path to the executable and then pass it to\n        // CreateProcess.\n        #[cfg(windows)]\n        let program = which::which(\"fzf.exe\").map_err(|_| anyhow!(Self::ERR_FZF_NOT_FOUND))?;\n        #[cfg(not(windows))]\n        let program = \"fzf\";\n\n        // TODO: check version of fzf here.\n\n        let mut cmd = Command::new(program);\n        cmd.args([\n            // Search mode\n            \"--delimiter=\\t\",\n            \"--nth=2\",\n            // Scripting\n            \"--read0\",\n        ])\n        .stdin(Stdio::piped())\n        .stdout(Stdio::piped());\n\n        Ok(Fzf(cmd))\n    }\n\n    pub fn enable_preview(&mut self) -> &mut Self {\n        // Previews are only supported on UNIX.\n        if !cfg!(unix) {\n            return self;\n        }\n\n        self.args([\n            // Non-POSIX args are only available on certain operating systems.\n            if cfg!(target_os = \"linux\") {\n                r\"--preview=\\command -p ls -Cp --color=always --group-directories-first {2..}\"\n            } else {\n                r\"--preview=\\command -p ls -Cp {2..}\"\n            },\n            // Rounded edges don't display correctly on some terminals.\n            \"--preview-window=down,30%,sharp\",\n        ])\n        .envs([\n            // Enables colorized `ls` output on macOS / FreeBSD.\n            (\"CLICOLOR\", \"1\"),\n            // Forces colorized `ls` output when the output is not a\n            // TTY (like in fzf's preview window) on macOS /\n            // FreeBSD.\n            (\"CLICOLOR_FORCE\", \"1\"),\n            // Ensures that the preview command is run in a\n            // POSIX-compliant shell, regardless of what shell the\n            // user has selected.\n            (\"SHELL\", \"sh\"),\n        ])\n    }\n\n    pub fn args<I, S>(&mut self, args: I) -> &mut Self\n    where\n        I: IntoIterator<Item = S>,\n        S: AsRef<OsStr>,\n    {\n        self.0.args(args);\n        self\n    }\n\n    pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self\n    where\n        K: AsRef<OsStr>,\n        V: AsRef<OsStr>,\n    {\n        self.0.env(key, val);\n        self\n    }\n\n    pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self\n    where\n        I: IntoIterator<Item = (K, V)>,\n        K: AsRef<OsStr>,\n        V: AsRef<OsStr>,\n    {\n        self.0.envs(vars);\n        self\n    }\n\n    pub fn spawn(&mut self) -> Result<FzfChild> {\n        match self.0.spawn() {\n            Ok(child) => Ok(FzfChild(child)),\n            Err(e) if e.kind() == io::ErrorKind::NotFound => bail!(Self::ERR_FZF_NOT_FOUND),\n            Err(e) => Err(e).context(\"could not launch fzf\"),\n        }\n    }\n}\n\npub struct FzfChild(Child);\n\nimpl FzfChild {\n    pub fn write(&mut self, dir: &Dir, now: Epoch) -> Result<Option<String>> {\n        let handle = self.0.stdin.as_mut().unwrap();\n        match write!(handle, \"{}\\0\", dir.display().with_score(now).with_separator('\\t')) {\n            Ok(()) => Ok(None),\n            Err(e) if e.kind() == io::ErrorKind::BrokenPipe => self.wait().map(Some),\n            Err(e) => Err(e).context(\"could not write to fzf\"),\n        }\n    }\n\n    pub fn wait(&mut self) -> Result<String> {\n        // Drop stdin to prevent deadlock.\n        mem::drop(self.0.stdin.take());\n\n        let mut stdout = self.0.stdout.take().unwrap();\n        let mut output = String::new();\n        stdout.read_to_string(&mut output).context(\"failed to read from fzf\")?;\n\n        let status = self.0.wait().context(\"wait failed on fzf\")?;\n        match status.code() {\n            Some(0) => Ok(output),\n            Some(1) => bail!(\"no match found\"),\n            Some(2) => bail!(\"fzf returned an error\"),\n            Some(130) => bail!(SilentExit { code: 130 }),\n            Some(128..=254) | None => bail!(\"fzf was terminated\"),\n            _ => bail!(\"fzf returned an unknown error\"),\n        }\n    }\n}\n\n/// Similar to [`fs::write`], but atomic (best effort on Windows).\npub fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<()> {\n    let path = path.as_ref();\n    let contents = contents.as_ref();\n    let dir = path.parent().unwrap();\n\n    // Create a tmpfile.\n    let (mut tmp_file, tmp_path) = tmpfile(dir)?;\n    let result = (|| {\n        // Write to the tmpfile.\n        _ = tmp_file.set_len(contents.len() as u64);\n        tmp_file\n            .write_all(contents)\n            .with_context(|| format!(\"could not write to file: {}\", tmp_path.display()))?;\n\n        // Set the owner of the tmpfile (UNIX only).\n        #[cfg(unix)]\n        if let Ok(metadata) = path.metadata() {\n            use std::os::unix::fs::MetadataExt;\n\n            use nix::unistd::{self, Gid, Uid};\n\n            let uid = Uid::from_raw(metadata.uid());\n            let gid = Gid::from_raw(metadata.gid());\n            _ = unistd::fchown(&tmp_file, Some(uid), Some(gid));\n        }\n\n        // Close and rename the tmpfile.\n        // In some cases, errors from the last write() are reported only on close().\n        // Rust ignores errors from close(), since it occurs inside `Drop`. To\n        // catch these errors, we manually call `File::sync_all()` first.\n        tmp_file\n            .sync_all()\n            .with_context(|| format!(\"could not sync writes to file: {}\", tmp_path.display()))?;\n        mem::drop(tmp_file);\n        rename(&tmp_path, path)\n    })();\n    // In case of an error, delete the tmpfile.\n    if result.is_err() {\n        _ = fs::remove_file(&tmp_path);\n    }\n    result\n}\n\n/// Atomically create a tmpfile in the given directory.\nfn tmpfile(dir: impl AsRef<Path>) -> Result<(File, PathBuf)> {\n    const MAX_ATTEMPTS: usize = 5;\n    const TMP_NAME_LEN: usize = 16;\n    let dir = dir.as_ref();\n\n    let mut attempts = 0;\n    loop {\n        attempts += 1;\n\n        // Generate a random name for the tmpfile.\n        let mut name = String::with_capacity(TMP_NAME_LEN);\n        name.push_str(\"tmp_\");\n        while name.len() < TMP_NAME_LEN {\n            name.push(fastrand::alphanumeric());\n        }\n        let path = dir.join(name);\n\n        // Atomically create the tmpfile.\n        match OpenOptions::new().write(true).create_new(true).open(&path) {\n            Ok(file) => break Ok((file, path)),\n            Err(e) if e.kind() == io::ErrorKind::AlreadyExists && attempts < MAX_ATTEMPTS => {}\n            Err(e) => {\n                break Err(e).with_context(|| format!(\"could not create file: {}\", path.display()));\n            }\n        }\n    }\n}\n\n/// Similar to [`fs::rename`], but with retries on Windows.\nfn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {\n    let from = from.as_ref();\n    let to = to.as_ref();\n\n    const MAX_ATTEMPTS: usize = if cfg!(windows) { 5 } else { 1 };\n    let mut attempts = 0;\n\n    loop {\n        match fs::rename(from, to) {\n            Err(e) if e.kind() == io::ErrorKind::PermissionDenied && attempts < MAX_ATTEMPTS => {\n                attempts += 1\n            }\n            result => {\n                break result.with_context(|| {\n                    format!(\"could not rename file: {} -> {}\", from.display(), to.display())\n                });\n            }\n        }\n    }\n}\n\npub fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {\n    dunce::canonicalize(&path)\n        .with_context(|| format!(\"could not resolve path: {}\", path.as_ref().display()))\n}\n\npub fn current_dir() -> Result<PathBuf> {\n    env::current_dir().context(\"could not get current directory\")\n}\n\npub fn current_time() -> Result<Epoch> {\n    let current_time = SystemTime::now()\n        .duration_since(SystemTime::UNIX_EPOCH)\n        .context(\"system clock set to invalid time\")?\n        .as_secs();\n\n    Ok(current_time)\n}\n\npub fn path_to_str(path: &impl AsRef<Path>) -> Result<&str> {\n    let path = path.as_ref();\n    path.to_str().with_context(|| format!(\"invalid unicode in path: {}\", path.display()))\n}\n\n/// Returns the absolute version of a path. Like\n/// [`std::path::Path::canonicalize`], but doesn't resolve symlinks.\npub fn resolve_path(path: impl AsRef<Path>) -> Result<PathBuf> {\n    let path = path.as_ref();\n    let base_path;\n\n    let mut components = path.components().peekable();\n    let mut stack = Vec::new();\n\n    // initialize root\n    if cfg!(windows) {\n        use std::path::Prefix;\n\n        fn get_drive_letter(path: impl AsRef<Path>) -> Option<u8> {\n            let path = path.as_ref();\n            let mut components = path.components();\n\n            match components.next() {\n                Some(Component::Prefix(prefix)) => match prefix.kind() {\n                    Prefix::Disk(drive_letter) | Prefix::VerbatimDisk(drive_letter) => {\n                        Some(drive_letter)\n                    }\n                    _ => None,\n                },\n                _ => None,\n            }\n        }\n\n        fn get_drive_path(drive_letter: u8) -> PathBuf {\n            format!(r\"{}:\\\", drive_letter as char).into()\n        }\n\n        fn get_drive_relative(drive_letter: u8) -> Result<PathBuf> {\n            let path = current_dir()?;\n            if Some(drive_letter) == get_drive_letter(&path) {\n                return Ok(path);\n            }\n\n            if let Some(path) = env::var_os(format!(\"={}:\", drive_letter as char)) {\n                return Ok(path.into());\n            }\n\n            let path = get_drive_path(drive_letter);\n            Ok(path)\n        }\n\n        match components.peek() {\n            Some(Component::Prefix(prefix)) => match prefix.kind() {\n                Prefix::Disk(drive_letter) => {\n                    let disk = components.next().unwrap();\n                    if components.peek() == Some(&Component::RootDir) {\n                        let root = components.next().unwrap();\n                        stack.push(disk);\n                        stack.push(root);\n                    } else {\n                        base_path = get_drive_relative(drive_letter)?;\n                        stack.extend(base_path.components());\n                    }\n                }\n                Prefix::VerbatimDisk(drive_letter) => {\n                    components.next();\n                    if components.peek() == Some(&Component::RootDir) {\n                        components.next();\n                    }\n\n                    base_path = get_drive_path(drive_letter);\n                    stack.extend(base_path.components());\n                }\n                _ => bail!(\"invalid path: {}\", path.display()),\n            },\n            Some(Component::RootDir) => {\n                components.next();\n\n                let current_dir = env::current_dir()?;\n                let drive_letter = get_drive_letter(&current_dir).with_context(|| {\n                    format!(\"could not get drive letter: {}\", current_dir.display())\n                })?;\n                base_path = get_drive_path(drive_letter);\n                stack.extend(base_path.components());\n            }\n            _ => {\n                base_path = current_dir()?;\n                stack.extend(base_path.components());\n            }\n        }\n    } else if components.peek() == Some(&Component::RootDir) {\n        let root = components.next().unwrap();\n        stack.push(root);\n    } else {\n        base_path = current_dir()?;\n        stack.extend(base_path.components());\n    }\n\n    for component in components {\n        match component {\n            Component::Normal(_) => stack.push(component),\n            Component::CurDir => {}\n            Component::ParentDir => {\n                if stack.last() != Some(&Component::RootDir) {\n                    stack.pop();\n                }\n            }\n            Component::Prefix(_) | Component::RootDir => unreachable!(),\n        }\n    }\n\n    Ok(stack.iter().collect())\n}\n\n/// Convert a string to lowercase, with a fast path for ASCII strings.\npub fn to_lowercase(s: impl AsRef<str>) -> String {\n    let s = s.as_ref();\n    if s.is_ascii() { s.to_ascii_lowercase() } else { s.to_lowercase() }\n}\n"
  },
  {
    "path": "templates/bash.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\n# shellcheck shell=bash\n\n{{ section }}\n# Utility functions for zoxide.\n#\n\n# pwd based on the value of _ZO_RESOLVE_SYMLINKS.\nfunction __zoxide_pwd() {\n{%- let pwd -%}\n{%- if resolve_symlinks -%}\n{%- let pwd = \"\\\\builtin pwd -P\" -%}\n{%- else -%}\n{%- let pwd = \"\\\\builtin pwd -L\" -%}\n{%- endif -%}\n{%- if cfg!(windows) %}\n    \\command cygpath -w \"{{ pwd }}\"\n{%- else %}\n    {{ pwd }}\n{%- endif %}\n}\n\n# cd + custom logic based on the value of _ZO_ECHO.\nfunction __zoxide_cd() {\n    # shellcheck disable=SC2164\n    \\builtin cd -- \"$@\" {%- if echo %} && __zoxide_pwd {%- endif %}\n}\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n{%- if hook != InitHook::None %}\n\n# Hook to add new entries to the database.\n{%- if hook == InitHook::Prompt %}\nfunction __zoxide_hook() {\n    \\builtin local -r retval=\"$?\"\n    # shellcheck disable=SC2312\n    \\command zoxide add -- \"$(__zoxide_pwd)\"\n    return \"${retval}\"\n}\n\n{%- else if hook == InitHook::Pwd %}\n__zoxide_oldpwd=\"$(__zoxide_pwd)\"\n\nfunction __zoxide_hook() {\n    \\builtin local -r retval=\"$?\"\n    \\builtin local pwd_tmp\n    pwd_tmp=\"$(__zoxide_pwd)\"\n    if [[ ${__zoxide_oldpwd} != \"${pwd_tmp}\" ]]; then\n        __zoxide_oldpwd=\"${pwd_tmp}\"\n        \\command zoxide add -- \"${__zoxide_oldpwd}\"\n    fi\n    return \"${retval}\"\n}\n{%- endif %}\n\n# Initialize hook.\nif [[ ${PROMPT_COMMAND:=} != *'__zoxide_hook'* ]]; then\n    if [[ \"$(declare -p PROMPT_COMMAND 2>&1)\" == \"declare -a\"* ]]; then\n        PROMPT_COMMAND=(\"${PROMPT_COMMAND[@]}\" __zoxide_hook)\n    else\n        # shellcheck disable=SC2128,SC2178\n        PROMPT_COMMAND=\"${PROMPT_COMMAND%\"${PROMPT_COMMAND##*[![:space:];]}\"}\"\n        # shellcheck disable=SC2128,SC2178\n        PROMPT_COMMAND=\"${PROMPT_COMMAND:+${PROMPT_COMMAND};}__zoxide_hook\"\n    fi\nfi\n\n{%- endif %}\n\n# Report common issues.\nfunction __zoxide_doctor() {\n{%- if hook == InitHook::None %}\n    return 0\n\n{%- else %}\n    [[ ${_ZO_DOCTOR:-1} -eq 0 ]] && return 0\n    # shellcheck disable=SC2199\n    [[ ${PROMPT_COMMAND[@]:-} == *'__zoxide_hook'* ]] && return 0\n    # shellcheck disable=SC2199\n    [[ ${__vsc_original_prompt_command[@]:-} == *'__zoxide_hook'* ]] && return 0\n\n    _ZO_DOCTOR=0\n    \\builtin printf '%s\\n' \\\n        'zoxide: detected a possible configuration issue.' \\\n        'Please ensure that zoxide is initialized right at the end of your shell configuration file (usually ~/.bashrc).' \\\n        '' \\\n        'If the issue persists, consider filing an issue at:' \\\n        'https://github.com/ajeetdsouza/zoxide/issues' \\\n        '' \\\n        'Disable this message by setting _ZO_DOCTOR=0.' \\\n        '' >&2\n{%- endif %}\n}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n__zoxide_z_prefix='z#'\n\n# Jump to a directory using only keywords.\nfunction __zoxide_z() {\n    __zoxide_doctor\n\n    # shellcheck disable=SC2199\n    if [[ $# -eq 0 ]]; then\n        __zoxide_cd ~\n    elif [[ $# -eq 1 && $1 == '-' ]]; then\n        __zoxide_cd \"${OLDPWD}\"\n    elif [[ $# -eq 1 && -d $1 ]]; then\n        __zoxide_cd \"$1\"\n    elif [[ $# -eq 2 && $1 == '--' ]]; then\n        __zoxide_cd \"$2\"\n    elif [[ ${@: -1} == \"${__zoxide_z_prefix}\"?* ]]; then\n        # shellcheck disable=SC2124\n        \\builtin local result=\"${@: -1}\"\n        __zoxide_cd \"{{ \"${result:${#__zoxide_z_prefix}}\" }}\"\n    else\n        \\builtin local result\n        # shellcheck disable=SC2312\n        result=\"$(\\command zoxide query --exclude \"$(__zoxide_pwd)\" -- \"$@\")\" &&\n            __zoxide_cd \"${result}\"\n    fi\n}\n\n# Jump to a directory using interactive search.\nfunction __zoxide_zi() {\n    __zoxide_doctor\n    \\builtin local result\n    result=\"$(\\command zoxide query --interactive -- \"$@\")\" && __zoxide_cd \"${result}\"\n}\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\n\\builtin unalias {{cmd}} &>/dev/null || \\builtin true\nfunction {{cmd}}() {\n    __zoxide_z \"$@\"\n}\n\n\\builtin unalias {{cmd}}i &>/dev/null || \\builtin true\nfunction {{cmd}}i() {\n    __zoxide_zi \"$@\"\n}\n\n# Load completions.\n# - Bash 4.4+ is required to use `@Q`.\n# - Completions require line editing. Since Bash supports only two modes of\n#   line editing (`vim` and `emacs`), we check if either them is enabled.\n# - Completions don't work on `dumb` terminals.\nif [[ ${BASH_VERSINFO[0]:-0} -eq 4 && ${BASH_VERSINFO[1]:-0} -ge 4 || ${BASH_VERSINFO[0]:-0} -ge 5 ]] &&\n    [[ :\"${SHELLOPTS}\": =~ :(vi|emacs): && ${TERM} != 'dumb' ]]; then\n\n    function __zoxide_z_complete_helper() {\n        READLINE_LINE=\"{{ cmd }} ${__zoxide_result@Q}\"\n        READLINE_POINT={{ \"${#READLINE_LINE}\" }}\n        bind '\"\\e[0n\": accept-line'\n        \\builtin printf '\\e[5n' >/dev/tty\n    }\n\n    function __zoxide_z_complete() {\n        # Only show completions when the cursor is at the end of the line.\n        [[ {{ \"${#COMP_WORDS[@]}\" }} -eq $((COMP_CWORD + 1)) ]] || return\n\n        # If there is only one argument, use `cd` completions.\n        if [[ {{ \"${#COMP_WORDS[@]}\" }} -eq 2 ]]; then\n            \\builtin mapfile -t COMPREPLY < <(\n                \\builtin compgen -A directory -- \"${COMP_WORDS[-1]}\" || \\builtin true\n            )\n        # If there is a space after the last word, use interactive selection.\n        elif [[ -z ${COMP_WORDS[-1]} ]]; then\n            # shellcheck disable=SC2312\n            __zoxide_result=\"$(\\command zoxide query --exclude \"$(__zoxide_pwd)\" --interactive -- \"{{ \"${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}\" }}\")\" && {\n                # In case the terminal does not respond to \\e[5n or another\n                # mechanism steals the response, it is still worth completing\n                # the directory in the command line.\n                COMPREPLY=(\"${__zoxide_z_prefix}${__zoxide_result}/\")\n\n                # Note: We here call \"bind\" without prefixing \"\\builtin\" to be\n                # compatible with frameworks like ble.sh, which emulates Bash's\n                # builtin \"bind\".\n                bind -x '\"\\e[0n\": __zoxide_z_complete_helper'\n                \\builtin printf '\\e[5n' >/dev/tty\n            }\n        fi\n    }\n\n    \\builtin complete -F __zoxide_z_complete -o filenames -- {{cmd}}\n    \\builtin complete -r {{cmd}}i &>/dev/null || \\builtin true\nfi\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n{{ section }}\n# To initialize zoxide, add this to your shell configuration file (usually ~/.bashrc):\n#\n# eval \"$(zoxide init bash)\"\n"
  },
  {
    "path": "templates/elvish.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\nuse builtin\nuse path\n\n{{ section }}\n# Utility functions for zoxide.\n#\n\n# cd + custom logic based on the value of _ZO_ECHO.\nfn __zoxide_cd {|path|\n    builtin:cd $path\n{%- if echo %}\n    builtin:echo $pwd\n{%- endif %}\n}\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n\n# Initialize hook to track previous directory.\nvar oldpwd = $builtin:pwd\nset builtin:before-chdir = [$@builtin:before-chdir {|_| set oldpwd = $builtin:pwd }]\n\n# Initialize hook to add directories to zoxide.\n{%- if hook == InitHook::None %}\n{{ not_configured }}\n\n{%- else %}\nif (builtin:not (builtin:eq $E:__zoxide_shlvl $E:SHLVL)) {\n    set E:__zoxide_shlvl = $E:SHLVL\n{%- if hook == InitHook::Prompt %}\n    set edit:before-readline = [$@edit:before-readline {|| zoxide add -- $pwd }]\n{%- else if hook == InitHook::Pwd %}\n    set builtin:after-chdir = [$@builtin:after-chdir {|_| zoxide add -- $pwd }]\n{%- endif %}\n}\n\n{%- endif %}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n# Jump to a directory using only keywords.\nfn __zoxide_z {|@rest|\n    if (builtin:eq [] $rest) {\n        __zoxide_cd ~\n    } elif (builtin:eq [-] $rest) {\n        __zoxide_cd $oldpwd\n    } elif (and ('builtin:==' (builtin:count $rest) 1) (path:is-dir &follow-symlink=$true $rest[0])) {\n        __zoxide_cd $rest[0]\n    } else {\n        var path\n        try {\n            set path = (zoxide query --exclude $pwd -- $@rest)\n        } catch {\n        } else {\n            __zoxide_cd $path\n        }\n    }\n}\nedit:add-var __zoxide_z~ $__zoxide_z~\n\n# Jump to a directory using interactive search.\nfn __zoxide_zi {|@rest|\n    var path\n    try {\n        set path = (zoxide query --interactive -- $@rest)\n    } catch {\n    } else {\n        __zoxide_cd $path\n    }\n}\nedit:add-var __zoxide_zi~ $__zoxide_zi~\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\nedit:add-var {{cmd}}~ $__zoxide_z~\nedit:add-var {{cmd}}i~ $__zoxide_zi~\n\n# Load completions.\n{#-\n  zoxide-based completions are currently not possible, because Elvish only prints\n  a completion if the current token is a prefix of it.\n#}\nfn __zoxide_z_complete {|@rest|\n    if (!= (builtin:count $rest) 2) {\n        builtin:return\n    }\n    edit:complete-filename $rest[1] |\n        builtin:each {|completion|\n            var dir = $completion[stem]\n            if (path:is-dir $dir) {\n                builtin:put $dir\n            }\n        }\n}\nset edit:completion:arg-completer[{{cmd}}] = $__zoxide_z_complete~\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n{{ section }}\n# To initialize zoxide, add this to your configuration (usually\n# ~/.elvish/rc.elv):\n#\n#   eval (zoxide init elvish | slurp)\n#\n# Note: zoxide only supports elvish v0.18.0 and above.\n"
  },
  {
    "path": "templates/fish.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\n{{ section }}\n# Utility functions for zoxide.\n#\n\n# pwd based on the value of _ZO_RESOLVE_SYMLINKS.\nfunction __zoxide_pwd\n{%- let pwd -%}\n{%- if resolve_symlinks -%}\n{%- let pwd = \"builtin pwd -P\" -%}\n{%- else -%}\n{%- let pwd = \"builtin pwd -L\" -%}\n{%- endif -%}\n{%- if cfg!(windows) %}\n    command cygpath -w ({{ pwd }})\n{%- else %}\n    {{ pwd }}\n{%- endif %}\nend\n\n# A copy of fish's internal cd function. This makes it possible to use\n# `alias cd=z` without causing an infinite loop.\nif ! builtin functions --query __zoxide_cd_internal\n    if status list-files functions/cd.fish &>/dev/null\n        status get-file functions/cd.fish | string replace --regex -- '^function cd\\s' 'function __zoxide_cd_internal ' | source\n    else\n        string replace --regex -- '^function cd\\s' 'function __zoxide_cd_internal ' <$__fish_data_dir/functions/cd.fish | source\n    end\nend\n\n# cd + custom logic based on the value of _ZO_ECHO.\nfunction __zoxide_cd\n    if set -q __zoxide_loop\n        builtin echo \"zoxide: infinite loop detected\"\n        builtin echo \"Avoid aliasing `cd` to `z` directly, use `zoxide init --cmd=cd fish` instead\"\n        return 1\n    end\n\n{%- if cfg!(windows) %}\n    __zoxide_loop=1 __zoxide_cd_internal (cygpath -u $argv)\n{%- else %}\n    __zoxide_loop=1 __zoxide_cd_internal $argv\n{%- endif %}\n{%- if echo %}\n    and __zoxide_pwd\n{%- endif %}\nend\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n\n{% if hook == InitHook::None -%}\n{{ not_configured }}\n\n{%- else -%}\n# Initialize hook to add new entries to the database.\n{%- if hook == InitHook::Prompt %}\nfunction __zoxide_hook --on-event fish_prompt\n{%- else if hook == InitHook::Pwd %}\nfunction __zoxide_hook --on-variable PWD\n{%- endif %}\n    test -z \"$fish_private_mode\"\n    and command zoxide add -- (__zoxide_pwd)\nend\n\n{%- endif %}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n# Jump to a directory using only keywords.\nfunction __zoxide_z\n    set -l argc (builtin count $argv)\n    if test $argc -eq 0\n        __zoxide_cd $HOME\n    else if test \"$argv\" = -\n        __zoxide_cd -\n    else if test $argc -eq 1 -a -d $argv[1]\n        __zoxide_cd $argv[1]\n    else if test $argc -eq 2 -a $argv[1] = --\n        __zoxide_cd -- $argv[2]\n    else\n        set -l result (command zoxide query --exclude (__zoxide_pwd) -- $argv)\n        and __zoxide_cd $result\n    end\nend\n\n# Completions.\nfunction __zoxide_z_complete\n    set -l tokens (builtin commandline --current-process --tokenize)\n    set -l curr_tokens (builtin commandline --cut-at-cursor --current-process --tokenize)\n\n    if test (builtin count $tokens) -le 2 -a (builtin count $curr_tokens) -eq 1\n        # If there are < 2 arguments, use `cd` completions.\n        complete --do-complete \"'' \"(builtin commandline --cut-at-cursor --current-token) | string match --regex -- '.*/$'\n    else if test (builtin count $tokens) -eq (builtin count $curr_tokens)\n        # If the last argument is empty, use interactive selection.\n        set -l query $tokens[2..-1]\n        set -l result (command zoxide query --exclude (__zoxide_pwd) --interactive -- $query)\n        and __zoxide_cd $result\n        and builtin commandline --function cancel-commandline repaint\n    end\nend\ncomplete --command __zoxide_z --no-files --arguments '(__zoxide_z_complete)'\n\n# Jump to a directory using interactive search.\nfunction __zoxide_zi\n    set -l result (command zoxide query --interactive -- $argv)\n    and __zoxide_cd $result\nend\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\nabbr --erase {{cmd}} &>/dev/null\ncomplete --erase --command {{cmd}}\nalias {{cmd}}=__zoxide_z\n\nabbr --erase {{cmd}}i &>/dev/null\ncomplete --erase --command {{cmd}}i\nalias {{cmd}}i=__zoxide_zi\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n{{ section }}\n# To initialize zoxide, add this to your configuration (usually\n# ~/.config/fish/config.fish):\n#\n#   zoxide init fish | source\n"
  },
  {
    "path": "templates/nushell.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\n# Code generated by zoxide. DO NOT EDIT.\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n\n{% if hook == InitHook::None -%}\n{{ not_configured }}\n\n{%- else -%}\n# Initialize hook to add new entries to the database.\nexport-env {\n{%- if hook == InitHook::Prompt %}\n  $env.config = (\n    $env.config?\n    | default {}\n    | upsert hooks { default {} }\n    | upsert hooks.pre_prompt { default [] }\n  )\n  let __zoxide_hooked = (\n    $env.config.hooks.pre_prompt | any { try { get __zoxide_hook } catch { false } }\n  )\n  if not $__zoxide_hooked {\n    $env.config.hooks.pre_prompt = ($env.config.hooks.pre_prompt | append {\n      __zoxide_hook: true,\n      code: {|| ^zoxide add -- $env.PWD}\n    })\n  }\n{%- else if hook == InitHook::Pwd %}\n  $env.config = (\n    $env.config?\n    | default {}\n    | upsert hooks { default {} }\n    | upsert hooks.env_change { default {} }\n    | upsert hooks.env_change.PWD { default [] }\n  )\n  let __zoxide_hooked = (\n    $env.config.hooks.env_change.PWD | any { try { get __zoxide_hook } catch { false } }\n  )\n  if not $__zoxide_hooked {\n    $env.config.hooks.env_change.PWD = ($env.config.hooks.env_change.PWD | append {\n      __zoxide_hook: true,\n      code: {|_, dir| ^zoxide add -- $dir}\n    })\n  }\n{%- endif %}\n}\n\n{%- endif %}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n# Jump to a directory using only keywords.\ndef --env --wrapped __zoxide_z [...rest: string] {\n  let path = match $rest {\n    [] => {'~'},\n    [ '-' ] => {'-'},\n    [ $arg ] if ($arg | path expand | path type) == 'dir' => {$arg}\n    _ => {\n      ^zoxide query --exclude $env.PWD -- ...$rest | str trim -r -c \"\\n\"\n    }\n  }\n  cd $path\n{%- if echo %}\n  echo $env.PWD\n{%- endif %}\n}\n\n# Jump to a directory using interactive search.\ndef --env --wrapped __zoxide_zi [...rest:string] {\n  cd $'(^zoxide query --interactive -- ...$rest | str trim -r -c \"\\n\")'\n{%- if echo %}\n  echo $env.PWD\n{%- endif %}\n}\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\nalias {{cmd}} = __zoxide_z\nalias {{cmd}}i = __zoxide_zi\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n{{ section }}\n# Add this to your env file (find it by running `$nu.env-path` in Nushell):\n#\n#   zoxide init nushell | save -f ~/.zoxide.nu\n#\n# Now, add this to the end of your config file (find it by running\n# `$nu.config-path` in Nushell):\n#\n#   source ~/.zoxide.nu\n#\n# Note: zoxide only supports Nushell v0.89.0+.\n"
  },
  {
    "path": "templates/posix.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\n# shellcheck shell=sh\n\n{{ section }}\n# Utility functions for zoxide.\n#\n\n# pwd based on the value of _ZO_RESOLVE_SYMLINKS.\n{%- let pwd -%}\n{%- if resolve_symlinks -%}\n{%- let pwd = \"\\\\command pwd -P\" -%}\n{%- else -%}\n{%- let pwd = \"\\\\command pwd -L\" -%}\n{%- endif -%}\n{%- if cfg!(windows) %}\nif \\command -v cygpath >/dev/null\nthen\n  __zoxide_pwd() {\n    \\command cygpath -w \"$({{ pwd }})\"\n  }\nelse\n  __zoxide_pwd() {\n    {{ pwd }}\n  }\nfi\n{%- else %}\n__zoxide_pwd() {\n    {{ pwd }}\n}\n{%- endif %}\n\n# cd + custom logic based on the value of _ZO_ECHO.\n__zoxide_cd() {\n    # shellcheck disable=SC2164\n    \\command cd \"$@\" {%- if echo %} && __zoxide_pwd {%- endif %}\n}\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n\n{% match hook %}\n{%- when InitHook::None -%}\n{{ not_configured }}\n\n{%- when InitHook::Prompt -%}\n# Hook to add new entries to the database.\n__zoxide_hook() {\n    \\command zoxide add -- \"$(__zoxide_pwd || \\command true)\"\n}\n\n# Initialize hook.\nif [ \"${PS1:=}\" = \"${PS1#*\\$(__zoxide_hook)}\" ]; then\n    PS1=\"${PS1}\\$(__zoxide_hook)\"\nfi\n\n# Report common issues.\n__zoxide_doctor() {\n{%- if hook != InitHook::Prompt %}\n    return 0\n{%- else %}\n    [ \"${_ZO_DOCTOR:-1}\" -eq 0 ] && return 0\n    case \"${PS1:-}\" in\n    *__zoxide_hook*) return 0 ;;\n    *) ;;\n    esac\n\n    _ZO_DOCTOR=0\n    \\command printf '%s\\n' \\\n        'zoxide: detected a possible configuration issue.' \\\n        'Please ensure that zoxide is initialized right at the end of your shell configuration file.' \\\n        '' \\\n        'If the issue persists, consider filing an issue at:' \\\n        'https://github.com/ajeetdsouza/zoxide/issues' \\\n        '' \\\n        'Disable this message by setting _ZO_DOCTOR=0.' \\\n        '' >&2\n{%- endif %}\n}\n\n{%- when InitHook::Pwd -%}\n\\command printf \"%s\\n%s\\n\" \\\n    \"zoxide: PWD hooks are not supported on POSIX shells.\" \\\n    \"        Use 'zoxide init posix --hook prompt' instead.\"\n\n{%- endmatch %}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n# Jump to a directory using only keywords.\n__zoxide_z() {\n    __zoxide_doctor\n\n    if [ \"$#\" -eq 0 ]; then\n        __zoxide_cd ~\n    elif [ \"$#\" -eq 1 ] && [ \"$1\" = '-' ]; then\n        if [ -n \"${OLDPWD}\" ]; then\n            __zoxide_cd \"${OLDPWD}\"\n        else\n            # shellcheck disable=SC2016\n            \\command printf 'zoxide: $OLDPWD is not set'\n            return 1\n        fi\n    elif [ \"$#\" -eq 1 ] && [ -d \"$1\" ]; then\n        __zoxide_cd \"$1\"\n    else\n        __zoxide_result=\"$(\\command zoxide query --exclude \"$(__zoxide_pwd || \\command true)\" -- \"$@\")\" &&\n            __zoxide_cd \"${__zoxide_result}\"\n    fi\n}\n\n# Jump to a directory using interactive search.\n__zoxide_zi() {\n    __zoxide_doctor\n    __zoxide_result=\"$(\\command zoxide query --interactive -- \"$@\")\" && __zoxide_cd \"${__zoxide_result}\"\n}\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\n\\command unalias {{cmd}} >/dev/null 2>&1 || \\true\n{{cmd}}() {\n    __zoxide_z \"$@\"\n}\n\n\\command unalias {{cmd}}i >/dev/null 2>&1 || \\true\n{{cmd}}i() {\n    __zoxide_zi \"$@\"\n}\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n{{ section }}\n# To initialize zoxide, add this to your configuration:\n#\n# eval \"$(zoxide init posix --hook prompt)\"\n"
  },
  {
    "path": "templates/powershell.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\n{{ section }}\n# Utility functions for zoxide.\n#\n\n# Call zoxide binary, returning the output as UTF-8.\nfunction global:__zoxide_bin {\n    $encoding = [Console]::OutputEncoding\n    try {\n        [Console]::OutputEncoding = [System.Text.Utf8Encoding]::new()\n        $result = zoxide @args\n        return $result\n    } finally {\n        [Console]::OutputEncoding = $encoding\n    }\n}\n\n# pwd based on zoxide's format.\nfunction global:__zoxide_pwd {\n    $cwd = Get-Location\n    if ($cwd.Provider.Name -eq \"FileSystem\") {\n        $cwd.ProviderPath\n    }\n}\n\n# cd + custom logic based on the value of _ZO_ECHO.\nfunction global:__zoxide_cd($dir, $literal) {\n    $dir = if ($literal) {\n        if ($null -eq $dir) {\n            Set-Location\n        } else {\n            Set-Location -LiteralPath $dir -Passthru -ErrorAction Stop\n        }\n    } else {\n        if ($dir -eq '-' -and ($PSVersionTable.PSVersion -lt 6.1)) {\n            Write-Error \"cd - is not supported below PowerShell 6.1. Please upgrade your version of PowerShell.\"\n        }\n        elseif ($dir -eq '+' -and ($PSVersionTable.PSVersion -lt 6.2)) {\n            Write-Error \"cd + is not supported below PowerShell 6.2. Please upgrade your version of PowerShell.\"\n        }\n        else {\n            Set-Location -Path $dir -Passthru -ErrorAction Stop\n        }\n    }\n{%- if echo %}\n    Write-Output $dir.Path\n{%- endif %}\n}\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n\n{% if hook == InitHook::None -%}\n{{ not_configured }}\n\n{%- else -%}\n{#-\n  Initialize $__zoxide_hooked if it does not exist. Removing this will cause an\n  unset variable error in StrictMode.\n-#}\n{%- if hook == InitHook::Prompt -%}\n# Hook to add new entries to the database.\nfunction global:__zoxide_hook {\n    $result = __zoxide_pwd\n    if ($null -ne $result) {\n        zoxide add \"--\" $result\n    }\n}\n{%- else if hook == InitHook::Pwd -%}\n# Hook to add new entries to the database.\n$global:__zoxide_oldpwd = __zoxide_pwd\nfunction global:__zoxide_hook {\n    $result = __zoxide_pwd\n    if ($result -ne $global:__zoxide_oldpwd) {\n        if ($null -ne $result) {\n            zoxide add \"--\" $result\n        }\n        $global:__zoxide_oldpwd = $result\n    }\n}\n{%- endif %}\n\n# Initialize hook.\n$global:__zoxide_hooked = (Get-Variable __zoxide_hooked -ErrorAction Ignore -ValueOnly)\nif ($global:__zoxide_hooked -ne 1) {\n    $global:__zoxide_hooked = 1\n    $global:__zoxide_prompt_old = $function:prompt\n\n    function global:prompt {\n        if ($null -ne $__zoxide_prompt_old) {\n            & $__zoxide_prompt_old\n        }\n        $null = __zoxide_hook\n    }\n}\n{%- endif %}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n# Jump to a directory using only keywords.\nfunction global:__zoxide_z {\n    if ($args.Length -eq 0) {\n        __zoxide_cd $null $true\n    }\n    elseif ($args.Length -eq 1 -and ($args[0] -eq '-' -or $args[0] -eq '+')) {\n        __zoxide_cd $args[0] $false\n    }\n    elseif ($args.Length -eq 1 -and (Test-Path -PathType Container -LiteralPath $args[0])) {\n        __zoxide_cd $args[0] $true\n    }\n    elseif ($args.Length -eq 1 -and (Test-Path -PathType Container -Path $args[0] )) {\n        __zoxide_cd $args[0] $false\n    }\n    else {\n        $result = __zoxide_pwd\n        if ($null -ne $result) {\n            $result = __zoxide_bin query --exclude $result \"--\" @args\n        }\n        else {\n            $result = __zoxide_bin query \"--\" @args\n        }\n        if ($LASTEXITCODE -eq 0) {\n            __zoxide_cd $result $true\n        }\n    }\n}\n\n# Jump to a directory using interactive search.\nfunction global:__zoxide_zi {\n    $result = __zoxide_bin query -i \"--\" @args\n    if ($LASTEXITCODE -eq 0) {\n        __zoxide_cd $result $true\n    }\n}\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\nSet-Alias -Name {{cmd}} -Value __zoxide_z -Option AllScope -Scope Global -Force\nSet-Alias -Name {{cmd}}i -Value __zoxide_zi -Option AllScope -Scope Global -Force\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n{{ section }}\n# To initialize zoxide, add this to your configuration (find it by running\n# `echo $profile` in PowerShell):\n#\n# Invoke-Expression (& { (zoxide init powershell | Out-String) })\n"
  },
  {
    "path": "templates/tcsh.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\n{%- let pwd_cmd -%}\n{%- if resolve_symlinks -%}\n{%- let pwd_cmd = \"pwd -P\" -%}\n{%- else -%}\n{%- let pwd_cmd = \"pwd -L\" -%}\n{%- endif -%}\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n{%- if hook != InitHook::None %}\n\n# Hook to add new entries to the database.\n{%- if hook == InitHook::Prompt %}\nalias __zoxide_hook 'zoxide add -- \"`{{ pwd_cmd }}`\"'\n\n{%- else if hook == InitHook::Pwd %}\nset __zoxide_pwd_old = `{{ pwd_cmd }}`\nalias __zoxide_hook 'set __zoxide_pwd_tmp = \"`{{ pwd_cmd }}`\"; test \"$__zoxide_pwd_tmp\" != \"$__zoxide_pwd_old\" && zoxide add -- \"$__zoxide_pwd_tmp\"; set __zoxide_pwd_old = \"$__zoxide_pwd_tmp\"'\n{%- endif %}\n\n# Initialize hook.\nalias precmd ';__zoxide_hook'\n\n{%- endif %}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n# Jump to a directory using only keywords.\nalias __zoxide_z 'set __zoxide_args = (\\!*)\\\nif (\"$#__zoxide_args\" == 0) then\\\n    cd ~\\\nelse\\\n    if (\"$#__zoxide_args\" == 1 && \"$__zoxide_args[1]\" == \"-\") then\\\n        cd -\\\n    else if (\"$#__zoxide_args\" == 1 && -d \"$__zoxide_args[1]\") then\\\n        cd \"$__zoxide_args[1]\"\\\n    else\\\n        set __zoxide_pwd = `{{ pwd_cmd }}`\\\n        set __zoxide_result = \"`zoxide query --exclude '\"'\"'$__zoxide_pwd'\"'\"' -- $__zoxide_args`\" && cd \"$__zoxide_result\"\\\n    endif\\\nendif'\n\n# Jump to a directory using interactive search.\nalias __zoxide_zi 'set __zoxide_args = (\\!*)\\\nset __zoxide_pwd = `{{ pwd_cmd }}`\\\nset __zoxide_result = \"`zoxide query --exclude '\"'\"'$__zoxide_pwd'\"'\"' --interactive -- $__zoxide_args`\" && cd \"$__zoxide_result\"'\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\nalias {{cmd}} __zoxide_z\nalias {{cmd}}i __zoxide_zi\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n{{ section }}\n# To initialize zoxide, add this to your shell configuration file (usually ~/.tcshrc):\n#\n#     zoxide init tcsh > ~/.zoxide.tcsh\n#     source ~/.zoxide.tcsh\n"
  },
  {
    "path": "templates/xonsh.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\n# pylint: disable=missing-module-docstring\n\nimport builtins  # pylint: disable=unused-import\nimport os\nimport os.path\nimport subprocess\nimport sys\nimport typing\n\nimport xonsh.dirstack  # type: ignore # pylint: disable=import-error\nimport xonsh.environ  # type: ignore # pylint: disable=import-error\n\n{{ section }}\n# Utility functions for zoxide.\n#\n\n\ndef __zoxide_bin() -> str:\n    \"\"\"Finds and returns the location of the zoxide binary.\"\"\"\n    zoxide = typing.cast(str, xonsh.environ.locate_binary(\"zoxide\"))\n    if zoxide is None:\n        zoxide = \"zoxide\"\n    return zoxide\n\n\ndef __zoxide_env() -> dict[str, str]:\n    \"\"\"Returns the current environment.\"\"\"\n    return builtins.__xonsh__.env.detype()  # type: ignore  # pylint:disable=no-member\n\n\ndef __zoxide_pwd() -> str:\n    \"\"\"pwd based on the value of _ZO_RESOLVE_SYMLINKS.\"\"\"\n{%- if resolve_symlinks %}\n    pwd = os.getcwd()\n{%- else %}\n    pwd = __zoxide_env().get(\"PWD\")\n    if pwd is None:\n        raise RuntimeError(\"$PWD not found\")\n{%- endif %}\n    return pwd\n\n\ndef __zoxide_cd(path: str | bytes | None = None) -> None:\n    \"\"\"cd + custom logic based on the value of _ZO_ECHO.\"\"\"\n    if path is None:\n        args = []\n    elif isinstance(path, bytes):\n        args = [path.decode(\"utf-8\")]\n    else:\n        args = [path]\n    _, exc, _ = xonsh.dirstack.cd(args)\n    if exc is not None:\n        raise RuntimeError(exc)\n{%- if echo %}\n    print(__zoxide_pwd())\n{%- endif %}\n\n\nclass ZoxideSilentException(Exception):\n    \"\"\"Exit without complaining.\"\"\"\n\n\ndef __zoxide_errhandler(\n    func: typing.Callable[[list[str]], None],\n) -> typing.Callable[[list[str]], int]:\n    \"\"\"Print exception and exit with error code 1.\"\"\"\n\n    def wrapper(args: list[str]) -> int:\n        try:\n            func(args)\n            return 0\n        except ZoxideSilentException:\n            return 1\n        except Exception as exc:  # pylint: disable=broad-except\n            print(f\"zoxide: {exc}\", file=sys.stderr)\n            return 1\n\n    return wrapper\n\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n\n{% if hook == InitHook::None -%}\n{{ not_configured }}\n\n{%- else -%}\n# Initialize hook to add new entries to the database.\nif \"__zoxide_hook\" not in globals():\n{% if hook == InitHook::Prompt %}\n    @builtins.events.on_post_prompt  # type: ignore  # pylint:disable=no-member\n{%- else if hook == InitHook::Pwd %}\n    @builtins.events.on_chdir  # type: ignore  # pylint:disable=no-member\n{%- endif %}\n    def __zoxide_hook(**_kwargs: typing.Any) -> None:\n        \"\"\"Hook to add new entries to the database.\"\"\"\n        pwd = __zoxide_pwd()\n        zoxide = __zoxide_bin()\n        subprocess.run(\n            [zoxide, \"add\", \"--\", pwd],\n            check=False,\n            env=__zoxide_env(),\n        )\n{% endif %}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n\n@__zoxide_errhandler\ndef __zoxide_z(args: list[str]) -> None:\n    \"\"\"Jump to a directory using only keywords.\"\"\"\n    if args == []:\n        __zoxide_cd()\n    elif args == [\"-\"]:\n        __zoxide_cd(\"-\")\n    elif len(args) == 1 and os.path.isdir(args[0]):\n        __zoxide_cd(args[0])\n    else:\n        try:\n            zoxide = __zoxide_bin()\n            cmd = subprocess.run(\n                [zoxide, \"query\", \"--exclude\", __zoxide_pwd(), \"--\"] + args,\n                check=True,\n                env=__zoxide_env(),\n                stdout=subprocess.PIPE,\n            )\n        except subprocess.CalledProcessError as exc:\n            raise ZoxideSilentException() from exc\n\n        result = cmd.stdout[:-1]\n        __zoxide_cd(result)\n\n\n@__zoxide_errhandler\ndef __zoxide_zi(args: list[str]) -> None:\n    \"\"\"Jump to a directory using interactive search.\"\"\"\n    try:\n        zoxide = __zoxide_bin()\n        cmd = subprocess.run(\n            [zoxide, \"query\", \"-i\", \"--\"] + args,\n            check=True,\n            env=__zoxide_env(),\n            stdout=subprocess.PIPE,\n        )\n    except subprocess.CalledProcessError as exc:\n        raise ZoxideSilentException() from exc\n\n    result = cmd.stdout[:-1]\n    __zoxide_cd(result)\n\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\nbuiltins.aliases[\"{{cmd}}\"] = __zoxide_z  # type: ignore  # pylint:disable=no-member\nbuiltins.aliases[\"{{cmd}}i\"] = __zoxide_zi  # type: ignore  # pylint:disable=no-member\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n{{ section }}\n# To initialize zoxide, add this to your configuration (usually ~/.xonshrc):\n#\n# execx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')\n"
  },
  {
    "path": "templates/zsh.txt",
    "content": "{%- let section = \"# =============================================================================\\n#\" -%}\n{%- let not_configured = \"# -- not configured --\" -%}\n\n# shellcheck shell=bash\n\n{{ section }}\n# Utility functions for zoxide.\n#\n\n# pwd based on the value of _ZO_RESOLVE_SYMLINKS.\nfunction __zoxide_pwd() {\n{%- let pwd -%}\n{%- if resolve_symlinks -%}\n{%- let pwd = \"\\\\builtin pwd -P\" -%}\n{%- else -%}\n{%- let pwd = \"\\\\builtin pwd -L\" -%}\n{%- endif -%}\n{%- if cfg!(windows) %}\n    \\command cygpath -w \"{{ pwd }}\"\n{%- else %}\n    {{ pwd }}\n{%- endif %}\n}\n\n# cd + custom logic based on the value of _ZO_ECHO.\nfunction __zoxide_cd() {\n    # shellcheck disable=SC2164\n    \\builtin cd -- \"$@\" {%- if echo %} && __zoxide_pwd {%- endif %}\n}\n\n{{ section }}\n# Hook configuration for zoxide.\n#\n\n# Hook to add new entries to the database.\nfunction __zoxide_hook() {\n    # shellcheck disable=SC2312\n    \\command zoxide add -- \"$(__zoxide_pwd)\"\n}\n\n# Initialize hook.\n\\builtin typeset -ga precmd_functions\n\\builtin typeset -ga chpwd_functions\n# shellcheck disable=SC2034,SC2296\nprecmd_functions=(\"${(@)precmd_functions:#__zoxide_hook}\")\n# shellcheck disable=SC2034,SC2296\nchpwd_functions=(\"${(@)chpwd_functions:#__zoxide_hook}\")\n\n{%- if hook == InitHook::Prompt %}\nprecmd_functions+=(__zoxide_hook)\n{%- else if hook == InitHook::Pwd %}\nchpwd_functions+=(__zoxide_hook)\n{%- endif %}\n\n# Report common issues.\nfunction __zoxide_doctor() {\n{%- if hook == InitHook::None %}\n    return 0\n\n{%- else %}\n    [[ ${_ZO_DOCTOR:-1} -ne 0 ]] || return 0\n\n{%- if hook == InitHook::Prompt %}\n    [[ ${precmd_functions[(Ie)__zoxide_hook]:-} -eq 0 ]] || return 0\n{%- else if hook == InitHook::Pwd %}\n    [[ ${chpwd_functions[(Ie)__zoxide_hook]:-} -eq 0 ]] || return 0\n{%- endif %}\n\n    _ZO_DOCTOR=0\n    \\builtin printf '%s\\n' \\\n        'zoxide: detected a possible configuration issue.' \\\n        'Please ensure that zoxide is initialized right at the end of your shell configuration file (usually ~/.zshrc).' \\\n        '' \\\n        'If the issue persists, consider filing an issue at:' \\\n        'https://github.com/ajeetdsouza/zoxide/issues' \\\n        '' \\\n        'Disable this message by setting _ZO_DOCTOR=0.' \\\n        '' >&2\n{%- endif %}\n}\n\n{{ section }}\n# When using zoxide with --no-cmd, alias these internal functions as desired.\n#\n\n# Jump to a directory using only keywords.\nfunction __zoxide_z() {\n    __zoxide_doctor\n    if [[ \"$#\" -eq 0 ]]; then\n        __zoxide_cd ~\n    elif [[ \"$#\" -eq 1 ]] && { [[ -d \"$1\" ]] || [[ \"$1\" = '-' ]] || [[ \"$1\" =~ ^[-+][0-9]+$ ]]; }; then\n        __zoxide_cd \"$1\"\n    elif [[ \"$#\" -eq 2 ]] && [[ \"$1\" = \"--\" ]]; then\n        __zoxide_cd \"$2\"\n    else\n        \\builtin local result\n        # shellcheck disable=SC2312\n        result=\"$(\\command zoxide query --exclude \"$(__zoxide_pwd)\" -- \"$@\")\" && __zoxide_cd \"${result}\"\n    fi\n}\n\n# Jump to a directory using interactive search.\nfunction __zoxide_zi() {\n    __zoxide_doctor\n    \\builtin local result\n    result=\"$(\\command zoxide query --interactive -- \"$@\")\" && __zoxide_cd \"${result}\"\n}\n\n{{ section }}\n# Commands for zoxide. Disable these using --no-cmd.\n#\n\n{%- match cmd %}\n{%- when Some with (cmd) %}\n\nfunction {{ cmd }}() {\n    __zoxide_z \"$@\"\n}\n\nfunction {{ cmd }}i() {\n    __zoxide_zi \"$@\"\n}\n\n{%- when None %}\n\n{{ not_configured }}\n\n{%- endmatch %}\n\n# Completions.\nif [[ -o zle ]]; then\n    __zoxide_result=''\n\n    function __zoxide_z_complete() {\n        # Only show completions when the cursor is at the end of the line.\n        # shellcheck disable=SC2154\n        [[ \"{{ \"${#words[@]}\" }}\" -eq \"${CURRENT}\" ]] || return 0\n\n        if [[ \"{{ \"${#words[@]}\" }}\" -eq 2 ]]; then\n            # Show completions for local directories.\n            _cd -/\n\n        elif [[ \"${words[-1]}\" == '' ]]; then\n            # Show completions for Space-Tab.\n            # shellcheck disable=SC2086\n            __zoxide_result=\"$(\\command zoxide query --exclude \"$(__zoxide_pwd || \\builtin true)\" --interactive -- ${words[2,-1]})\" || __zoxide_result=''\n\n            # Set a result to ensure completion doesn't re-run\n            compadd -Q \"\"\n\n            # Bind '\\e[0n' to helper function.\n            \\builtin bindkey '\\e[0n' '__zoxide_z_complete_helper'\n            # Sends query device status code, which results in a '\\e[0n' being sent to console input.\n            \\builtin printf '\\e[5n'\n\n            # Report that the completion was successful, so that we don't fall back\n            # to another completion function.\n            return 0\n        fi\n    }\n\n    function __zoxide_z_complete_helper() {\n        if [[ -n \"${__zoxide_result}\" ]]; then\n            # shellcheck disable=SC2034,SC2296\n            BUFFER=\"{{ cmd.unwrap_or(\"cd\") }} ${(q-)__zoxide_result}\"\n            __zoxide_result=''\n            \\builtin zle reset-prompt\n            \\builtin zle accept-line\n        else\n            \\builtin zle reset-prompt\n        fi\n    }\n    \\builtin zle -N __zoxide_z_complete_helper\n{%- if let Some(cmd) = cmd %}\n\n    [[ \"${+functions[compdef]}\" -ne 0 ]] && \\compdef __zoxide_z_complete {{ cmd }}\n{%- endif %}\nfi\n\n{{ section }}\n# To initialize zoxide, add this to your shell configuration file (usually ~/.zshrc):\n#\n# eval \"$(zoxide init zsh)\"\n"
  },
  {
    "path": "tests/completions.rs",
    "content": "//! Test clap generated completions.\n#![cfg(feature = \"nix-dev\")]\n\nuse assert_cmd::Command;\n\n#[test]\nfn completions_bash() {\n    let source = include_str!(\"../contrib/completions/zoxide.bash\");\n    Command::new(\"bash\")\n        .args([\"--noprofile\", \"--norc\", \"-c\", source])\n        .assert()\n        .success()\n        .stdout(\"\")\n        .stderr(\"\");\n}\n\n// Elvish: the completions file uses editor commands to add completions to the\n// shell. However, Elvish does not support running editor commands from a\n// script, so we can't create a test for this. See: https://github.com/elves/elvish/issues/1299\n\n#[test]\nfn completions_fish() {\n    let source = include_str!(\"../contrib/completions/zoxide.fish\");\n    let tempdir = tempfile::tempdir().unwrap();\n    let tempdir = tempdir.path().to_str().unwrap();\n\n    Command::new(\"fish\")\n        .env(\"HOME\", tempdir)\n        .args([\"--command\", source, \"--private\"])\n        .assert()\n        .success()\n        .stdout(\"\")\n        .stderr(\"\");\n}\n\n#[test]\nfn completions_powershell() {\n    let source = include_str!(\"../contrib/completions/_zoxide.ps1\");\n    Command::new(\"pwsh\")\n        .args([\"-NoLogo\", \"-NonInteractive\", \"-NoProfile\", \"-Command\", source])\n        .assert()\n        .success()\n        .stdout(\"\")\n        .stderr(\"\");\n}\n\n#[test]\nfn completions_zsh() {\n    let source = r#\"\n    set -eu\n    completions='./contrib/completions'\n    test -d \"$completions\"\n    fpath=(\"$completions\" $fpath)\n    autoload -Uz compinit\n    compinit -u\n    \"#;\n\n    Command::new(\"zsh\").args([\"-c\", source, \"--no-rcs\"]).assert().success().stdout(\"\").stderr(\"\");\n}\n"
  },
  {
    "path": "zoxide.plugin.zsh",
    "content": "if (( $+commands[zoxide] )); then\n  eval \"$(zoxide init zsh)\"\nelse\n  echo 'zoxide: command not found, please install it from https://github.com/ajeetdsouza/zoxide'\nfi\n"
  }
]