mirror of
https://github.com/Llewellynvdm/zoxide.git
synced 2024-11-21 20:35:15 +00:00
Merge branch 'ajeetdsouza:main' into install
This commit is contained in:
commit
e7b753e365
@ -1,2 +0,0 @@
|
||||
[advisories]
|
||||
ignore = ["RUSTSEC-2020-0095"]
|
@ -1,2 +1,7 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
|
||||
# On Windows MSVC, statically link the C runtime so that the resulting EXE does
|
||||
# not depend on the vcruntime DLL.
|
||||
[target.'cfg(all(windows, target_env = "msvc"))']
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
|
@ -2,4 +2,9 @@ version = 1
|
||||
|
||||
[[analyzers]]
|
||||
name = "rust"
|
||||
enabled = true
|
||||
|
||||
[analyzers.meta]
|
||||
msrv = "stable"
|
||||
|
||||
[[analyzers]]
|
||||
name = "shell"
|
22
.github/CODE_OF_CONDUCT.md
vendored
22
.github/CODE_OF_CONDUCT.md
vendored
@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
|
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
@ -4,31 +4,51 @@ on:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
env:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_TERM_COLOR: always
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
ci:
|
||||
name: ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
components: clippy
|
||||
profile: minimal
|
||||
override: true
|
||||
- uses: cachix/install-nix-action@v15
|
||||
toolchain: stable
|
||||
- uses: actions-rs/toolchain@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
components: rustfmt
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
- uses: cachix/install-nix-action@v23
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixpkgs-unstable
|
||||
|
||||
- run: cargo xtask ci
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
- run: nix-shell --cores 0 --pure --run 'rm -rf ~/.cargo/bin; cargo xtask ci'
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
if: ${{ matrix.os != 'windows-latest' && env.CACHIX_AUTH_TOKEN != '' }}
|
||||
with:
|
||||
authToken: ${{ env.CACHIX_AUTH_TOKEN }}
|
||||
name: zoxide
|
||||
- name: Setup cache
|
||||
uses: Swatinem/rust-cache@v2.7.0
|
||||
with:
|
||||
key: ${{ matrix.os }}
|
||||
- name: Install just
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: just
|
||||
- name: Run lints + tests
|
||||
run: just lint test
|
||||
|
22
.github/workflows/no-response.yml
vendored
Normal file
22
.github/workflows/no-response.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
name: no-response
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # daily at 00:00
|
||||
jobs:
|
||||
no-response:
|
||||
if: github.repository == 'ajeetdsouza/zoxide'
|
||||
permissions:
|
||||
issues: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: lee-dohm/no-response@v0.5.0
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
daysUntilClose: 30
|
||||
responseRequiredLabel: waiting-for-response
|
||||
closeComment: >
|
||||
This issue has been automatically closed due to inactivity. If you feel
|
||||
this is still relevant, please comment here or create a fresh issue.
|
||||
|
86
.github/workflows/release.yml
vendored
86
.github/workflows/release.yml
vendored
@ -1,11 +1,11 @@
|
||||
name: release
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
release:
|
||||
name: ${{ matrix.target }}
|
||||
@ -16,38 +16,33 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
deb: true
|
||||
- os: ubuntu-latest
|
||||
target: arm-unknown-linux-musleabihf
|
||||
- os: ubuntu-latest
|
||||
target: armv7-unknown-linux-musleabihf
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-unknown-linux-musl
|
||||
|
||||
deb: true
|
||||
- os: macos-11
|
||||
target: x86_64-apple-darwin
|
||||
- os: macos-11
|
||||
target: aarch64-apple-darwin
|
||||
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
- os: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set artifact name
|
||||
shell: bash
|
||||
run: |
|
||||
version="$(git describe --tags --match='v*.*.*' --always)"
|
||||
name="zoxide-$version-${{ matrix.target }}"
|
||||
echo "ARTIFACT_NAME=$name" >> $GITHUB_ENV
|
||||
|
||||
echo "version: $version"
|
||||
echo "artifact: $name"
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
uses: SebRollen/toml-action@v1.0.2
|
||||
with:
|
||||
file: Cargo.toml
|
||||
field: package.version
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@ -55,45 +50,58 @@ jobs:
|
||||
profile: minimal
|
||||
override: true
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Setup cache
|
||||
uses: Swatinem/rust-cache@v2.7.0
|
||||
with:
|
||||
key: ${{ matrix.target }}
|
||||
- name: Build binary
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release --locked --target=${{ matrix.target }} --color=always --verbose
|
||||
use-cross: ${{ runner.os == 'Linux' }}
|
||||
|
||||
- name: Install cargo-deb
|
||||
if: ${{ matrix.deb == true }}
|
||||
uses: actions-rs/install@v0.1
|
||||
with:
|
||||
crate: cargo-deb
|
||||
- name: Build deb
|
||||
if: ${{ matrix.deb == true }}
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: deb
|
||||
args: --no-build --no-strip --output=. --target=${{ matrix.target }}
|
||||
- name: Package (*nix)
|
||||
if: runner.os != 'Windows'
|
||||
run: >
|
||||
tar -cv
|
||||
CHANGELOG.md LICENSE README.md
|
||||
man/
|
||||
-C contrib/ completions/ -C ../
|
||||
-C target/${{ matrix.target }}/release/ zoxide
|
||||
| gzip --best > '${{ env.ARTIFACT_NAME }}.tar.gz'
|
||||
run: |
|
||||
tar -cv CHANGELOG.md LICENSE README.md man/ \
|
||||
-C contrib/ completions/ -C ../ \
|
||||
-C target/${{ matrix.target }}/release/ zoxide |
|
||||
gzip --best > \
|
||||
zoxide-${{ steps.get_version.outputs.value }}-${{ matrix.target }}.tar.gz
|
||||
- name: Package (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: >
|
||||
7z a ${{ env.ARTIFACT_NAME }}.zip
|
||||
CHANGELOG.md LICENSE README.md
|
||||
./man/
|
||||
./contrib/completions/
|
||||
./target/${{ matrix.target }}/release/zoxide.exe
|
||||
|
||||
run: |
|
||||
7z a zoxide-${{ steps.get_version.outputs.value }}-${{ matrix.target }}.zip `
|
||||
CHANGELOG.md LICENSE README.md ./man/ ./contrib/completions/ `
|
||||
./target/${{ matrix.target }}/release/zoxide.exe
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
path: |
|
||||
*.zip
|
||||
*.deb
|
||||
*.tar.gz
|
||||
|
||||
*.zip
|
||||
- name: Create release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
if: |
|
||||
github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'chore(release)')
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
*.zip
|
||||
*.deb
|
||||
*.tar.gz
|
||||
*.zip
|
||||
name: ${{ steps.get_version.outputs.value }}
|
||||
tag_name: ""
|
||||
|
13
.github/workflows/winget.yml
vendored
Normal file
13
.github/workflows/winget.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
name: winget
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||
with:
|
||||
identifier: ajeetdsouza.zoxide
|
||||
installers-regex: '-pc-windows-msvc\.zip$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@
|
||||
# Compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
target_nix/
|
||||
|
||||
# Backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
95
CHANGELOG.md
95
CHANGELOG.md
@ -7,22 +7,101 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
## [0.9.2] - 2023-08-04
|
||||
|
||||
### Added
|
||||
|
||||
- Short option `-a` for `zoxide query --all`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- PowerShell: use `global` scope for variables / functions.
|
||||
|
||||
## [0.9.1] - 2023-05-07
|
||||
|
||||
### Added
|
||||
|
||||
- Fish/Zsh: aliases on `__zoxide_z` will now use completions.
|
||||
- Nushell: add support for v0.78.0.
|
||||
- Fish: plugin now works on older versions.
|
||||
- PowerShell: warn when PowerShell version is too old for `z -` and `z +`.
|
||||
- PowerShell: support for PWD hooks on all versions.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fish: not providing `cd` completions when there is a space in the path.
|
||||
- Bash/Fish/Zsh: providing `z` completions when the last argument starts with `z!`.
|
||||
- Bash/Fish/Zsh: attempting to `cd` when the last argument is `z!`.
|
||||
|
||||
## [0.9.0] - 2023-01-08
|
||||
|
||||
### Added
|
||||
|
||||
- `edit` subcommand to adjust the scores of entries.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Zsh: completions clashing with `zsh-autocomplete`.
|
||||
- Fzf: 'invalid option' on macOS.
|
||||
- PowerShell: handle UTF-8 encoding correctly.
|
||||
- Zsh: don't hide output from `chpwd` hooks.
|
||||
- Nushell: upgrade minimum supported version to v0.73.0.
|
||||
- Zsh: fix extra space in interactive completions when no match is found.
|
||||
- Fzf: various improvements, upgrade minimum supported version to v0.33.0.
|
||||
- Nushell: accidental redefinition of hooks when initialized twice.
|
||||
|
||||
### Removed
|
||||
|
||||
- `remove -i` subcommand: use `edit` instead.
|
||||
|
||||
## [0.8.3] - 2022-09-02
|
||||
|
||||
### Added
|
||||
|
||||
- Nushell: support for `z -`.
|
||||
- Nushell: support for PWD hooks.
|
||||
|
||||
### Changed
|
||||
|
||||
- Fish: change fuzzy completion prefix to `z!`.
|
||||
- Zsh: allow `z` to navigate dirstack via `+n` and `-n`.
|
||||
- Fzf: improved preview window.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bash: double forward slash in completions.
|
||||
|
||||
## [0.8.2] - 2022-06-26
|
||||
|
||||
### Changed
|
||||
|
||||
- Fzf: show preview window below results.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bash/Fish/POSIX/Zsh: paths on Cygwin.
|
||||
- Fish: completions not working on certain systems.
|
||||
- Bash: completions not escaping spaces correctly.
|
||||
|
||||
## [0.8.1] - 2021-04-23
|
||||
|
||||
### Changed
|
||||
|
||||
- Manpages: moved to `man/man1/*.1`.
|
||||
- Replace `--no-aliases` with `--no-cmd`.
|
||||
- Elvish: upgrade minimum supported version to v0.18.0.
|
||||
- Nushell: upgrade minimum supported version to v0.61.0.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bash/Zsh: rename `_z` completion function to avoid conflicts with other shell
|
||||
plugins.
|
||||
- Elvish: upgrade to new lambda syntax.
|
||||
- Fzf: added `--keep-right` option by default, upgraded minimum version to
|
||||
v0.21.0.
|
||||
- Fzf: added `--keep-right` option by default, upgrade minimum supported version
|
||||
to v0.21.0.
|
||||
- Bash: only enable completions on 4.4+.
|
||||
- Fzf: bypass `ls` alias in preview window.
|
||||
- Retain ownership of database file.
|
||||
- `zoxide query --interactive` should not conflict with `--score`.
|
||||
|
||||
## [0.8.0] - 2021-12-25
|
||||
|
||||
@ -73,7 +152,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- PowerShell: Hook not initializing correctly.
|
||||
- PowerShell: hook not initializing correctly.
|
||||
|
||||
## [0.7.6] - 2021-10-13
|
||||
|
||||
@ -371,6 +450,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- GitHub Actions pipeline to build and upload releases.
|
||||
- Support for zsh.
|
||||
|
||||
[0.9.2]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.1...v0.9.2
|
||||
[0.9.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.9.0...v0.9.1
|
||||
[0.9.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.8.3...v0.9.0
|
||||
[0.8.3]: https://github.com/ajeetdsouza/zoxide/compare/v0.8.2...v0.8.3
|
||||
[0.8.2]: https://github.com/ajeetdsouza/zoxide/compare/v0.8.1...v0.8.2
|
||||
[0.8.1]: https://github.com/ajeetdsouza/zoxide/compare/v0.8.0...v0.8.1
|
||||
[0.8.0]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.9...v0.8.0
|
||||
[0.7.9]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.8...v0.7.9
|
||||
[0.7.8]: https://github.com/ajeetdsouza/zoxide/compare/v0.7.7...v0.7.8
|
||||
|
791
Cargo.lock
generated
791
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
92
Cargo.toml
92
Cargo.toml
@ -3,43 +3,50 @@ authors = ["Ajeet D'Souza <98ajeet@gmail.com>"]
|
||||
categories = ["command-line-utilities", "filesystem"]
|
||||
description = "A smarter cd command for your terminal"
|
||||
edition = "2021"
|
||||
keywords = ["cli"]
|
||||
homepage = "https://github.com/ajeetdsouza/zoxide"
|
||||
keywords = ["cli", "filesystem", "shell", "tool", "utility"]
|
||||
license = "MIT"
|
||||
name = "zoxide"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/ajeetdsouza/zoxide"
|
||||
rust-version = "1.59"
|
||||
version = "0.8.0"
|
||||
rust-version = "1.65"
|
||||
version = "0.9.2"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[workspace]
|
||||
members = ["xtask/"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.32"
|
||||
askama = { version = "0.11.0", default-features = false }
|
||||
askama = { version = "0.12.0", default-features = false }
|
||||
bincode = "1.3.1"
|
||||
clap = { version = "3.1.0", features = ["derive"] }
|
||||
dirs = "4.0.0"
|
||||
clap = { version = "4.3.0", features = ["derive"] }
|
||||
color-print = "0.3.4"
|
||||
dirs = "5.0.0"
|
||||
dunce = "1.0.1"
|
||||
fastrand = "1.7.0"
|
||||
fastrand = "2.0.0"
|
||||
glob = "0.3.0"
|
||||
ordered-float = "2.0.0"
|
||||
ouroboros = "0.17.2"
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = "0.23.1"
|
||||
nix = { version = "0.26.1", default-features = false, features = [
|
||||
"fs",
|
||||
"user",
|
||||
] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
which = "4.2.5"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.0", features = ["derive"] }
|
||||
clap_complete = "3.1.0"
|
||||
clap_complete_fig = "3.1.0"
|
||||
clap = { version = "4.3.0", features = ["derive"] }
|
||||
clap_complete = "4.3.0"
|
||||
clap_complete_fig = "4.3.0"
|
||||
color-print = "0.3.4"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0.0"
|
||||
rstest = "0.12.0"
|
||||
rstest_reuse = "0.3.0"
|
||||
rstest = { version = "0.18.0", default-features = false }
|
||||
rstest_reuse = "0.6.0"
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[features]
|
||||
@ -48,5 +55,56 @@ nix-dev = []
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
debug = 0
|
||||
lto = true
|
||||
strip = true
|
||||
|
||||
[package.metadata.deb]
|
||||
assets = [
|
||||
[
|
||||
"target/release/zoxide",
|
||||
"usr/bin/",
|
||||
"755",
|
||||
],
|
||||
[
|
||||
"contrib/completions/zoxide.bash",
|
||||
"usr/share/bash-completion/completions/zoxide",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"contrib/completions/zoxide.fish",
|
||||
"usr/share/fish/vendor_completions.d/",
|
||||
"664",
|
||||
],
|
||||
[
|
||||
"contrib/completions/_zoxide",
|
||||
"usr/share/zsh/vendor-completions/",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"man/man1/*",
|
||||
"usr/share/man/man1/",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"README.md",
|
||||
"usr/share/doc/zoxide/",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"CHANGELOG.md",
|
||||
"usr/share/doc/zoxide/",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"LICENSE",
|
||||
"usr/share/doc/zoxide/",
|
||||
"644",
|
||||
],
|
||||
]
|
||||
extended-description = """\
|
||||
zoxide is a smarter cd command, inspired by z and autojump. It remembers which \
|
||||
directories you use most frequently, so you can "jump" to them in just a few \
|
||||
keystrokes."""
|
||||
priority = "optional"
|
||||
section = "utils"
|
||||
|
2
Cross.toml
Normal file
2
Cross.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[build.env]
|
||||
passthrough = ["CARGO_INCREMENTAL"]
|
613
README.md
613
README.md
@ -9,6 +9,20 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
<sup>Special thanks to:</sup>
|
||||
|
||||
<a href="https://www.warp.dev/?utm_source=github&utm_medium=referral&utm_campaign=zoxide_20231001">
|
||||
<div>
|
||||
<img src="contrib/warp.png" width="230" alt="Warp" />
|
||||
</div>
|
||||
<b>Warp is a modern, Rust-based terminal with AI built in so you and your team can build great software, faster.</b>
|
||||
<div>
|
||||
<sup>Visit <u>warp.dev</u> to learn more.</sup>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<hr />
|
||||
|
||||
# zoxide
|
||||
|
||||
[![crates.io][crates.io-badge]][crates.io]
|
||||
@ -51,240 +65,300 @@ Read more about the matching algorithm [here][algorithm-matching].
|
||||
|
||||
## Installation
|
||||
|
||||
### *Step 1: Install zoxide*
|
||||
|
||||
zoxide runs on most major platforms. If your platform isn't listed below,
|
||||
please [open an issue][issues].
|
||||
|
||||
<details>
|
||||
<summary>Linux</summary>
|
||||
|
||||
To install zoxide, run this command in your terminal:
|
||||
|
||||
```sh
|
||||
curl -sS https://webinstall.dev/zoxide | bash
|
||||
```
|
||||
|
||||
Alternatively, you can use a package manager:
|
||||
|
||||
| Distribution | Repository | Instructions |
|
||||
| ------------------ | ----------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| ***Any*** | **[crates.io]** | `cargo install zoxide --locked` |
|
||||
| *Any* | [conda-forge] | `conda install -c conda-forge zoxide` |
|
||||
| *Any* | [Linuxbrew] | `brew install zoxide` |
|
||||
| Alpine Linux 3.13+ | [Alpine Linux Packages] | `apk add zoxide` |
|
||||
| Arch Linux | [Arch Linux Community] | `pacman -S zoxide` |
|
||||
| CentOS 7+ | [Copr] | `dnf copr enable atim/zoxide` <br /> `dnf install zoxide` |
|
||||
| Debian 11+ | [Debian Packages] | `apt install zoxide` |
|
||||
| Devuan 4.0+ | [Devuan Packages] | `apt install zoxide` |
|
||||
| Fedora 32+ | [Fedora Packages] | `dnf install zoxide` |
|
||||
| Gentoo | [GURU Overlay] | `eselect repository enable guru` <br /> `emerge --sync guru` <br /> `emerge app-shells/zoxide` |
|
||||
| Manjaro | | `pacman -S zoxide` |
|
||||
| NixOS | [nixpkgs] | `nix-env -iA nixpkgs.zoxide` |
|
||||
| Parrot OS | | `apt install zoxide` |
|
||||
| Raspbian 11+ | [Raspbian Packages] | `apt install zoxide` |
|
||||
| Ubuntu 21.04+ | [Ubuntu Packages] | `apt install zoxide` |
|
||||
| Void Linux | [Void Linux Packages] | `xbps-install -S zoxide` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>macOS</summary>
|
||||
|
||||
To install zoxide, use a package manager:
|
||||
|
||||
| Repository | Instructions |
|
||||
| --------------- | ------------------------------------- |
|
||||
| **[crates.io]** | `cargo install zoxide --locked` |
|
||||
| [conda-forge] | `conda install -c conda-forge zoxide` |
|
||||
| [Homebrew] | `brew install zoxide` |
|
||||
| [MacPorts] | `port install zoxide` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Windows</summary>
|
||||
|
||||
To install zoxide, run this command in your command prompt:
|
||||
|
||||
```sh
|
||||
curl.exe -A "MS" https://webinstall.dev/zoxide | powershell
|
||||
```
|
||||
|
||||
Alternatively, you can use a package manager:
|
||||
|
||||
| Repository | Instructions |
|
||||
| --------------- | ------------------------------------- |
|
||||
| **[crates.io]** | `cargo install zoxide --locked` |
|
||||
| [Chocolatey] | `choco install zoxide` |
|
||||
| [conda-forge] | `conda install -c conda-forge zoxide` |
|
||||
| [Scoop] | `scoop install zoxide` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>BSD</summary>
|
||||
|
||||
To install zoxide, use a package manager:
|
||||
|
||||
| Distribution | Repository | Instructions |
|
||||
| ------------- | --------------- | ------------------------------- |
|
||||
| ***Any*** | **[crates.io]** | `cargo install zoxide --locked` |
|
||||
| DragonFly BSD | [DPorts] | `pkg install zoxide` |
|
||||
| FreeBSD | [FreshPorts] | `pkg install zoxide` |
|
||||
| NetBSD | [pkgsrc] | `pkgin install zoxide` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Android</summary>
|
||||
|
||||
To install zoxide, use a package manager:
|
||||
|
||||
| Repository | Instructions |
|
||||
| ---------- | -------------------- |
|
||||
| [Termux] | `pkg install zoxide` |
|
||||
|
||||
</details>
|
||||
|
||||
### *Step 2: Add zoxide to your shell*
|
||||
|
||||
To start using zoxide, add it to your shell.
|
||||
|
||||
<details>
|
||||
<summary>Bash</summary>
|
||||
|
||||
Add this to your configuration (usually `~/.bashrc`):
|
||||
|
||||
```sh
|
||||
eval "$(zoxide init bash)"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Elvish</summary>
|
||||
|
||||
Add this to your configuration (usually `~/.elvish/rc.elv`):
|
||||
|
||||
```sh
|
||||
eval (zoxide init elvish | slurp)
|
||||
```
|
||||
|
||||
Note: zoxide only supports elvish v0.16.0 and above.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Fish</summary>
|
||||
|
||||
Add this to your configuration (usually `~/.config/fish/config.fish`):
|
||||
|
||||
```fish
|
||||
zoxide init fish | source
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Nushell</summary>
|
||||
|
||||
Add this to your configuration (find it by running `config path` in Nushell):
|
||||
|
||||
```toml
|
||||
startup = ["zoxide init nushell --hook prompt | save ~/.zoxide.nu", "source ~/.zoxide.nu"]
|
||||
```
|
||||
|
||||
Note: zoxide only supports Nushell v0.37.0 and above.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>PowerShell</summary>
|
||||
|
||||
Add this to your configuration (find it by running `echo $profile` in
|
||||
PowerShell):
|
||||
|
||||
```powershell
|
||||
# For zoxide v0.8.0+
|
||||
Invoke-Expression (& {
|
||||
$hook = if ($PSVersionTable.PSVersion.Major -lt 6) { 'prompt' } else { 'pwd' }
|
||||
(zoxide init --hook $hook powershell | Out-String)
|
||||
})
|
||||
|
||||
# For older versions of zoxide
|
||||
Invoke-Expression (& {
|
||||
$hook = if ($PSVersionTable.PSVersion.Major -lt 6) { 'prompt' } else { 'pwd' }
|
||||
(zoxide init --hook $hook powershell) -join "`n"
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Xonsh</summary>
|
||||
|
||||
Add this to your configuration (usually `~/.xonshrc`):
|
||||
|
||||
```python
|
||||
execx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Zsh</summary>
|
||||
|
||||
Add this to your configuration (usually `~/.zshrc`):
|
||||
|
||||
```sh
|
||||
eval "$(zoxide init zsh)"
|
||||
```
|
||||
|
||||
For completions to work, the above line must be added _after_ `compinit` is
|
||||
called. You may have to rebuild your cache by running `rm ~/.zcompdump*; compinit`.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Any POSIX shell</summary>
|
||||
|
||||
Add this to your configuration:
|
||||
|
||||
```sh
|
||||
eval "$(zoxide init posix --hook prompt)"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### *Step 3: Install fzf (optional)*
|
||||
|
||||
[fzf] is a command-line fuzzy finder, used by zoxide for interactive
|
||||
selection. It can be installed from [here][fzf-installation]. zoxide supports
|
||||
fzf v0.21.0+.
|
||||
|
||||
### *Step 4: Import your data (optional)*
|
||||
|
||||
If you currently use any of the following utilities, you may want to import
|
||||
your data into zoxide:
|
||||
|
||||
<details>
|
||||
<summary>autojump</summary>
|
||||
|
||||
```sh
|
||||
zoxide import --from autojump path/to/db
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>z, z.lua, or zsh-z</summary>
|
||||
|
||||
```sh
|
||||
zoxide import --from z path/to/db
|
||||
```
|
||||
|
||||
</details>
|
||||
zoxide can be installed in 4 easy steps:
|
||||
|
||||
1. **Install binary**
|
||||
|
||||
zoxide runs on most major platforms. If your platform isn't listed below,
|
||||
please [open an issue][issues].
|
||||
|
||||
<details>
|
||||
<summary>Linux</summary>
|
||||
|
||||
> The recommended way to install zoxide is via the install script:
|
||||
>
|
||||
> ```sh
|
||||
> curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
|
||||
> ```
|
||||
>
|
||||
> Or, you can use a package manager:
|
||||
>
|
||||
> | Distribution | Repository | Instructions |
|
||||
> | ------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
> | **_Any_** | **[crates.io]** | `cargo install zoxide --locked` |
|
||||
> | _Any_ | [asdf] | `asdf plugin add zoxide https://github.com/nyrst/asdf-zoxide.git` <br /> `asdf install zoxide latest` |
|
||||
> | _Any_ | [conda-forge] | `conda install -c conda-forge zoxide` |
|
||||
> | _Any_ | [Linuxbrew] | `brew install zoxide` |
|
||||
> | _Any_ | [nixpkgs] | `nix-env -iA nixpkgs.zoxide` |
|
||||
> | Alpine Linux 3.13+ | [Alpine Linux Packages] | `apk add zoxide` |
|
||||
> | Arch Linux | [Arch Linux Extra] | `pacman -S zoxide` |
|
||||
> | CentOS 7+ | [Copr] | `dnf copr enable atim/zoxide` <br /> `dnf install zoxide` |
|
||||
> | Debian 11+[^1] | [Debian Packages] | `apt install zoxide` |
|
||||
> | Devuan 4.0+[^1] | [Devuan Packages] | `apt install zoxide` |
|
||||
> | Fedora 32+ | [Fedora Packages] | `dnf install zoxide` |
|
||||
> | Gentoo | [GURU Overlay] | `eselect repository enable guru` <br /> `emerge --sync guru` <br /> `emerge app-shells/zoxide` |
|
||||
> | Manjaro | | `pacman -S zoxide` |
|
||||
> | openSUSE Tumbleweed | [openSUSE Factory] | `zypper install zoxide` |
|
||||
> | Parrot OS[^1] | | `apt install zoxide` |
|
||||
> | Raspbian 11+[^1] | [Raspbian Packages] | `apt install zoxide` |
|
||||
> | Rhino Linux | [Pacstall Packages] | `pacstall -I zoxide-deb` |
|
||||
> | Slackware 15.0+ | [SlackBuilds] | [Instructions][slackbuilds-howto] |
|
||||
> | Ubuntu 21.04+[^1] | [Ubuntu Packages] | `apt install zoxide` |
|
||||
> | Void Linux | [Void Linux Packages] | `xbps-install -S zoxide` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>macOS</summary>
|
||||
|
||||
> To install zoxide, use a package manager:
|
||||
>
|
||||
> | Repository | Instructions |
|
||||
> | --------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
> | **[crates.io]** | `cargo install zoxide --locked` |
|
||||
> | **[Homebrew]** | `brew install zoxide` |
|
||||
> | [asdf] | `asdf plugin add zoxide https://github.com/nyrst/asdf-zoxide.git` <br /> `asdf install zoxide latest` |
|
||||
> | [conda-forge] | `conda install -c conda-forge zoxide` |
|
||||
> | [MacPorts] | `port install zoxide` |
|
||||
> | [nixpkgs] | `nix-env -iA nixpkgs.zoxide` |
|
||||
>
|
||||
> Or, run this command in your terminal:
|
||||
>
|
||||
> ```sh
|
||||
> curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Windows</summary>
|
||||
|
||||
> The recommended way to install zoxide is via `winget`:
|
||||
>
|
||||
> ```sh
|
||||
> winget install ajeetdsouza.zoxide
|
||||
> ```
|
||||
>
|
||||
> Or, you can use an alternative package manager:
|
||||
>
|
||||
> | Repository | Instructions |
|
||||
> | --------------- | ------------------------------------- |
|
||||
> | **[crates.io]** | `cargo install zoxide --locked` |
|
||||
> | [Chocolatey] | `choco install zoxide` |
|
||||
> | [conda-forge] | `conda install -c conda-forge zoxide` |
|
||||
> | [Scoop] | `scoop install zoxide` |
|
||||
>
|
||||
> If you're using Cygwin, Git Bash, or MSYS2, use the install script instead:
|
||||
>
|
||||
> ```sh
|
||||
> curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>BSD</summary>
|
||||
|
||||
> To install zoxide, use a package manager:
|
||||
>
|
||||
> | Distribution | Repository | Instructions |
|
||||
> | ------------- | --------------- | ------------------------------- |
|
||||
> | **_Any_** | **[crates.io]** | `cargo install zoxide --locked` |
|
||||
> | DragonFly BSD | [DPorts] | `pkg install zoxide` |
|
||||
> | FreeBSD | [FreshPorts] | `pkg install zoxide` |
|
||||
> | NetBSD | [pkgsrc] | `pkgin install zoxide` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Android</summary>
|
||||
|
||||
> To install zoxide, use a package manager:
|
||||
>
|
||||
> | Repository | Instructions |
|
||||
> | ---------- | -------------------- |
|
||||
> | [Termux] | `pkg install zoxide` |
|
||||
|
||||
</details>
|
||||
|
||||
2. **Setup zoxide on your shell**
|
||||
|
||||
To start using zoxide, add it to your shell.
|
||||
|
||||
<details>
|
||||
<summary>Bash</summary>
|
||||
|
||||
> Add this to the **end** of your config file (usually `~/.bashrc`):
|
||||
>
|
||||
> ```sh
|
||||
> eval "$(zoxide init bash)"
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Elvish</summary>
|
||||
|
||||
> Add this to the **end** of your config file (usually `~/.elvish/rc.elv`):
|
||||
>
|
||||
> ```sh
|
||||
> eval (zoxide init elvish | slurp)
|
||||
> ```
|
||||
>
|
||||
> **Note**
|
||||
> zoxide only supports elvish v0.18.0 and above.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Fish</summary>
|
||||
|
||||
> Add this to the **end** of your config file (usually
|
||||
> `~/.config/fish/config.fish`):
|
||||
>
|
||||
> ```fish
|
||||
> zoxide init fish | source
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Nushell</summary>
|
||||
|
||||
> Add this to the **end** of your env file (find it by running `$nu.env-path`
|
||||
> in Nushell):
|
||||
>
|
||||
> ```sh
|
||||
> zoxide init nushell | save -f ~/.zoxide.nu
|
||||
> ```
|
||||
>
|
||||
> Now, add this to the **end** of your config file (find it by running
|
||||
> `$nu.config-path` in Nushell):
|
||||
>
|
||||
> ```sh
|
||||
> source ~/.zoxide.nu
|
||||
> ```
|
||||
>
|
||||
> **Note**
|
||||
> zoxide only supports Nushell v0.73.0 and above.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>PowerShell</summary>
|
||||
|
||||
> Add this to the **end** of your config file (find it by running
|
||||
> `echo $profile` in PowerShell):
|
||||
>
|
||||
> ```powershell
|
||||
> Invoke-Expression (& { (zoxide init powershell | Out-String) })
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Xonsh</summary>
|
||||
|
||||
> Add this to the **end** of your config file (usually `~/.xonshrc`):
|
||||
>
|
||||
> ```python
|
||||
> execx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Zsh</summary>
|
||||
|
||||
> Add this to the **end** of your config file (usually `~/.zshrc`):
|
||||
>
|
||||
> ```sh
|
||||
> eval "$(zoxide init zsh)"
|
||||
> ```
|
||||
>
|
||||
> For completions to work, the above line must be added _after_ `compinit` is
|
||||
> called. You may have to rebuild your completions cache by running
|
||||
> `rm ~/.zcompdump*; compinit`.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Any POSIX shell</summary>
|
||||
|
||||
> Add this to the **end** of your config file:
|
||||
>
|
||||
> ```sh
|
||||
> eval "$(zoxide init posix --hook prompt)"
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
3. **Install fzf** <sup>(optional)</sup>
|
||||
|
||||
[fzf] is a command-line fuzzy finder, used by zoxide for completions /
|
||||
interactive selection. It can be installed from [here][fzf-installation].
|
||||
|
||||
> **Note**
|
||||
> zoxide only supports fzf v0.33.0 and above.
|
||||
|
||||
4. **Import your data** <sup>(optional)</sup>
|
||||
|
||||
If you currently use any of these plugins, you may want to import your data
|
||||
into zoxide:
|
||||
|
||||
<details>
|
||||
<summary>autojump</summary>
|
||||
|
||||
> Run this command in your terminal:
|
||||
>
|
||||
> ```sh
|
||||
> zoxide import --from=autojump "/path/to/autojump/db"
|
||||
> ```
|
||||
>
|
||||
> The path usually varies according to your system:
|
||||
>
|
||||
> | OS | Path | Example |
|
||||
> | ------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------ |
|
||||
> | Linux | `$XDG_DATA_HOME/autojump/autojump.txt` or `$HOME/.local/share/autojump/autojump.txt` | `/home/alice/.local/share/autojump/autojump.txt` |
|
||||
> | macOS | `$HOME/Library/autojump/autojump.txt` | `/Users/Alice/Library/autojump/autojump.txt` |
|
||||
> | Windows | `%APPDATA%\autojump\autojump.txt` | `C:\Users\Alice\AppData\Roaming\autojump\autojump.txt` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>fasd, z, z.lua, zsh-z</summary>
|
||||
|
||||
> Run this command in your terminal:
|
||||
>
|
||||
> ```sh
|
||||
> zoxide import --from=z "path/to/z/db"
|
||||
> ```
|
||||
>
|
||||
> The path usually varies according to your system:
|
||||
>
|
||||
> | Plugin | Path |
|
||||
> | ---------------- | ----------------------------------------------------------------------------------- |
|
||||
> | fasd | `$_FASD_DATA` or `$HOME/.fasd` |
|
||||
> | z (bash/zsh) | `$_Z_DATA` or `$HOME/.z` |
|
||||
> | z (fish) | `$Z_DATA` or `$XDG_DATA_HOME/z/data` or `$HOME/.local/share/z/data` |
|
||||
> | z.lua (bash/zsh) | `$_ZL_DATA` or `$HOME/.zlua` |
|
||||
> | z.lua (fish) | `$XDG_DATA_HOME/zlua/zlua.txt` or `$HOME/.local/share/zlua/zlua.txt` or `$_ZL_DATA` |
|
||||
> | zsh-z | `$ZSHZ_DATA` or `$_Z_DATA` or `$HOME/.z` |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>ZLocation</summary>
|
||||
|
||||
> Run this command in PowerShell:
|
||||
>
|
||||
> ```powershell
|
||||
> $db = New-TemporaryFile
|
||||
> (Get-ZLocation).GetEnumerator() | ForEach-Object { Write-Output ($_.Name+'|'+$_.Value+'|0') } | Out-File $db
|
||||
> zoxide import --from=z $db
|
||||
> ```
|
||||
|
||||
</details>
|
||||
|
||||
## Configuration
|
||||
|
||||
@ -293,8 +367,8 @@ zoxide import --from z path/to/db
|
||||
When calling `zoxide init`, the following flags are available:
|
||||
|
||||
- `--cmd`
|
||||
- Changes the prefix of predefined aliases (`z`, `zi`).
|
||||
- `--cmd j` would change the aliases to (`j`, `ji`).
|
||||
- Changes the prefix of the `z` and `zi` commands.
|
||||
- `--cmd j` would change the commands to (`j`, `ji`).
|
||||
- `--cmd cd` would replace the `cd` command (doesn't work on Nushell / POSIX shells).
|
||||
- `--hook <HOOK>`
|
||||
- Changes how often zoxide increments a directory's score:
|
||||
@ -303,15 +377,15 @@ When calling `zoxide init`, the following flags are available:
|
||||
| `none` | Never |
|
||||
| `prompt` | At every shell prompt |
|
||||
| `pwd` | Whenever the directory is changed |
|
||||
- `--no-aliases`
|
||||
- Don't define aliases (`z`, `zi`).
|
||||
- `--no-cmd`
|
||||
- Prevents zoxide from defining the `z` and `zi` commands.
|
||||
- These functions will still be available in your shell as `__zoxide_z` and
|
||||
`__zoxide_zi`, should you choose to redefine them.
|
||||
|
||||
### Environment variables
|
||||
|
||||
Environment variables<sup>[?][wiki-env]</sup> can be used for configuration.
|
||||
They must be set before `zoxide init` is called.
|
||||
Environment variables[^2] can be used for configuration. They must be set before
|
||||
`zoxide init` is called.
|
||||
|
||||
- `_ZO_DATA_DIR`
|
||||
- Specifies the directory in which the database is stored.
|
||||
@ -346,38 +420,55 @@ They must be set before `zoxide init` is called.
|
||||
|
||||
## Third-party integrations
|
||||
|
||||
| Application | Description | Plugin |
|
||||
| ------------------ | -------------------------------------------- | -------------------------- |
|
||||
| [clink] | Improved cmd.exe for Windows | [clink-zoxide] |
|
||||
| [emacs] | Text editor | [zoxide.el] |
|
||||
| [nnn] | File manager | [nnn-autojump] |
|
||||
| [ranger] | File manager | [ranger-zoxide] |
|
||||
| [telescope.nvim] | Fuzzy finder for Neovim | [telescope-zoxide] |
|
||||
| [vim] | Text editor | [zoxide.vim] |
|
||||
| [xplr] | File manager | [zoxide.xplr] |
|
||||
| [xxh] | Transports shell configuration over SSH | [xxh-plugin-prerun-zoxide] |
|
||||
| [zabb] | Finds the shortest possible query for a path | Natively supported |
|
||||
| [zsh-autocomplete] | Realtime completions for zsh | Natively supported |
|
||||
| Application | Description | Plugin |
|
||||
| --------------------- | -------------------------------------------- | -------------------------- |
|
||||
| [aerc] | Email client | Natively supported |
|
||||
| [clink] | Improved cmd.exe for Windows | [clink-zoxide] |
|
||||
| [emacs] | Text editor | [zoxide.el] |
|
||||
| [felix] | File manager | Natively supported |
|
||||
| [joshuto] | File manager | Natively supported |
|
||||
| [lf] | File manager | See the [wiki][lf-wiki] |
|
||||
| [nnn] | File manager | [nnn-autojump] |
|
||||
| [ranger] | File manager | [ranger-zoxide] |
|
||||
| [telescope.nvim] | Fuzzy finder for Neovim | [telescope-zoxide] |
|
||||
| [t] | `tmux` session manager | Natively supported |
|
||||
| [tmux-session-wizard] | `tmux` session manager | Natively supported |
|
||||
| [vim] / [neovim] | Text editor | [zoxide.vim] |
|
||||
| [xplr] | File manager | [zoxide.xplr] |
|
||||
| [xxh] | Transports shell configuration over SSH | [xxh-plugin-prerun-zoxide] |
|
||||
| [zabb] | Finds the shortest possible query for a path | Natively supported |
|
||||
| [zsh-autocomplete] | Realtime completions for zsh | Natively supported |
|
||||
|
||||
[^1]:
|
||||
Debian / Ubuntu derivatives update their packages very slowly. If you're
|
||||
using one of these distributions, consider using the install script instead.
|
||||
|
||||
[^2]:
|
||||
If you're not sure how to set an environment variable on your shell, check
|
||||
out the [wiki][wiki-env].
|
||||
|
||||
[aerc]: https://github.com/rjarry/aerc
|
||||
[algorithm-aging]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#aging
|
||||
[algorithm-matching]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#matching
|
||||
[alpine linux packages]: https://pkgs.alpinelinux.org/packages?name=zoxide
|
||||
[arch linux community]: https://archlinux.org/packages/community/x86_64/zoxide/
|
||||
[builtwithnix-badge]: https://img.shields.io/badge/builtwith-nix-7d81f7?style=flat-square
|
||||
[arch linux extra]: https://archlinux.org/packages/extra/x86_64/zoxide/
|
||||
[asdf]: https://github.com/asdf-vm/asdf
|
||||
[builtwithnix-badge]: https://img.shields.io/badge/builtwith-nix-7d81f7?logo=nixos&logoColor=white&style=flat-square
|
||||
[builtwithnix]: https://builtwithnix.org/
|
||||
[chocolatey]: https://community.chocolatey.org/packages/zoxide
|
||||
[clink-zoxide]: https://github.com/shunsambongi/clink-zoxide
|
||||
[clink]: https://github.com/mridgers/clink
|
||||
[conda-forge]: https://anaconda.org/conda-forge/zoxide
|
||||
[copr]: https://copr.fedorainfracloud.org/coprs/atim/zoxide/
|
||||
[crates.io-badge]: https://img.shields.io/crates/v/zoxide?style=flat-square
|
||||
[crates.io-badge]: https://img.shields.io/crates/v/zoxide?logo=rust&logoColor=white&style=flat-square
|
||||
[crates.io]: https://crates.io/crates/zoxide
|
||||
[debian packages]: https://packages.debian.org/stable/admin/zoxide
|
||||
[devuan packages]: https://pkginfo.devuan.org/cgi-bin/package-query.html?c=package&q=zoxide
|
||||
[downloads-badge]: https://img.shields.io/github/downloads/ajeetdsouza/zoxide/total?style=flat-square
|
||||
[downloads-badge]: https://img.shields.io/github/downloads/ajeetdsouza/zoxide/total?logo=github&logoColor=white&style=flat-square
|
||||
[dports]: https://github.com/DragonFlyBSD/DPorts/tree/master/sysutils/zoxide
|
||||
[emacs]: https://www.gnu.org/software/emacs/
|
||||
[fedora packages]: https://src.fedoraproject.org/rpms/rust-zoxide
|
||||
[felix]: https://github.com/kyoheiu/felix
|
||||
[freshports]: https://www.freshports.org/sysutils/zoxide/
|
||||
[fzf-installation]: https://github.com/junegunn/fzf#installation
|
||||
[fzf-man]: https://manpages.ubuntu.com/manpages/en/man1/fzf.1.html
|
||||
@ -386,23 +477,31 @@ They must be set before `zoxide init` is called.
|
||||
[guru overlay]: https://github.com/gentoo-mirror/guru
|
||||
[homebrew]: https://formulae.brew.sh/formula/zoxide
|
||||
[issues]: https://github.com/ajeetdsouza/zoxide/issues/new
|
||||
[joshuto]: https://github.com/kamiyaa/joshuto
|
||||
[lf]: https://github.com/gokcehan/lf
|
||||
[lf-wiki]: https://github.com/gokcehan/lf/wiki/Integrations#zoxide
|
||||
[linuxbrew]: https://formulae.brew.sh/formula-linux/zoxide
|
||||
[macports]: https://ports.macports.org/port/zoxide/summary
|
||||
[neovim]: https://github.com/neovim/neovim
|
||||
[nixpkgs]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/misc/zoxide/default.nix
|
||||
[nnn-autojump]: https://github.com/jarun/nnn/blob/master/plugins/autojump
|
||||
[nnn]: https://github.com/jarun/nnn
|
||||
[opensuse factory]: https://build.opensuse.org/package/show/openSUSE:Factory/zoxide
|
||||
[pacstall packages]: https://pacstall.dev/packages/zoxide-deb
|
||||
[pkgsrc]: https://pkgsrc.se/sysutils/zoxide
|
||||
[ranger-zoxide]: https://github.com/jchook/ranger-zoxide
|
||||
[ranger]: https://github.com/ranger/ranger
|
||||
[raspbian packages]: https://archive.raspbian.org/raspbian/pool/main/r/rust-zoxide/
|
||||
[releases]: https://github.com/ajeetdsouza/zoxide/releases
|
||||
[scoop]: https://github.com/ScoopInstaller/Main/tree/master/bucket/zoxide.json
|
||||
[slackbuilds-howto]: https://slackbuilds.org/howto/
|
||||
[t]: https://github.com/joshmedeski/t-smart-tmux-session-manager
|
||||
[telescope-zoxide]: https://github.com/jvgrootveld/telescope-zoxide
|
||||
[telescope.nvim]: https://github.com/nvim-telescope/telescope.nvim
|
||||
[termux]: https://github.com/termux/termux-packages/tree/master/packages/zoxide
|
||||
[tmux-session-wizard]: https://github.com/27medkamal/tmux-session-wizard
|
||||
[tutorial]: contrib/tutorial.webp
|
||||
[ubuntu packages]: https://packages.ubuntu.com/hirsute/zoxide
|
||||
[ubuntu packages]: https://packages.ubuntu.com/jammy/zoxide
|
||||
[vim]: https://github.com/vim/vim
|
||||
[void linux packages]: https://github.com/void-linux/void-packages/tree/master/srcpkgs/zoxide
|
||||
[wiki-env]: https://github.com/ajeetdsouza/zoxide/wiki/HOWTO:-set-environment-variables "HOWTO: set environment variables"
|
||||
|
66
build.rs
66
build.rs
@ -1,56 +1,34 @@
|
||||
use std::process::Command;
|
||||
#[path = "src/cmd/cmd.rs"]
|
||||
mod cmd;
|
||||
|
||||
use std::{env, io};
|
||||
|
||||
fn main() {
|
||||
let pkg_version = env!("CARGO_PKG_VERSION");
|
||||
let version = match env::var_os("PROFILE") {
|
||||
Some(profile) if profile == "release" => format!("v{}", pkg_version),
|
||||
_ => git_version().unwrap_or_else(|| format!("v{}-unknown", pkg_version)),
|
||||
};
|
||||
println!("cargo:rustc-env=ZOXIDE_VERSION={}", version);
|
||||
use clap::CommandFactory;
|
||||
use clap_complete::shells::{Bash, Elvish, Fish, PowerShell, Zsh};
|
||||
use clap_complete_fig::Fig;
|
||||
use cmd::Cmd;
|
||||
|
||||
// Since we are generating completions in the package directory, we need to set this so that
|
||||
// Cargo doesn't rebuild every time.
|
||||
fn main() -> io::Result<()> {
|
||||
// Since we are generating completions in the package directory, we need to
|
||||
// set this so that Cargo doesn't rebuild every time.
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=src");
|
||||
println!("cargo:rerun-if-changed=templates");
|
||||
println!("cargo:rerun-if-changed=tests");
|
||||
|
||||
generate_completions().unwrap();
|
||||
}
|
||||
|
||||
fn git_version() -> Option<String> {
|
||||
let dir = env!("CARGO_MANIFEST_DIR");
|
||||
let mut git = Command::new("git");
|
||||
git.args(&["-C", dir, "describe", "--tags", "--match=v*.*.*", "--always", "--broken"]);
|
||||
|
||||
let output = git.output().ok()?;
|
||||
if !output.status.success() || output.stdout.is_empty() || !output.stderr.is_empty() {
|
||||
return None;
|
||||
}
|
||||
String::from_utf8(output.stdout).ok()
|
||||
println!("cargo:rerun-if-changed=src/");
|
||||
println!("cargo:rerun-if-changed=templates/");
|
||||
println!("cargo:rerun-if-changed=tests/");
|
||||
generate_completions()
|
||||
}
|
||||
|
||||
fn generate_completions() -> io::Result<()> {
|
||||
#[path = "src/cmd/_cmd.rs"]
|
||||
mod cmd;
|
||||
|
||||
use clap::CommandFactory;
|
||||
use clap_complete::generate_to;
|
||||
use clap_complete::shells::{Bash, Elvish, Fish, PowerShell, Zsh};
|
||||
use clap_complete_fig::Fig;
|
||||
use cmd::Cmd;
|
||||
|
||||
const BIN_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
const OUT_DIR: &str = "contrib/completions";
|
||||
let cmd = &mut Cmd::command();
|
||||
let bin_name = env!("CARGO_PKG_NAME");
|
||||
let out_dir = "contrib/completions";
|
||||
|
||||
generate_to(Bash, cmd, bin_name, out_dir)?;
|
||||
generate_to(Elvish, cmd, bin_name, out_dir)?;
|
||||
generate_to(Fig, cmd, bin_name, out_dir)?;
|
||||
generate_to(Fish, cmd, bin_name, out_dir)?;
|
||||
generate_to(PowerShell, cmd, bin_name, out_dir)?;
|
||||
generate_to(Zsh, cmd, bin_name, out_dir)?;
|
||||
clap_complete::generate_to(Bash, cmd, BIN_NAME, OUT_DIR)?;
|
||||
clap_complete::generate_to(Elvish, cmd, BIN_NAME, OUT_DIR)?;
|
||||
clap_complete::generate_to(Fig, cmd, BIN_NAME, OUT_DIR)?;
|
||||
clap_complete::generate_to(Fish, cmd, BIN_NAME, OUT_DIR)?;
|
||||
clap_complete::generate_to(PowerShell, cmd, BIN_NAME, OUT_DIR)?;
|
||||
clap_complete::generate_to(Zsh, cmd, BIN_NAME, OUT_DIR)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
157
contrib/completions/_zoxide
generated
157
contrib/completions/_zoxide
generated
@ -15,10 +15,10 @@ _zoxide() {
|
||||
|
||||
local context curcontext="$curcontext" state line
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Print help information]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
":: :_zoxide_commands" \
|
||||
"*::: :->zoxide" \
|
||||
&& ret=0
|
||||
@ -30,61 +30,115 @@ _zoxide() {
|
||||
case $line[1] in
|
||||
(add)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Print help information]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
'*::paths:_files -/' \
|
||||
&& ret=0
|
||||
;;
|
||||
(edit)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
":: :_zoxide__edit_commands" \
|
||||
"*::: :->edit" \
|
||||
&& ret=0
|
||||
|
||||
case $state in
|
||||
(edit)
|
||||
words=($line[1] "${words[@]}")
|
||||
(( CURRENT += 1 ))
|
||||
curcontext="${curcontext%:*:*}:zoxide-edit-command-$line[1]:"
|
||||
case $line[1] in
|
||||
(decrement)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
':path:' \
|
||||
&& ret=0
|
||||
;;
|
||||
(delete)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
':path:' \
|
||||
&& ret=0
|
||||
;;
|
||||
(increment)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
':path:' \
|
||||
&& ret=0
|
||||
;;
|
||||
(reload)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
&& ret=0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
(import)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--from=[Application to import from]:FROM:(autojump z)' \
|
||||
'--merge[Merge into existing database]' \
|
||||
'-h[Print help information]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
':path:_files' \
|
||||
&& ret=0
|
||||
;;
|
||||
(init)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--cmd=[Renames the '\''z'\'' command and corresponding aliases]:CMD: ' \
|
||||
'--hook=[Chooses event upon which an entry is added to the database]:HOOK:(none prompt pwd)' \
|
||||
'--no-aliases[Prevents zoxide from defining any commands]' \
|
||||
'-h[Print help information]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
'--cmd=[Changes the prefix of the \`z\` and \`zi\` commands]:CMD: ' \
|
||||
'--hook=[Changes how often zoxide increments a directory'\''s score]:HOOK:(none prompt pwd)' \
|
||||
'--no-cmd[Prevents zoxide from defining the \`z\` and \`zi\` commands]' \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
':shell:(bash elvish fish nushell posix powershell xonsh zsh)' \
|
||||
&& ret=0
|
||||
;;
|
||||
(query)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--exclude=[Exclude a path from results]:path:_files -/' \
|
||||
'--all[Show deleted directories]' \
|
||||
'--exclude=[Exclude the current directory]:path:_files -/' \
|
||||
'-a[Show unavailable directories]' \
|
||||
'--all[Show unavailable directories]' \
|
||||
'(-l --list)-i[Use interactive selection]' \
|
||||
'(-l --list)--interactive[Use interactive selection]' \
|
||||
'(-i --interactive)-l[List all matching directories]' \
|
||||
'(-i --interactive)--list[List all matching directories]' \
|
||||
'(-i --interactive)-s[Print score with results]' \
|
||||
'(-i --interactive)--score[Print score with results]' \
|
||||
'-h[Print help information]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
'-s[Print score with results]' \
|
||||
'--score[Print score with results]' \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
'*::keywords:' \
|
||||
&& ret=0
|
||||
;;
|
||||
(remove)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-i[Use interactive selection]' \
|
||||
'--interactive[Use interactive selection]' \
|
||||
'-h[Print help information]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
'--version[Print version]' \
|
||||
'*::paths:_files -/' \
|
||||
&& ret=0
|
||||
;;
|
||||
@ -97,6 +151,7 @@ esac
|
||||
_zoxide_commands() {
|
||||
local commands; commands=(
|
||||
'add:Add a new directory or increment its rank' \
|
||||
'edit:Edit the database' \
|
||||
'import:Import entries from another application' \
|
||||
'init:Generate shell configuration' \
|
||||
'query:Search for a directory in the database' \
|
||||
@ -109,11 +164,36 @@ _zoxide__add_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide add commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__edit__decrement_commands] )) ||
|
||||
_zoxide__edit__decrement_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide edit decrement commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__edit__delete_commands] )) ||
|
||||
_zoxide__edit__delete_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide edit delete commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__edit_commands] )) ||
|
||||
_zoxide__edit_commands() {
|
||||
local commands; commands=(
|
||||
'decrement:' \
|
||||
'delete:' \
|
||||
'increment:' \
|
||||
'reload:' \
|
||||
)
|
||||
_describe -t commands 'zoxide edit commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__import_commands] )) ||
|
||||
_zoxide__import_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide import commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__edit__increment_commands] )) ||
|
||||
_zoxide__edit__increment_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide edit increment commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__init_commands] )) ||
|
||||
_zoxide__init_commands() {
|
||||
local commands; commands=()
|
||||
@ -124,10 +204,19 @@ _zoxide__query_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide query commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__edit__reload_commands] )) ||
|
||||
_zoxide__edit__reload_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide edit reload commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_zoxide__remove_commands] )) ||
|
||||
_zoxide__remove_commands() {
|
||||
local commands; commands=()
|
||||
_describe -t commands 'zoxide remove commands' commands "$@"
|
||||
}
|
||||
|
||||
_zoxide "$@"
|
||||
if [ "$funcstack[1]" = "_zoxide" ]; then
|
||||
_zoxide "$@"
|
||||
else
|
||||
compdef _zoxide zoxide
|
||||
fi
|
||||
|
101
contrib/completions/_zoxide.ps1
generated
101
contrib/completions/_zoxide.ps1
generated
@ -21,11 +21,12 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
|
||||
|
||||
$completions = @(switch ($command) {
|
||||
'zoxide' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Add a new directory or increment its rank')
|
||||
[CompletionResult]::new('edit', 'edit', [CompletionResultType]::ParameterValue, 'Edit the database')
|
||||
[CompletionResult]::new('import', 'import', [CompletionResultType]::ParameterValue, 'Import entries from another application')
|
||||
[CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Generate shell configuration')
|
||||
[CompletionResult]::new('query', 'query', [CompletionResultType]::ParameterValue, 'Search for a directory in the database')
|
||||
@ -33,53 +34,91 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
|
||||
break
|
||||
}
|
||||
'zoxide;add' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
'zoxide;edit' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('decrement', 'decrement', [CompletionResultType]::ParameterValue, 'decrement')
|
||||
[CompletionResult]::new('delete', 'delete', [CompletionResultType]::ParameterValue, 'delete')
|
||||
[CompletionResult]::new('increment', 'increment', [CompletionResultType]::ParameterValue, 'increment')
|
||||
[CompletionResult]::new('reload', 'reload', [CompletionResultType]::ParameterValue, 'reload')
|
||||
break
|
||||
}
|
||||
'zoxide;edit;decrement' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
'zoxide;edit;delete' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
'zoxide;edit;increment' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
'zoxide;edit;reload' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
'zoxide;import' {
|
||||
[CompletionResult]::new('--from', 'from', [CompletionResultType]::ParameterName, 'Application to import from')
|
||||
[CompletionResult]::new('--merge', 'merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
'zoxide;init' {
|
||||
[CompletionResult]::new('--cmd', 'cmd', [CompletionResultType]::ParameterName, 'Renames the ''z'' command and corresponding aliases')
|
||||
[CompletionResult]::new('--hook', 'hook', [CompletionResultType]::ParameterName, 'Chooses event upon which an entry is added to the database')
|
||||
[CompletionResult]::new('--no-aliases', 'no-aliases', [CompletionResultType]::ParameterName, 'Prevents zoxide from defining any commands')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--cmd', 'cmd', [CompletionResultType]::ParameterName, 'Changes the prefix of the `z` and `zi` commands')
|
||||
[CompletionResult]::new('--hook', 'hook', [CompletionResultType]::ParameterName, 'Changes how often zoxide increments a directory''s score')
|
||||
[CompletionResult]::new('--no-cmd', 'no-cmd', [CompletionResultType]::ParameterName, 'Prevents zoxide from defining the `z` and `zi` commands')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
'zoxide;query' {
|
||||
[CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'Exclude a path from results')
|
||||
[CompletionResult]::new('--all', 'all', [CompletionResultType]::ParameterName, 'Show deleted directories')
|
||||
[CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'Exclude the current directory')
|
||||
[CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Show unavailable directories')
|
||||
[CompletionResult]::new('--all', 'all', [CompletionResultType]::ParameterName, 'Show unavailable directories')
|
||||
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
||||
[CompletionResult]::new('--interactive', 'interactive', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
||||
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List all matching directories')
|
||||
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List all matching directories')
|
||||
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Print score with results')
|
||||
[CompletionResult]::new('--score', 'score', [CompletionResultType]::ParameterName, 'Print score with results')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
'zoxide;remove' {
|
||||
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
||||
[CompletionResult]::new('--interactive', 'interactive', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||
break
|
||||
}
|
||||
})
|
||||
|
123
contrib/completions/zoxide.bash
generated
123
contrib/completions/zoxide.bash
generated
@ -1,5 +1,5 @@
|
||||
_zoxide() {
|
||||
local i cur prev opts cmds
|
||||
local i cur prev opts cmd
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
@ -8,24 +8,39 @@ _zoxide() {
|
||||
|
||||
for i in ${COMP_WORDS[@]}
|
||||
do
|
||||
case "${i}" in
|
||||
"$1")
|
||||
case "${cmd},${i}" in
|
||||
",$1")
|
||||
cmd="zoxide"
|
||||
;;
|
||||
add)
|
||||
cmd+="__add"
|
||||
zoxide,add)
|
||||
cmd="zoxide__add"
|
||||
;;
|
||||
import)
|
||||
cmd+="__import"
|
||||
zoxide,edit)
|
||||
cmd="zoxide__edit"
|
||||
;;
|
||||
init)
|
||||
cmd+="__init"
|
||||
zoxide,import)
|
||||
cmd="zoxide__import"
|
||||
;;
|
||||
query)
|
||||
cmd+="__query"
|
||||
zoxide,init)
|
||||
cmd="zoxide__init"
|
||||
;;
|
||||
remove)
|
||||
cmd+="__remove"
|
||||
zoxide,query)
|
||||
cmd="zoxide__query"
|
||||
;;
|
||||
zoxide,remove)
|
||||
cmd="zoxide__remove"
|
||||
;;
|
||||
zoxide__edit,decrement)
|
||||
cmd="zoxide__edit__decrement"
|
||||
;;
|
||||
zoxide__edit,delete)
|
||||
cmd="zoxide__edit__delete"
|
||||
;;
|
||||
zoxide__edit,increment)
|
||||
cmd="zoxide__edit__increment"
|
||||
;;
|
||||
zoxide__edit,reload)
|
||||
cmd="zoxide__edit__reload"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
@ -34,7 +49,7 @@ _zoxide() {
|
||||
|
||||
case "${cmd}" in
|
||||
zoxide)
|
||||
opts="-h -V --help --version add import init query remove"
|
||||
opts="-h -V --help --version add edit import init query remove"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
@ -61,6 +76,76 @@ _zoxide() {
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
zoxide__edit)
|
||||
opts="-h -V --help --version decrement delete increment reload"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
zoxide__edit__decrement)
|
||||
opts="-h -V --help --version <PATH>"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
zoxide__edit__delete)
|
||||
opts="-h -V --help --version <PATH>"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
zoxide__edit__increment)
|
||||
opts="-h -V --help --version <PATH>"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
zoxide__edit__reload)
|
||||
opts="-h -V --help --version"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
zoxide__import)
|
||||
opts="-h -V --from --merge --help --version <PATH>"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
@ -69,7 +154,7 @@ _zoxide() {
|
||||
fi
|
||||
case "${prev}" in
|
||||
--from)
|
||||
COMPREPLY=($(compgen -W "" -- "${cur}"))
|
||||
COMPREPLY=($(compgen -W "autojump z" -- "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
@ -80,7 +165,7 @@ _zoxide() {
|
||||
return 0
|
||||
;;
|
||||
zoxide__init)
|
||||
opts="-h -V --no-aliases --cmd --hook --help --version bash elvish fish nushell posix powershell xonsh zsh"
|
||||
opts="-h -V --no-cmd --cmd --hook --help --version bash elvish fish nushell posix powershell xonsh zsh"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
@ -91,7 +176,7 @@ _zoxide() {
|
||||
return 0
|
||||
;;
|
||||
--hook)
|
||||
COMPREPLY=($(compgen -W "" -- "${cur}"))
|
||||
COMPREPLY=($(compgen -W "none prompt pwd" -- "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
@ -102,7 +187,7 @@ _zoxide() {
|
||||
return 0
|
||||
;;
|
||||
zoxide__query)
|
||||
opts="-i -l -s -h -V --all --interactive --list --score --exclude --help --version <KEYWORDS>..."
|
||||
opts="-a -i -l -s -h -V --all --interactive --list --score --exclude --help --version [KEYWORDS]..."
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
@ -120,7 +205,7 @@ _zoxide() {
|
||||
return 0
|
||||
;;
|
||||
zoxide__remove)
|
||||
opts="-i -h -V --interactive --help --version <PATHS>..."
|
||||
opts="-h -V --help --version [PATHS]..."
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
96
contrib/completions/zoxide.elv
generated
96
contrib/completions/zoxide.elv
generated
@ -18,60 +18,94 @@ set edit:completion:arg-completer[zoxide] = {|@words|
|
||||
}
|
||||
var completions = [
|
||||
&'zoxide'= {
|
||||
cand -h 'Print help information'
|
||||
cand --help 'Print help information'
|
||||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
cand add 'Add a new directory or increment its rank'
|
||||
cand edit 'Edit the database'
|
||||
cand import 'Import entries from another application'
|
||||
cand init 'Generate shell configuration'
|
||||
cand query 'Search for a directory in the database'
|
||||
cand remove 'Remove a directory from the database'
|
||||
}
|
||||
&'zoxide;add'= {
|
||||
cand -h 'Print help information'
|
||||
cand --help 'Print help information'
|
||||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
&'zoxide;edit'= {
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
cand decrement 'decrement'
|
||||
cand delete 'delete'
|
||||
cand increment 'increment'
|
||||
cand reload 'reload'
|
||||
}
|
||||
&'zoxide;edit;decrement'= {
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
&'zoxide;edit;delete'= {
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
&'zoxide;edit;increment'= {
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
&'zoxide;edit;reload'= {
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
&'zoxide;import'= {
|
||||
cand --from 'Application to import from'
|
||||
cand --merge 'Merge into existing database'
|
||||
cand -h 'Print help information'
|
||||
cand --help 'Print help information'
|
||||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
&'zoxide;init'= {
|
||||
cand --cmd 'Renames the ''z'' command and corresponding aliases'
|
||||
cand --hook 'Chooses event upon which an entry is added to the database'
|
||||
cand --no-aliases 'Prevents zoxide from defining any commands'
|
||||
cand -h 'Print help information'
|
||||
cand --help 'Print help information'
|
||||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
cand --cmd 'Changes the prefix of the `z` and `zi` commands'
|
||||
cand --hook 'Changes how often zoxide increments a directory''s score'
|
||||
cand --no-cmd 'Prevents zoxide from defining the `z` and `zi` commands'
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
&'zoxide;query'= {
|
||||
cand --exclude 'Exclude a path from results'
|
||||
cand --all 'Show deleted directories'
|
||||
cand --exclude 'Exclude the current directory'
|
||||
cand -a 'Show unavailable directories'
|
||||
cand --all 'Show unavailable directories'
|
||||
cand -i 'Use interactive selection'
|
||||
cand --interactive 'Use interactive selection'
|
||||
cand -l 'List all matching directories'
|
||||
cand --list 'List all matching directories'
|
||||
cand -s 'Print score with results'
|
||||
cand --score 'Print score with results'
|
||||
cand -h 'Print help information'
|
||||
cand --help 'Print help information'
|
||||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
&'zoxide;remove'= {
|
||||
cand -i 'Use interactive selection'
|
||||
cand --interactive 'Use interactive selection'
|
||||
cand -h 'Print help information'
|
||||
cand --help 'Print help information'
|
||||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
cand --version 'Print version'
|
||||
}
|
||||
]
|
||||
$completions[$command]
|
||||
|
50
contrib/completions/zoxide.fish
generated
50
contrib/completions/zoxide.fish
generated
@ -1,28 +1,42 @@
|
||||
complete -c zoxide -n "__fish_use_subcommand" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "add" -d 'Add a new directory or increment its rank'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "edit" -d 'Edit the database'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "import" -d 'Import entries from another application'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "init" -d 'Generate shell configuration'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "query" -d 'Search for a directory in the database'
|
||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "remove" -d 'Remove a directory from the database'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from add" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from add" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from add" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from add" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and not __fish_seen_subcommand_from decrement; and not __fish_seen_subcommand_from delete; and not __fish_seen_subcommand_from increment; and not __fish_seen_subcommand_from reload" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and not __fish_seen_subcommand_from decrement; and not __fish_seen_subcommand_from delete; and not __fish_seen_subcommand_from increment; and not __fish_seen_subcommand_from reload" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and not __fish_seen_subcommand_from decrement; and not __fish_seen_subcommand_from delete; and not __fish_seen_subcommand_from increment; and not __fish_seen_subcommand_from reload" -f -a "decrement"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and not __fish_seen_subcommand_from decrement; and not __fish_seen_subcommand_from delete; and not __fish_seen_subcommand_from increment; and not __fish_seen_subcommand_from reload" -f -a "delete"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and not __fish_seen_subcommand_from decrement; and not __fish_seen_subcommand_from delete; and not __fish_seen_subcommand_from increment; and not __fish_seen_subcommand_from reload" -f -a "increment"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and not __fish_seen_subcommand_from decrement; and not __fish_seen_subcommand_from delete; and not __fish_seen_subcommand_from increment; and not __fish_seen_subcommand_from reload" -f -a "reload"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from decrement" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from decrement" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from delete" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from delete" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from increment" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from increment" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from reload" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from reload" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -l from -d 'Application to import from' -r -f -a "{autojump ,z }"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -l merge -d 'Merge into existing database'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -l cmd -d 'Renames the \'z\' command and corresponding aliases' -r
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -l hook -d 'Chooses event upon which an entry is added to the database' -r -f -a "{none ,prompt ,pwd }"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -l no-aliases -d 'Prevents zoxide from defining any commands'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -l exclude -d 'Exclude a path from results' -r -f -a "(__fish_complete_directories)"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -l all -d 'Show deleted directories'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -l cmd -d 'Changes the prefix of the `z` and `zi` commands' -r
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -l hook -d 'Changes how often zoxide increments a directory\'s score' -r -f -a "{none ,prompt ,pwd }"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -l no-cmd -d 'Prevents zoxide from defining the `z` and `zi` commands'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -l exclude -d 'Exclude the current directory' -r -f -a "(__fish_complete_directories)"
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s a -l all -d 'Show unavailable directories'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s i -l interactive -d 'Use interactive selection'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s l -l list -d 'List all matching directories'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s s -l score -d 'Print score with results'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from remove" -s i -l interactive -d 'Use interactive selection'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from remove" -s h -l help -d 'Print help information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from remove" -s V -l version -d 'Print version information'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s V -l version -d 'Print version'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from remove" -s h -l help -d 'Print help'
|
||||
complete -c zoxide -n "__fish_seen_subcommand_from remove" -s V -l version -d 'Print version'
|
||||
|
195
contrib/completions/zoxide.ts
generated
195
contrib/completions/zoxide.ts
generated
@ -8,11 +8,11 @@ const completion: Fig.Spec = {
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help information",
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version information",
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
@ -21,6 +21,87 @@ const completion: Fig.Spec = {
|
||||
template: "folders",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edit",
|
||||
description: "Edit the database",
|
||||
subcommands: [
|
||||
{
|
||||
name: "decrement",
|
||||
hidden: true,
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
name: "path",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete",
|
||||
hidden: true,
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
name: "path",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "increment",
|
||||
hidden: true,
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
name: "path",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reload",
|
||||
hidden: true,
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "import",
|
||||
description: "Import entries from another application",
|
||||
@ -28,16 +109,13 @@ const completion: Fig.Spec = {
|
||||
{
|
||||
name: "--from",
|
||||
description: "Application to import from",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "from",
|
||||
suggestions: [
|
||||
{
|
||||
name: "autojump",
|
||||
},
|
||||
{
|
||||
name: "z",
|
||||
},
|
||||
]
|
||||
"autojump",
|
||||
"z",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -46,11 +124,11 @@ const completion: Fig.Spec = {
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help information",
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version information",
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
@ -64,7 +142,8 @@ const completion: Fig.Spec = {
|
||||
options: [
|
||||
{
|
||||
name: "--cmd",
|
||||
description: "Renames the 'z' command and corresponding aliases",
|
||||
description: "Changes the prefix of the `z` and `zi` commands",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "cmd",
|
||||
isOptional: true,
|
||||
@ -72,64 +151,43 @@ const completion: Fig.Spec = {
|
||||
},
|
||||
{
|
||||
name: "--hook",
|
||||
description: "Chooses event upon which an entry is added to the database",
|
||||
description: "Changes how often zoxide increments a directory's score",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "hook",
|
||||
isOptional: true,
|
||||
suggestions: [
|
||||
{
|
||||
name: "none",
|
||||
},
|
||||
{
|
||||
name: "prompt",
|
||||
},
|
||||
{
|
||||
name: "pwd",
|
||||
},
|
||||
]
|
||||
"none",
|
||||
"prompt",
|
||||
"pwd",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--no-aliases",
|
||||
description: "Prevents zoxide from defining any commands",
|
||||
name: "--no-cmd",
|
||||
description: "Prevents zoxide from defining the `z` and `zi` commands",
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help information",
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version information",
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
name: "shell",
|
||||
suggestions: [
|
||||
{
|
||||
name: "bash",
|
||||
},
|
||||
{
|
||||
name: "elvish",
|
||||
},
|
||||
{
|
||||
name: "fish",
|
||||
},
|
||||
{
|
||||
name: "nushell",
|
||||
},
|
||||
{
|
||||
name: "posix",
|
||||
},
|
||||
{
|
||||
name: "powershell",
|
||||
},
|
||||
{
|
||||
name: "xonsh",
|
||||
},
|
||||
{
|
||||
name: "zsh",
|
||||
},
|
||||
]
|
||||
"bash",
|
||||
"elvish",
|
||||
"fish",
|
||||
"nushell",
|
||||
"posix",
|
||||
"powershell",
|
||||
"xonsh",
|
||||
"zsh",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -138,7 +196,8 @@ const completion: Fig.Spec = {
|
||||
options: [
|
||||
{
|
||||
name: "--exclude",
|
||||
description: "Exclude a path from results",
|
||||
description: "Exclude the current directory",
|
||||
isRepeatable: true,
|
||||
args: {
|
||||
name: "exclude",
|
||||
isOptional: true,
|
||||
@ -146,16 +205,24 @@ const completion: Fig.Spec = {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "--all",
|
||||
description: "Show deleted directories",
|
||||
name: ["-a", "--all"],
|
||||
description: "Show unavailable directories",
|
||||
},
|
||||
{
|
||||
name: ["-i", "--interactive"],
|
||||
description: "Use interactive selection",
|
||||
exclusiveOn: [
|
||||
"-l",
|
||||
"--list",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: ["-l", "--list"],
|
||||
description: "List all matching directories",
|
||||
exclusiveOn: [
|
||||
"-i",
|
||||
"--interactive",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: ["-s", "--score"],
|
||||
@ -163,15 +230,16 @@ const completion: Fig.Spec = {
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help information",
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version information",
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
name: "keywords",
|
||||
isVariadic: true,
|
||||
isOptional: true,
|
||||
},
|
||||
},
|
||||
@ -179,21 +247,18 @@ const completion: Fig.Spec = {
|
||||
name: "remove",
|
||||
description: "Remove a directory from the database",
|
||||
options: [
|
||||
{
|
||||
name: ["-i", "--interactive"],
|
||||
description: "Use interactive selection",
|
||||
},
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help information",
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version information",
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
args: {
|
||||
name: "paths",
|
||||
isVariadic: true,
|
||||
isOptional: true,
|
||||
template: "folders",
|
||||
},
|
||||
@ -202,11 +267,11 @@ const completion: Fig.Spec = {
|
||||
options: [
|
||||
{
|
||||
name: ["-h", "--help"],
|
||||
description: "Print help information",
|
||||
description: "Print help",
|
||||
},
|
||||
{
|
||||
name: ["-V", "--version"],
|
||||
description: "Print version information",
|
||||
description: "Print version",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
BIN
contrib/warp.png
Normal file
BIN
contrib/warp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 817 KiB |
391
install.sh
Executable file
391
install.sh
Executable file
@ -0,0 +1,391 @@
|
||||
#!/bin/sh
|
||||
# shellcheck shell=dash
|
||||
|
||||
# The official zoxide installer.
|
||||
#
|
||||
# It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local`
|
||||
# extension. Note: Most shells limit `local` to 1 var per line, contra bash.
|
||||
|
||||
main() {
|
||||
if [ "${KSH_VERSION-}" = 'Version JM 93t+ 2010-03-05' ]; then
|
||||
# The version of ksh93 that ships with many illumos systems does not
|
||||
# support the "local" extension. Print a message rather than fail in
|
||||
# subtle ways later on:
|
||||
err 'the installer does not work with this ksh93 version; please try bash'
|
||||
fi
|
||||
|
||||
set -u
|
||||
|
||||
# Detect and print host target triple.
|
||||
ensure get_architecture
|
||||
local _arch="${RETVAL}"
|
||||
assert_nz "${_arch}" "arch"
|
||||
echo "Detected architecture: ${_arch}"
|
||||
|
||||
# Create and enter a temporary directory.
|
||||
local _tmp_dir
|
||||
_tmp_dir="$(mktemp -d)" || err "mktemp: could not create temporary directory"
|
||||
cd "${_tmp_dir}" || err "cd: failed to enter directory: ${_tmp_dir}"
|
||||
|
||||
# Download and extract zoxide.
|
||||
ensure download_zoxide "${_arch}"
|
||||
local _package="${RETVAL}"
|
||||
assert_nz "${_package}" "package"
|
||||
echo "Downloaded package: ${_package}"
|
||||
case "${_package}" in
|
||||
*.tar.gz)
|
||||
need_cmd tar
|
||||
ensure tar -xf "${_package}"
|
||||
;;
|
||||
*.zip)
|
||||
need_cmd unzip
|
||||
ensure unzip -oq "${_package}"
|
||||
;;
|
||||
*)
|
||||
err "unsupported package format: ${_package}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Install binary.
|
||||
local _bin_dir="${HOME}/.local/bin"
|
||||
local _bin_name
|
||||
case "${_arch}" in
|
||||
*windows*) _bin_name="zoxide.exe" ;;
|
||||
*) _bin_name="zoxide" ;;
|
||||
esac
|
||||
ensure mkdir -p "${_bin_dir}"
|
||||
ensure cp "${_bin_name}" "${_bin_dir}"
|
||||
ensure chmod +x "${_bin_dir}/${_bin_name}"
|
||||
echo "Installed zoxide to ${_bin_dir}"
|
||||
|
||||
# Install manpages.
|
||||
local _man_dir="${HOME}/.local/share/man"
|
||||
ensure mkdir -p "${_man_dir}/man1"
|
||||
ensure cp "man/man1/"* "${_man_dir}/man1/"
|
||||
echo "Installed manpages to ${_man_dir}"
|
||||
|
||||
# Print success message and check $PATH.
|
||||
echo ""
|
||||
echo "zoxide is installed!"
|
||||
if ! echo ":${PATH}:" | grep -Fq ":${_bin_dir}:"; then
|
||||
echo "NOTE: ${_bin_dir} is not on your \$PATH. zoxide will not work unless it is added to \$PATH."
|
||||
fi
|
||||
}
|
||||
|
||||
download_zoxide() {
|
||||
local _arch="$1"
|
||||
|
||||
if check_cmd curl; then
|
||||
_dld=curl
|
||||
elif check_cmd wget; then
|
||||
_dld=wget
|
||||
else
|
||||
need_cmd 'curl or wget'
|
||||
fi
|
||||
need_cmd grep
|
||||
|
||||
local _releases_url="https://api.github.com/repos/ajeetdsouza/zoxide/releases/latest"
|
||||
local _releases
|
||||
case "${_dld}" in
|
||||
curl) _releases="$(curl -sL "${_releases_url}")" ||
|
||||
err "curl: failed to download ${_releases_url}" ;;
|
||||
wget) _releases="$(wget -qO- "${_releases_url}")" ||
|
||||
err "wget: failed to download ${_releases_url}" ;;
|
||||
*) err "unsupported downloader: ${_dld}" ;;
|
||||
esac
|
||||
(echo "${_releases}" | grep -q 'API rate limit exceeded') &&
|
||||
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"
|
||||
|
||||
local _package_url
|
||||
_package_url="$(echo "${_releases}" | grep "browser_download_url" | cut -d '"' -f 4 | grep "${_arch}")" ||
|
||||
err "zoxide has not yet been packaged for your architecture (${_arch}), please file an issue: https://github.com/ajeetdsouza/zoxide/issues"
|
||||
|
||||
local _ext
|
||||
case "${_package_url}" in
|
||||
*.tar.gz) _ext="tar.gz" ;;
|
||||
*.zip) _ext="zip" ;;
|
||||
*) err "unsupported package format: ${_package_url}" ;;
|
||||
esac
|
||||
|
||||
local _package="zoxide.${_ext}"
|
||||
case "${_dld}" in
|
||||
curl) _releases="$(curl -sLo "${_package}" "${_package_url}")" || err "curl: failed to download ${_package_url}" ;;
|
||||
wget) _releases="$(wget -qO "${_package}" "${_package_url}")" || err "wget: failed to download ${_package_url}" ;;
|
||||
*) err "unsupported downloader: ${_dld}" ;;
|
||||
esac
|
||||
|
||||
RETVAL="${_package}"
|
||||
}
|
||||
|
||||
# The below functions have been extracted with minor modifications from the
|
||||
# Rustup install script:
|
||||
#
|
||||
# https://github.com/rust-lang/rustup/blob/4c1289b2c3f3702783900934a38d7c5f912af787/rustup-init.sh
|
||||
|
||||
get_architecture() {
|
||||
local _ostype _cputype _bitness _arch _clibtype
|
||||
_ostype="$(uname -s)"
|
||||
_cputype="$(uname -m)"
|
||||
_clibtype="musl"
|
||||
|
||||
if [ "${_ostype}" = Linux ]; then
|
||||
if [ "$(uname -o || true)" = Android ]; then
|
||||
_ostype=Android
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${_ostype}" = Darwin ] && [ "${_cputype}" = i386 ]; then
|
||||
# Darwin `uname -m` lies
|
||||
if sysctl hw.optional.x86_64 | grep -q ': 1'; then
|
||||
_cputype=x86_64
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${_ostype}" = SunOS ]; then
|
||||
# Both Solaris and illumos presently announce as "SunOS" in "uname -s"
|
||||
# so use "uname -o" to disambiguate. We use the full path to the
|
||||
# system uname in case the user has coreutils uname first in PATH,
|
||||
# which has historically sometimes printed the wrong value here.
|
||||
if [ "$(/usr/bin/uname -o || true)" = illumos ]; then
|
||||
_ostype=illumos
|
||||
fi
|
||||
|
||||
# illumos systems have multi-arch userlands, and "uname -m" reports the
|
||||
# machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86
|
||||
# systems. Check for the native (widest) instruction set on the
|
||||
# running kernel:
|
||||
if [ "${_cputype}" = i86pc ]; then
|
||||
_cputype="$(isainfo -n)"
|
||||
fi
|
||||
fi
|
||||
|
||||
case "${_ostype}" in
|
||||
Android)
|
||||
_ostype=linux-android
|
||||
;;
|
||||
Linux)
|
||||
check_proc
|
||||
_ostype=unknown-linux-${_clibtype}
|
||||
_bitness=$(get_bitness)
|
||||
;;
|
||||
FreeBSD)
|
||||
_ostype=unknown-freebsd
|
||||
;;
|
||||
NetBSD)
|
||||
_ostype=unknown-netbsd
|
||||
;;
|
||||
DragonFly)
|
||||
_ostype=unknown-dragonfly
|
||||
;;
|
||||
Darwin)
|
||||
_ostype=apple-darwin
|
||||
;;
|
||||
illumos)
|
||||
_ostype=unknown-illumos
|
||||
;;
|
||||
MINGW* | MSYS* | CYGWIN* | Windows_NT)
|
||||
_ostype=pc-windows-msvc
|
||||
;;
|
||||
*)
|
||||
err "unrecognized OS type: ${_ostype}"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "${_cputype}" in
|
||||
i386 | i486 | i686 | i786 | x86)
|
||||
_cputype=i686
|
||||
;;
|
||||
xscale | arm)
|
||||
_cputype=arm
|
||||
if [ "${_ostype}" = "linux-android" ]; then
|
||||
_ostype=linux-androideabi
|
||||
fi
|
||||
;;
|
||||
armv6l)
|
||||
_cputype=arm
|
||||
if [ "${_ostype}" = "linux-android" ]; then
|
||||
_ostype=linux-androideabi
|
||||
else
|
||||
_ostype="${_ostype}eabihf"
|
||||
fi
|
||||
;;
|
||||
armv7l | armv8l)
|
||||
_cputype=armv7
|
||||
if [ "${_ostype}" = "linux-android" ]; then
|
||||
_ostype=linux-androideabi
|
||||
else
|
||||
_ostype="${_ostype}eabihf"
|
||||
fi
|
||||
;;
|
||||
aarch64 | arm64)
|
||||
_cputype=aarch64
|
||||
;;
|
||||
x86_64 | x86-64 | x64 | amd64)
|
||||
_cputype=x86_64
|
||||
;;
|
||||
mips)
|
||||
_cputype=$(get_endianness mips '' el)
|
||||
;;
|
||||
mips64)
|
||||
if [ "${_bitness}" -eq 64 ]; then
|
||||
# only n64 ABI is supported for now
|
||||
_ostype="${_ostype}abi64"
|
||||
_cputype=$(get_endianness mips64 '' el)
|
||||
fi
|
||||
;;
|
||||
ppc)
|
||||
_cputype=powerpc
|
||||
;;
|
||||
ppc64)
|
||||
_cputype=powerpc64
|
||||
;;
|
||||
ppc64le)
|
||||
_cputype=powerpc64le
|
||||
;;
|
||||
s390x)
|
||||
_cputype=s390x
|
||||
;;
|
||||
riscv64)
|
||||
_cputype=riscv64gc
|
||||
;;
|
||||
*)
|
||||
err "unknown CPU type: ${_cputype}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Detect 64-bit linux with 32-bit userland
|
||||
if [ "${_ostype}" = unknown-linux-musl ] && [ "${_bitness}" -eq 32 ]; then
|
||||
case ${_cputype} in
|
||||
x86_64)
|
||||
# 32-bit executable for amd64 = x32
|
||||
if is_host_amd64_elf; then {
|
||||
echo "x32 userland is unsupported" 1>&2
|
||||
exit 1
|
||||
}; else
|
||||
_cputype=i686
|
||||
fi
|
||||
;;
|
||||
mips64)
|
||||
_cputype=$(get_endianness mips '' el)
|
||||
;;
|
||||
powerpc64)
|
||||
_cputype=powerpc
|
||||
;;
|
||||
aarch64)
|
||||
_cputype=armv7
|
||||
if [ "${_ostype}" = "linux-android" ]; then
|
||||
_ostype=linux-androideabi
|
||||
else
|
||||
_ostype="${_ostype}eabihf"
|
||||
fi
|
||||
;;
|
||||
riscv64gc)
|
||||
err "riscv64 with 32-bit userland unsupported"
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Detect armv7 but without the CPU features Rust needs in that build,
|
||||
# and fall back to arm.
|
||||
# See https://github.com/rust-lang/rustup.rs/issues/587.
|
||||
if [ "${_ostype}" = "unknown-linux-musleabihf" ] && [ "${_cputype}" = armv7 ]; then
|
||||
if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then
|
||||
# At least one processor does not have NEON.
|
||||
_cputype=arm
|
||||
fi
|
||||
fi
|
||||
|
||||
_arch="${_cputype}-${_ostype}"
|
||||
RETVAL="${_arch}"
|
||||
}
|
||||
|
||||
get_bitness() {
|
||||
need_cmd head
|
||||
# Architecture detection without dependencies beyond coreutils.
|
||||
# ELF files start out "\x7fELF", and the following byte is
|
||||
# 0x01 for 32-bit and
|
||||
# 0x02 for 64-bit.
|
||||
# The printf builtin on some shells like dash only supports octal
|
||||
# escape sequences, so we use those.
|
||||
local _current_exe_head
|
||||
_current_exe_head=$(head -c 5 /proc/self/exe)
|
||||
if [ "${_current_exe_head}" = "$(printf '\177ELF\001')" ]; then
|
||||
echo 32
|
||||
elif [ "${_current_exe_head}" = "$(printf '\177ELF\002')" ]; then
|
||||
echo 64
|
||||
else
|
||||
err "unknown platform bitness"
|
||||
fi
|
||||
}
|
||||
|
||||
get_endianness() {
|
||||
local cputype=$1
|
||||
local suffix_eb=$2
|
||||
local suffix_el=$3
|
||||
|
||||
# detect endianness without od/hexdump, like get_bitness() does.
|
||||
need_cmd head
|
||||
need_cmd tail
|
||||
|
||||
local _current_exe_endianness
|
||||
_current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)"
|
||||
if [ "${_current_exe_endianness}" = "$(printf '\001')" ]; then
|
||||
echo "${cputype}${suffix_el}"
|
||||
elif [ "${_current_exe_endianness}" = "$(printf '\002')" ]; then
|
||||
echo "${cputype}${suffix_eb}"
|
||||
else
|
||||
err "unknown platform endianness"
|
||||
fi
|
||||
}
|
||||
|
||||
is_host_amd64_elf() {
|
||||
need_cmd head
|
||||
need_cmd tail
|
||||
# ELF e_machine detection without dependencies beyond coreutils.
|
||||
# Two-byte field at offset 0x12 indicates the CPU,
|
||||
# but we're interested in it being 0x3E to indicate amd64, or not that.
|
||||
local _current_exe_machine
|
||||
_current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1)
|
||||
[ "${_current_exe_machine}" = "$(printf '\076')" ]
|
||||
}
|
||||
|
||||
check_proc() {
|
||||
# Check for /proc by looking for the /proc/self/exe link.
|
||||
# This is only run on Linux.
|
||||
if ! test -L /proc/self/exe; then
|
||||
err "unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc."
|
||||
fi
|
||||
}
|
||||
|
||||
need_cmd() {
|
||||
if ! check_cmd "$1"; then
|
||||
err "need '$1' (command not found)"
|
||||
fi
|
||||
}
|
||||
|
||||
check_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Run a command that should never fail. If the command fails execution
|
||||
# will immediately terminate with an error showing the failing
|
||||
# command.
|
||||
ensure() {
|
||||
if ! "$@"; then err "command failed: $*"; fi
|
||||
}
|
||||
|
||||
assert_nz() {
|
||||
if [ -z "$1" ]; then err "found empty string: $2"; fi
|
||||
}
|
||||
|
||||
err() {
|
||||
echo "Error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# This is put in braces to ensure that the script does not run until it is
|
||||
# downloaded completely.
|
||||
{
|
||||
main "$@" || exit 1
|
||||
}
|
39
justfile
Normal file
39
justfile
Normal file
@ -0,0 +1,39 @@
|
||||
default:
|
||||
@just --list
|
||||
|
||||
[unix]
|
||||
fmt:
|
||||
nix-shell --cores 0 --pure --run 'cargo-fmt --all'
|
||||
nix-shell --cores 0 --pure --run 'nixfmt -- *.nix'
|
||||
nix-shell --cores 0 --pure --run 'shfmt --indent=4 --language-dialect=posix --simplify --write *.sh'
|
||||
nix-shell --cores 0 --pure --run 'yamlfmt -- .github/workflows/*.yml'
|
||||
|
||||
[windows]
|
||||
fmt:
|
||||
cargo +nightly fmt --all
|
||||
|
||||
[unix]
|
||||
lint:
|
||||
nix-shell --cores 0 --pure --run 'cargo-fmt --all --check'
|
||||
nix-shell --cores 0 --pure --run 'cargo clippy --all-features --all-targets -- -Dwarnings'
|
||||
nix-shell --cores 0 --pure --run 'cargo msrv verify'
|
||||
nix-shell --cores 0 --pure --run 'cargo udeps --all-features --all-targets --workspace'
|
||||
nix-shell --cores 0 --pure --run 'mandoc -man -Wall -Tlint -- man/man1/*.1'
|
||||
nix-shell --cores 0 --pure --run 'markdownlint *.md'
|
||||
nix-shell --cores 0 --pure --run 'nixfmt --check -- *.nix'
|
||||
nix-shell --cores 0 --pure --run 'shellcheck --enable all *.sh'
|
||||
nix-shell --cores 0 --pure --run 'shfmt --diff --indent=4 --language-dialect=posix --simplify *.sh'
|
||||
nix-shell --cores 0 --pure --run 'yamlfmt -lint -- .github/workflows/*.yml'
|
||||
|
||||
[windows]
|
||||
lint:
|
||||
cargo +nightly fmt --all --check
|
||||
cargo +stable clippy --all-features --all-targets -- -Dwarnings
|
||||
|
||||
[unix]
|
||||
test *args:
|
||||
nix-shell --cores 0 --pure --run 'cargo nextest run --all-features --no-fail-fast --workspace {{args}}'
|
||||
|
||||
[windows]
|
||||
test *args:
|
||||
cargo +stable test --no-fail-fast --workspace {{args}}
|
@ -19,6 +19,6 @@ Print help information.
|
||||
.SH REPORTING BUGS
|
||||
For any issues, feature requests, or questions, please visit:
|
||||
.sp
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||
.SH AUTHOR
|
||||
Ajeet D'Souza <\fB98ajeet@gmail.com\fR>
|
||||
Ajeet D'Souza \fB<98ajeet@gmail.com>\fR
|
||||
|
@ -11,7 +11,7 @@ The format of the database being imported:
|
||||
tab(|);
|
||||
l l.
|
||||
\fBautojump\fR
|
||||
\fBz\fR|(for \fBz\fR, \fBz.lua\fR, or \fBzsh-z\fR)
|
||||
\fBz\fR|(for \fBfasd\fR, \fBz\fR, \fBz.lua\fR, or \fBzsh-z\fR)
|
||||
.TE
|
||||
.sp
|
||||
Note: zoxide only imports paths from autojump, since its matching
|
||||
@ -26,6 +26,6 @@ option merges imported data into the existing database.
|
||||
.SH REPORTING BUGS
|
||||
For any issues, feature requests, or questions, please visit:
|
||||
.sp
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||
.SH AUTHOR
|
||||
Ajeet D'Souza <\fB98ajeet@gmail.com\fR>
|
||||
Ajeet D'Souza \fB<98ajeet@gmail.com>\fR
|
||||
|
@ -7,58 +7,63 @@
|
||||
To initialize zoxide on your shell:
|
||||
.TP
|
||||
.B bash
|
||||
Add this to your configuration (usually \fB~/.bashrc\fR):
|
||||
Add this to the \fBend\fR of your config file (usually \fB~/.bashrc\fR):
|
||||
.sp
|
||||
.nf
|
||||
\fBeval "$(zoxide init bash)"\fR
|
||||
.fi
|
||||
.TP
|
||||
.B elvish
|
||||
Add this to your configuration (usually \fB~/.elvish/rc.elv\fR):
|
||||
Add this to the \fBend\fR of your config file (usually \fB~/.elvish/rc.elv\fR):
|
||||
.sp
|
||||
.nf
|
||||
\fBeval $(zoxide init elvish | slurp)\fR
|
||||
.fi
|
||||
.sp
|
||||
Note: zoxide only supports elvish v0.16.0 and above.
|
||||
Note: zoxide only supports elvish v0.18.0 and above.
|
||||
.TP
|
||||
.B fish
|
||||
Add this to your configuration (usually \fB~/.config/fish/config.fish\fR):
|
||||
Add this to the \fBend\fR of your config file (usually
|
||||
\fB~/.config/fish/config.fish\fR):
|
||||
.sp
|
||||
.nf
|
||||
\fBzoxide init fish | source\fR
|
||||
.fi
|
||||
.TP
|
||||
.B nushell
|
||||
Add this to your configuration (find it by running \fBconfig path\fR in
|
||||
Nushell):
|
||||
Add this to the \fBend\fR of your env file (find it by running
|
||||
\fB$nu.env-path\fR in Nushell):
|
||||
.sp
|
||||
.nf
|
||||
\fBstartup = ["zoxide init nushell --hook prompt | save ~/.zoxide.nu", "source ~/.zoxide.nu"]\fR
|
||||
\fBzoxide init nushell | save -f ~/.zoxide.nu\fR
|
||||
.fi
|
||||
.sp
|
||||
Note: zoxide only supports Nushell v0.37.0 and above.
|
||||
.TP
|
||||
.B powershell
|
||||
Add this to your configuration (find it by running \fBecho $profile\fR in
|
||||
PowerShell):
|
||||
Now, add this to the \fBend\fR of your config file (find it by running
|
||||
\fB$nu.config-path\fR in Nushell):
|
||||
.sp
|
||||
.nf
|
||||
\fBInvoke-Expression (& {
|
||||
$hook = if ($PSVersionTable.PSVersion.Major -lt 6) { 'prompt' } else { 'pwd' }
|
||||
(zoxide init --hook $hook powershell | Out-String)
|
||||
})\fR
|
||||
\fBsource ~/.zoxide.nu\fR
|
||||
.fi
|
||||
.sp
|
||||
Note: zoxide only supports Nushell v0.73.0 and above.
|
||||
.TP
|
||||
.B powershell
|
||||
Add this to the \fBend\fR of your config file (find it by running \fBecho
|
||||
$profile\fR in PowerShell):
|
||||
.sp
|
||||
.nf
|
||||
\fBInvoke-Expression (& { (zoxide init powershell | Out-String) })\fR
|
||||
.fi
|
||||
.TP
|
||||
.B xonsh
|
||||
Add this to your configuration (usually \fB~/.xonshrc\fR):
|
||||
Add this to the \fBend\fR of your config file (usually \fB~/.xonshrc\fR):
|
||||
.sp
|
||||
.nf
|
||||
\fBexecx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')\fR
|
||||
.fi
|
||||
.TP
|
||||
.B zsh
|
||||
Add this to your configuration (usually \fB~/.zshrc\fR):
|
||||
Add this to the \fBend\fR of your config file (usually \fB~/.zshrc\fR):
|
||||
.sp
|
||||
.nf
|
||||
\fBeval "$(zoxide init zsh)"\fR
|
||||
@ -66,7 +71,7 @@ Add this to your configuration (usually \fB~/.zshrc\fR):
|
||||
.TP
|
||||
.B any POSIX shell
|
||||
.sp
|
||||
Add this to your configuration:
|
||||
Add this to the \fBend\fR of your config file:
|
||||
.sp
|
||||
.nf
|
||||
\fBeval "$(zoxide init posix --hook prompt)"\fR
|
||||
@ -74,9 +79,9 @@ Add this to your configuration:
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B --cmd
|
||||
Changes the prefix of predefined aliases (\fBz\fR, \fBzi\fR).
|
||||
Changes the prefix of the \fBz\fR and \fBzi\fR commands.
|
||||
.br
|
||||
\fB--cmd j\fR would change the aliases to (\fBj\fR, \fBji\fR).
|
||||
\fB--cmd j\fR would change the commands to (\fBj\fR, \fBji\fR).
|
||||
.br
|
||||
\fB--cmd cd\fR would replace the \fBcd\fR command (doesn't work on Nushell /
|
||||
POSIX shells).
|
||||
@ -94,13 +99,13 @@ l l.
|
||||
\fBpwd\fR|Whenever the directory is changed
|
||||
.TE
|
||||
.TP
|
||||
.B --no-aliases
|
||||
Don't define extra aliases (\fBz\fR, \fBzi\fR). These functions will still be
|
||||
available in your shell as \fB__zoxide_z\fR and \fB__zoxide_zi\fR, should you
|
||||
choose to redefine them.
|
||||
.B --no-cmd
|
||||
Prevents zoxide from defining the \fBz\fR and \fBzi\fR commands. These functions
|
||||
will still be available in your shell as \fB__zoxide_z\fR and \fB__zoxide_zi\fR,
|
||||
should you choose to redefine them.
|
||||
.SH REPORTING BUGS
|
||||
For any issues, feature requests, or questions, please visit:
|
||||
.sp
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||
.SH AUTHOR
|
||||
Ajeet D'Souza <\fB98ajeet@gmail.com\fR>
|
||||
Ajeet D'Souza \fB<98ajeet@gmail.com>\fR
|
||||
|
@ -28,6 +28,6 @@ Print the calculated score as well as the matched path.
|
||||
.SH REPORTING BUGS
|
||||
For any issues, feature requests, or questions, please visit:
|
||||
.sp
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||
.SH AUTHOR
|
||||
Ajeet D'Souza <\fB98ajeet@gmail.com\fR>
|
||||
Ajeet D'Souza \fB<98ajeet@gmail.com>\fR
|
||||
|
@ -10,12 +10,9 @@ If you'd like to permanently exclude a directory from the database, see the
|
||||
.TP
|
||||
.B -h, --help
|
||||
Print help information.
|
||||
.TP
|
||||
.B -i, --interactive [KEYWORDS]
|
||||
Use interactive selection. This option requires \fBfzf\fR(1).
|
||||
.SH REPORTING BUGS
|
||||
For any issues, feature requests, or questions, please visit:
|
||||
.sp
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||
.SH AUTHOR
|
||||
Ajeet D'Souza <\fB98ajeet@gmail.com\fR>
|
||||
Ajeet D'Souza \fB<98ajeet@gmail.com>\fR
|
||||
|
@ -129,6 +129,6 @@ l l.
|
||||
.SH REPORTING BUGS
|
||||
For any issues, feature requests, or questions, please visit:
|
||||
.sp
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||
.SH AUTHOR
|
||||
Ajeet D'Souza <\fB98ajeet@gmail.com\fR>
|
||||
Ajeet D'Souza \fB<98ajeet@gmail.com>\fR
|
||||
|
12
rustfmt.toml
12
rustfmt.toml
@ -1,12 +1,8 @@
|
||||
# comment_width = 100
|
||||
# error_on_line_overflow = true
|
||||
# error_on_unformatted = true
|
||||
# group_imports = "StdExternalCrate"
|
||||
# imports_granularity = "Module"
|
||||
max_width = 120
|
||||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Module"
|
||||
newline_style = "Native"
|
||||
use_field_init_shorthand = true
|
||||
use_small_heuristics = "Max"
|
||||
use_try_shorthand = true
|
||||
# wrap_comments = true
|
||||
# version = "Two"
|
||||
wrap_comments = true
|
||||
version = "Two"
|
||||
|
24
shell.nix
24
shell.nix
@ -1,13 +1,22 @@
|
||||
let
|
||||
rust = import (builtins.fetchTarball
|
||||
"https://github.com/oxalica/rust-overlay/archive/46d8d20fce510c6a25fa66f36e31f207f6ea49e4.tar.gz");
|
||||
pkgs = import (builtins.fetchTarball
|
||||
"https://github.com/NixOS/nixpkgs/archive/fae46e66a5df220327b45e0d7c27c6961cf922ce.tar.gz") {
|
||||
"https://github.com/NixOS/nixpkgs/archive/22a6958f46fd8e14830d02856ff63b1d0e5cc3e4.tar.gz") {
|
||||
overlays = [ rust ];
|
||||
};
|
||||
rust = import (builtins.fetchTarball
|
||||
"https://github.com/oxalica/rust-overlay/archive/a61fcd9910229d097ffef92b5a2440065e3b64d5.tar.gz");
|
||||
|
||||
rust-nightly =
|
||||
pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal);
|
||||
cargo-udeps = pkgs.writeShellScriptBin "cargo-udeps" ''
|
||||
export RUSTC="${rust-nightly}/bin/rustc";
|
||||
export CARGO="${rust-nightly}/bin/cargo";
|
||||
exec "${pkgs.cargo-udeps}/bin/cargo-udeps" "$@"
|
||||
'';
|
||||
in pkgs.mkShell {
|
||||
buildInputs = [
|
||||
# Rust
|
||||
(pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.rustfmt))
|
||||
pkgs.rust-bin.stable.latest.default
|
||||
|
||||
# Shells
|
||||
@ -21,7 +30,11 @@ in pkgs.mkShell {
|
||||
pkgs.zsh
|
||||
|
||||
# Tools
|
||||
pkgs.cargo-audit
|
||||
cargo-udeps
|
||||
pkgs.cargo-msrv
|
||||
pkgs.cargo-nextest
|
||||
pkgs.cargo-udeps
|
||||
pkgs.just
|
||||
pkgs.mandoc
|
||||
pkgs.nixfmt
|
||||
pkgs.nodePackages.markdownlint-cli
|
||||
@ -30,6 +43,7 @@ in pkgs.mkShell {
|
||||
pkgs.python3Packages.pylint
|
||||
pkgs.shellcheck
|
||||
pkgs.shfmt
|
||||
pkgs.yamlfmt
|
||||
|
||||
# Dependencies
|
||||
pkgs.cacert
|
||||
@ -38,5 +52,5 @@ in pkgs.mkShell {
|
||||
pkgs.libiconv
|
||||
];
|
||||
|
||||
RUST_BACKTRACE = 1;
|
||||
CARGO_TARGET_DIR = "target_nix";
|
||||
}
|
||||
|
131
src/cmd/_cmd.rs
131
src/cmd/_cmd.rs
@ -1,131 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{ArgEnum, Parser, ValueHint};
|
||||
|
||||
const ENV_HELP: &str = "ENVIRONMENT VARIABLES:
|
||||
_ZO_DATA_DIR Path for zoxide data files
|
||||
_ZO_ECHO Print the matched directory before navigating to it when set to 1
|
||||
_ZO_EXCLUDE_DIRS List of directory globs to be excluded
|
||||
_ZO_FZF_OPTS Custom flags to pass to fzf
|
||||
_ZO_MAXAGE Maximum total age after which entries start getting deleted
|
||||
_ZO_RESOLVE_SYMLINKS Resolve symlinks when storing paths";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
bin_name = env!("CARGO_PKG_NAME"),
|
||||
about,
|
||||
author,
|
||||
after_help = ENV_HELP,
|
||||
disable_help_subcommand = true,
|
||||
propagate_version = true,
|
||||
version = option_env!("ZOXIDE_VERSION").unwrap_or_default()
|
||||
)]
|
||||
pub enum Cmd {
|
||||
Add(Add),
|
||||
Import(Import),
|
||||
Init(Init),
|
||||
Query(Query),
|
||||
Remove(Remove),
|
||||
}
|
||||
|
||||
/// Add a new directory or increment its rank
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Add {
|
||||
#[clap(min_values = 1, required = true, value_hint = ValueHint::DirPath)]
|
||||
pub paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
/// Import entries from another application
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Import {
|
||||
#[clap(value_hint = ValueHint::FilePath)]
|
||||
pub path: PathBuf,
|
||||
|
||||
/// Application to import from
|
||||
#[clap(arg_enum, long)]
|
||||
pub from: ImportFrom,
|
||||
|
||||
/// Merge into existing database
|
||||
#[clap(long)]
|
||||
pub merge: bool,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Debug)]
|
||||
pub enum ImportFrom {
|
||||
Autojump,
|
||||
Z,
|
||||
}
|
||||
|
||||
/// Generate shell configuration
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Init {
|
||||
#[clap(arg_enum)]
|
||||
pub shell: InitShell,
|
||||
|
||||
/// Prevents zoxide from defining any commands
|
||||
#[clap(long)]
|
||||
pub no_aliases: bool,
|
||||
|
||||
/// Renames the 'z' command and corresponding aliases
|
||||
#[clap(long, default_value = "z")]
|
||||
pub cmd: String,
|
||||
|
||||
/// Chooses event upon which an entry is added to the database
|
||||
#[clap(arg_enum, long, default_value = "pwd")]
|
||||
pub hook: InitHook,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum InitHook {
|
||||
None,
|
||||
Prompt,
|
||||
Pwd,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Clone, Debug)]
|
||||
pub enum InitShell {
|
||||
Bash,
|
||||
Elvish,
|
||||
Fish,
|
||||
Nushell,
|
||||
Posix,
|
||||
Powershell,
|
||||
Xonsh,
|
||||
Zsh,
|
||||
}
|
||||
|
||||
/// Search for a directory in the database
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Query {
|
||||
pub keywords: Vec<String>,
|
||||
|
||||
/// Show deleted directories
|
||||
#[clap(long)]
|
||||
pub all: bool,
|
||||
|
||||
/// Use interactive selection
|
||||
#[clap(long, short, conflicts_with = "list")]
|
||||
pub interactive: bool,
|
||||
|
||||
/// List all matching directories
|
||||
#[clap(long, short, conflicts_with = "interactive")]
|
||||
pub list: bool,
|
||||
|
||||
/// Print score with results
|
||||
#[clap(long, short, conflicts_with = "interactive")]
|
||||
pub score: bool,
|
||||
|
||||
/// Exclude a path from results
|
||||
#[clap(long, value_hint = ValueHint::DirPath, value_name = "path")]
|
||||
pub exclude: Option<String>,
|
||||
}
|
||||
|
||||
/// Remove a directory from the database
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Remove {
|
||||
/// Use interactive selection
|
||||
#[clap(long, short)]
|
||||
pub interactive: bool,
|
||||
#[clap(value_hint = ValueHint::DirPath)]
|
||||
pub paths: Vec<String>,
|
||||
}
|
@ -3,42 +3,42 @@ use std::path::Path;
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use crate::cmd::{Add, Run};
|
||||
use crate::db::DatabaseFile;
|
||||
use crate::db::Database;
|
||||
use crate::{config, util};
|
||||
|
||||
impl Run for Add {
|
||||
fn run(&self) -> Result<()> {
|
||||
// These characters can't be printed cleanly to a single line, so they can cause confusion
|
||||
// when writing to fzf / stdout.
|
||||
// These characters can't be printed cleanly to a single line, so they can cause
|
||||
// confusion when writing to stdout.
|
||||
const EXCLUDE_CHARS: &[char] = &['\n', '\r'];
|
||||
|
||||
let data_dir = config::data_dir()?;
|
||||
let exclude_dirs = config::exclude_dirs()?;
|
||||
let max_age = config::maxage()?;
|
||||
let now = util::current_time()?;
|
||||
|
||||
let mut db = DatabaseFile::new(data_dir);
|
||||
let mut db = db.open()?;
|
||||
let mut db = Database::open()?;
|
||||
|
||||
for path in &self.paths {
|
||||
let path = if config::resolve_symlinks() { util::canonicalize } else { util::resolve_path }(path)?;
|
||||
let path =
|
||||
if config::resolve_symlinks() { util::canonicalize } else { util::resolve_path }(
|
||||
path,
|
||||
)?;
|
||||
let path = util::path_to_str(&path)?;
|
||||
|
||||
// Ignore path if it contains unsupported characters, or if it's in the exclude list.
|
||||
// Ignore path if it contains unsupported characters, or if it's in the exclude
|
||||
// list.
|
||||
if path.contains(EXCLUDE_CHARS) || exclude_dirs.iter().any(|glob| glob.matches(path)) {
|
||||
continue;
|
||||
}
|
||||
if !Path::new(path).is_dir() {
|
||||
bail!("not a directory: {}", path);
|
||||
bail!("not a directory: {path}");
|
||||
}
|
||||
db.add(path, now);
|
||||
db.add_update(path, 1.0, now);
|
||||
}
|
||||
|
||||
if db.modified {
|
||||
if db.dirty() {
|
||||
db.age(max_age);
|
||||
db.save()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
db.save()
|
||||
}
|
||||
}
|
||||
|
186
src/cmd/cmd.rs
Normal file
186
src/cmd/cmd.rs
Normal file
@ -0,0 +1,186 @@
|
||||
#![allow(clippy::module_inception)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{Parser, Subcommand, ValueEnum, ValueHint};
|
||||
|
||||
const HELP_TEMPLATE: &str = color_print::cstr!("\
|
||||
{before-help}<bold><underline>{name} {version}</underline></bold>
|
||||
{author}
|
||||
https://github.com/ajeetdsouza/zoxide
|
||||
|
||||
{about}
|
||||
|
||||
{usage-heading}
|
||||
{tab}{usage}
|
||||
|
||||
{all-args}{after-help}
|
||||
|
||||
<bold><underline>Environment variables:</underline></bold>
|
||||
{tab}<bold>_ZO_DATA_DIR</bold> {tab}Path for zoxide data files
|
||||
{tab}<bold>_ZO_ECHO</bold> {tab}Print the matched directory before navigating to it when set to 1
|
||||
{tab}<bold>_ZO_EXCLUDE_DIRS</bold> {tab}List of directory globs to be excluded
|
||||
{tab}<bold>_ZO_FZF_OPTS</bold> {tab}Custom flags to pass to fzf
|
||||
{tab}<bold>_ZO_MAXAGE</bold> {tab}Maximum total age after which entries start getting deleted
|
||||
{tab}<bold>_ZO_RESOLVE_SYMLINKS</bold>{tab}Resolve symlinks when storing paths");
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
about,
|
||||
author,
|
||||
help_template = HELP_TEMPLATE,
|
||||
disable_help_subcommand = true,
|
||||
propagate_version = true,
|
||||
version,
|
||||
)]
|
||||
pub enum Cmd {
|
||||
Add(Add),
|
||||
Edit(Edit),
|
||||
Import(Import),
|
||||
Init(Init),
|
||||
Query(Query),
|
||||
Remove(Remove),
|
||||
}
|
||||
|
||||
/// Add a new directory or increment its rank
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
author,
|
||||
help_template = HELP_TEMPLATE,
|
||||
)]
|
||||
pub struct Add {
|
||||
#[clap(num_args = 1.., required = true, value_hint = ValueHint::DirPath)]
|
||||
pub paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
/// Edit the database
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
author,
|
||||
help_template = HELP_TEMPLATE,
|
||||
)]
|
||||
pub struct Edit {
|
||||
#[clap(subcommand)]
|
||||
pub cmd: Option<EditCommand>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Subcommand)]
|
||||
pub enum EditCommand {
|
||||
#[clap(hide = true)]
|
||||
Decrement { path: String },
|
||||
#[clap(hide = true)]
|
||||
Delete { path: String },
|
||||
#[clap(hide = true)]
|
||||
Increment { path: String },
|
||||
#[clap(hide = true)]
|
||||
Reload,
|
||||
}
|
||||
|
||||
/// Import entries from another application
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
author,
|
||||
help_template = HELP_TEMPLATE,
|
||||
)]
|
||||
pub struct Import {
|
||||
#[clap(value_hint = ValueHint::FilePath)]
|
||||
pub path: PathBuf,
|
||||
|
||||
/// Application to import from
|
||||
#[clap(value_enum, long)]
|
||||
pub from: ImportFrom,
|
||||
|
||||
/// Merge into existing database
|
||||
#[clap(long)]
|
||||
pub merge: bool,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Clone, Debug)]
|
||||
pub enum ImportFrom {
|
||||
Autojump,
|
||||
#[clap(alias = "fasd")]
|
||||
Z,
|
||||
}
|
||||
|
||||
/// Generate shell configuration
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
author,
|
||||
help_template = HELP_TEMPLATE,
|
||||
)]
|
||||
pub struct Init {
|
||||
#[clap(value_enum)]
|
||||
pub shell: InitShell,
|
||||
|
||||
/// Prevents zoxide from defining the `z` and `zi` commands
|
||||
#[clap(long, alias = "no-aliases")]
|
||||
pub no_cmd: bool,
|
||||
|
||||
/// Changes the prefix of the `z` and `zi` commands
|
||||
#[clap(long, default_value = "z")]
|
||||
pub cmd: String,
|
||||
|
||||
/// Changes how often zoxide increments a directory's score
|
||||
#[clap(value_enum, long, default_value = "pwd")]
|
||||
pub hook: InitHook,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum InitHook {
|
||||
None,
|
||||
Prompt,
|
||||
Pwd,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Clone, Debug)]
|
||||
pub enum InitShell {
|
||||
Bash,
|
||||
Elvish,
|
||||
Fish,
|
||||
Nushell,
|
||||
Posix,
|
||||
Powershell,
|
||||
Xonsh,
|
||||
Zsh,
|
||||
}
|
||||
|
||||
/// Search for a directory in the database
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
author,
|
||||
help_template = HELP_TEMPLATE,
|
||||
)]
|
||||
pub struct Query {
|
||||
pub keywords: Vec<String>,
|
||||
|
||||
/// Show unavailable directories
|
||||
#[clap(long, short)]
|
||||
pub all: bool,
|
||||
|
||||
/// Use interactive selection
|
||||
#[clap(long, short, conflicts_with = "list")]
|
||||
pub interactive: bool,
|
||||
|
||||
/// List all matching directories
|
||||
#[clap(long, short, conflicts_with = "interactive")]
|
||||
pub list: bool,
|
||||
|
||||
/// Print score with results
|
||||
#[clap(long, short)]
|
||||
pub score: bool,
|
||||
|
||||
/// Exclude the current directory
|
||||
#[clap(long, value_hint = ValueHint::DirPath, value_name = "path")]
|
||||
pub exclude: Option<String>,
|
||||
}
|
||||
|
||||
/// Remove a directory from the database
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
author,
|
||||
help_template = HELP_TEMPLATE,
|
||||
)]
|
||||
pub struct Remove {
|
||||
#[clap(value_hint = ValueHint::DirPath)]
|
||||
pub paths: Vec<String>,
|
||||
}
|
84
src/cmd/edit.rs
Normal file
84
src/cmd/edit.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::cmd::{Edit, EditCommand, Run};
|
||||
use crate::db::Database;
|
||||
use crate::error::BrokenPipeHandler;
|
||||
use crate::util::{self, Fzf, FzfChild};
|
||||
|
||||
impl Run for Edit {
|
||||
fn run(&self) -> Result<()> {
|
||||
let now = util::current_time()?;
|
||||
let db = &mut Database::open()?;
|
||||
|
||||
match &self.cmd {
|
||||
Some(cmd) => {
|
||||
match cmd {
|
||||
EditCommand::Decrement { path } => db.add(path, -1.0, now),
|
||||
EditCommand::Delete { path } => {
|
||||
db.remove(path);
|
||||
}
|
||||
EditCommand::Increment { path } => db.add(path, 1.0, now),
|
||||
EditCommand::Reload => {}
|
||||
}
|
||||
db.save()?;
|
||||
|
||||
let stdout = &mut io::stdout().lock();
|
||||
for dir in db.dirs().iter().rev() {
|
||||
write!(stdout, "{}\0", dir.display().with_score(now).with_separator('\t'))
|
||||
.pipe_exit("fzf")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
db.sort_by_score(now);
|
||||
db.save()?;
|
||||
Self::get_fzf()?.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edit {
|
||||
fn get_fzf() -> Result<FzfChild> {
|
||||
Fzf::new()?
|
||||
.args([
|
||||
// Search mode
|
||||
"--exact",
|
||||
// Search result
|
||||
"--no-sort",
|
||||
// Interface
|
||||
"--bind=\
|
||||
btab:up,\
|
||||
ctrl-r:reload(zoxide edit reload),\
|
||||
ctrl-d:reload(zoxide edit delete {2..}),\
|
||||
ctrl-w:reload(zoxide edit increment {2..}),\
|
||||
ctrl-s:reload(zoxide edit decrement {2..}),\
|
||||
ctrl-z:ignore,\
|
||||
double-click:ignore,\
|
||||
enter:abort,\
|
||||
start:reload(zoxide edit reload),\
|
||||
tab:down",
|
||||
"--cycle",
|
||||
"--keep-right",
|
||||
// Layout
|
||||
"--border=sharp",
|
||||
"--border-label= zoxide-edit ",
|
||||
"--header=\
|
||||
ctrl-r:reload \tctrl-d:delete
|
||||
ctrl-w:increment\tctrl-s:decrement
|
||||
|
||||
SCORE\tPATH",
|
||||
"--info=inline",
|
||||
"--layout=reverse",
|
||||
"--padding=1,0,0,0",
|
||||
// Display
|
||||
"--color=label:bold",
|
||||
"--tabstop=1",
|
||||
])
|
||||
.enable_preview()
|
||||
.spawn()
|
||||
}
|
||||
}
|
@ -3,24 +3,22 @@ use std::fs;
|
||||
use anyhow::{bail, Context, Result};
|
||||
|
||||
use crate::cmd::{Import, ImportFrom, Run};
|
||||
use crate::config;
|
||||
use crate::db::{Database, DatabaseFile, Dir};
|
||||
use crate::db::Database;
|
||||
|
||||
impl Run for Import {
|
||||
fn run(&self) -> Result<()> {
|
||||
let buffer = fs::read_to_string(&self.path)
|
||||
.with_context(|| format!("could not open database for importing: {}", &self.path.display()))?;
|
||||
let buffer = fs::read_to_string(&self.path).with_context(|| {
|
||||
format!("could not open database for importing: {}", &self.path.display())
|
||||
})?;
|
||||
|
||||
let data_dir = config::data_dir()?;
|
||||
let mut db = DatabaseFile::new(data_dir);
|
||||
let db = &mut db.open()?;
|
||||
if !self.merge && !db.dirs.is_empty() {
|
||||
let mut db = Database::open()?;
|
||||
if !self.merge && !db.dirs().is_empty() {
|
||||
bail!("current database is not empty, specify --merge to continue anyway");
|
||||
}
|
||||
|
||||
match self.from {
|
||||
ImportFrom::Autojump => from_autojump(db, &buffer),
|
||||
ImportFrom::Z => from_z(db, &buffer),
|
||||
ImportFrom::Autojump => import_autojump(&mut db, &buffer),
|
||||
ImportFrom::Z => import_z(&mut db, &buffer),
|
||||
}
|
||||
.context("import error")?;
|
||||
|
||||
@ -28,55 +26,51 @@ impl Run for Import {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_autojump<'a>(db: &mut Database<'a>, buffer: &'a str) -> Result<()> {
|
||||
fn import_autojump(db: &mut Database, buffer: &str) -> Result<()> {
|
||||
for line in buffer.lines() {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut split = line.splitn(2, '\t');
|
||||
let (rank, path) =
|
||||
line.split_once('\t').with_context(|| format!("invalid entry: {line}"))?;
|
||||
|
||||
let rank = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
||||
let mut rank = rank.parse::<f64>().with_context(|| format!("invalid rank: {}", rank))?;
|
||||
// Normalize the rank using a sigmoid function. Don't import actual ranks from autojump,
|
||||
// since its scoring algorithm is very different and might take a while to get normalized.
|
||||
let mut rank = rank.parse::<f64>().with_context(|| format!("invalid rank: {rank}"))?;
|
||||
// Normalize the rank using a sigmoid function. Don't import actual ranks from
|
||||
// autojump, since its scoring algorithm is very different and might
|
||||
// take a while to get normalized.
|
||||
rank = sigmoid(rank);
|
||||
|
||||
let path = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
||||
|
||||
db.dirs.push(Dir { path: path.into(), rank, last_accessed: 0 });
|
||||
db.modified = true;
|
||||
db.add_unchecked(path, rank, 0);
|
||||
}
|
||||
|
||||
if db.modified {
|
||||
if db.dirty() {
|
||||
db.dedup();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn from_z<'a>(db: &mut Database<'a>, buffer: &'a str) -> Result<()> {
|
||||
fn import_z(db: &mut Database, buffer: &str) -> Result<()> {
|
||||
for line in buffer.lines() {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut split = line.rsplitn(3, '|');
|
||||
|
||||
let last_accessed = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
||||
let last_accessed = last_accessed.parse().with_context(|| format!("invalid epoch: {}", last_accessed))?;
|
||||
let last_accessed = split.next().with_context(|| format!("invalid entry: {line}"))?;
|
||||
let last_accessed =
|
||||
last_accessed.parse().with_context(|| format!("invalid epoch: {last_accessed}"))?;
|
||||
|
||||
let rank = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
||||
let rank = rank.parse().with_context(|| format!("invalid rank: {}", rank))?;
|
||||
let rank = split.next().with_context(|| format!("invalid entry: {line}"))?;
|
||||
let rank = rank.parse().with_context(|| format!("invalid rank: {rank}"))?;
|
||||
|
||||
let path = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
||||
let path = split.next().with_context(|| format!("invalid entry: {line}"))?;
|
||||
|
||||
db.dirs.push(Dir { path: path.into(), rank, last_accessed });
|
||||
db.modified = true;
|
||||
db.add_unchecked(path, rank, last_accessed);
|
||||
}
|
||||
|
||||
if db.modified {
|
||||
if db.dirty() {
|
||||
db.dedup();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -86,33 +80,33 @@ fn sigmoid(x: f64) -> f64 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::sigmoid;
|
||||
use crate::db::{Database, Dir};
|
||||
use super::*;
|
||||
use crate::db::Dir;
|
||||
|
||||
#[test]
|
||||
fn from_autojump() {
|
||||
let buffer = r#"
|
||||
let data_dir = tempfile::tempdir().unwrap();
|
||||
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||
for (path, rank, last_accessed) in [
|
||||
("/quux/quuz", 1.0, 100),
|
||||
("/corge/grault/garply", 6.0, 600),
|
||||
("/waldo/fred/plugh", 3.0, 300),
|
||||
("/xyzzy/thud", 8.0, 800),
|
||||
("/foo/bar", 9.0, 900),
|
||||
] {
|
||||
db.add_unchecked(path, rank, last_accessed);
|
||||
}
|
||||
|
||||
let buffer = "\
|
||||
7.0 /baz
|
||||
2.0 /foo/bar
|
||||
5.0 /quux/quuz
|
||||
"#;
|
||||
5.0 /quux/quuz";
|
||||
import_autojump(&mut db, buffer).unwrap();
|
||||
|
||||
let dirs = vec![
|
||||
Dir { path: "/quux/quuz".into(), rank: 1.0, last_accessed: 100 },
|
||||
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
||||
Dir { path: "/foo/bar".into(), rank: 9.0, last_accessed: 900 },
|
||||
];
|
||||
let data_dir = tempfile::tempdir().unwrap();
|
||||
let data_dir = &data_dir.path().to_path_buf();
|
||||
let mut db = Database { dirs: dirs.into(), modified: false, data_dir };
|
||||
db.sort_by_path();
|
||||
println!("got: {:?}", &db.dirs());
|
||||
|
||||
super::from_autojump(&mut db, buffer).unwrap();
|
||||
db.dirs.sort_by(|dir1, dir2| dir1.path.cmp(&dir2.path));
|
||||
println!("got: {:?}", &db.dirs.as_slice());
|
||||
|
||||
let exp = &[
|
||||
let exp = [
|
||||
Dir { path: "/baz".into(), rank: sigmoid(7.0), last_accessed: 0 },
|
||||
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||
Dir { path: "/foo/bar".into(), rank: 9.0 + sigmoid(2.0), last_accessed: 900 },
|
||||
@ -120,9 +114,9 @@ mod tests {
|
||||
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
||||
];
|
||||
println!("exp: {:?}", &exp);
|
||||
println!("exp: {exp:?}");
|
||||
|
||||
for (dir1, dir2) in db.dirs.iter().zip(exp) {
|
||||
for (dir1, dir2) in db.dirs().iter().zip(exp) {
|
||||
assert_eq!(dir1.path, dir2.path);
|
||||
assert!((dir1.rank - dir2.rank).abs() < 0.01);
|
||||
assert_eq!(dir1.last_accessed, dir2.last_accessed);
|
||||
@ -131,29 +125,29 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn from_z() {
|
||||
let buffer = r#"
|
||||
let data_dir = tempfile::tempdir().unwrap();
|
||||
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||
for (path, rank, last_accessed) in [
|
||||
("/quux/quuz", 1.0, 100),
|
||||
("/corge/grault/garply", 6.0, 600),
|
||||
("/waldo/fred/plugh", 3.0, 300),
|
||||
("/xyzzy/thud", 8.0, 800),
|
||||
("/foo/bar", 9.0, 900),
|
||||
] {
|
||||
db.add_unchecked(path, rank, last_accessed);
|
||||
}
|
||||
|
||||
let buffer = "\
|
||||
/baz|7|700
|
||||
/quux/quuz|4|400
|
||||
/foo/bar|2|200
|
||||
/quux/quuz|5|500
|
||||
"#;
|
||||
/quux/quuz|5|500";
|
||||
import_z(&mut db, buffer).unwrap();
|
||||
|
||||
let dirs = vec![
|
||||
Dir { path: "/quux/quuz".into(), rank: 1.0, last_accessed: 100 },
|
||||
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
||||
Dir { path: "/foo/bar".into(), rank: 9.0, last_accessed: 900 },
|
||||
];
|
||||
let data_dir = tempfile::tempdir().unwrap();
|
||||
let data_dir = &data_dir.path().to_path_buf();
|
||||
let mut db = Database { dirs: dirs.into(), modified: false, data_dir };
|
||||
db.sort_by_path();
|
||||
println!("got: {:?}", &db.dirs());
|
||||
|
||||
super::from_z(&mut db, buffer).unwrap();
|
||||
db.dirs.sort_by(|dir1, dir2| dir1.path.cmp(&dir2.path));
|
||||
println!("got: {:?}", &db.dirs.as_slice());
|
||||
|
||||
let exp = &[
|
||||
let exp = [
|
||||
Dir { path: "/baz".into(), rank: 7.0, last_accessed: 700 },
|
||||
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||
Dir { path: "/foo/bar".into(), rank: 11.0, last_accessed: 900 },
|
||||
@ -161,9 +155,9 @@ mod tests {
|
||||
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
||||
];
|
||||
println!("exp: {:?}", &exp);
|
||||
println!("exp: {exp:?}");
|
||||
|
||||
for (dir1, dir2) in db.dirs.iter().zip(exp) {
|
||||
for (dir1, dir2) in db.dirs().iter().zip(exp) {
|
||||
assert_eq!(dir1.path, dir2.path);
|
||||
assert!((dir1.rank - dir2.rank).abs() < 0.01);
|
||||
assert_eq!(dir1.last_accessed, dir2.last_accessed);
|
||||
|
@ -6,28 +6,26 @@ use askama::Template;
|
||||
use crate::cmd::{Init, InitShell, Run};
|
||||
use crate::config;
|
||||
use crate::error::BrokenPipeHandler;
|
||||
use crate::shell::{self, Opts};
|
||||
use crate::shell::{Bash, Elvish, Fish, Nushell, Opts, Posix, Powershell, Xonsh, Zsh};
|
||||
|
||||
impl Run for Init {
|
||||
fn run(&self) -> Result<()> {
|
||||
let cmd = if self.no_aliases { None } else { Some(self.cmd.as_str()) };
|
||||
|
||||
let cmd = if self.no_cmd { None } else { Some(self.cmd.as_str()) };
|
||||
let echo = config::echo();
|
||||
let resolve_symlinks = config::resolve_symlinks();
|
||||
|
||||
let opts = &Opts { cmd, hook: self.hook, echo, resolve_symlinks };
|
||||
|
||||
let source = match self.shell {
|
||||
InitShell::Bash => shell::Bash(opts).render(),
|
||||
InitShell::Elvish => shell::Elvish(opts).render(),
|
||||
InitShell::Fish => shell::Fish(opts).render(),
|
||||
InitShell::Nushell => shell::Nushell(opts).render(),
|
||||
InitShell::Posix => shell::Posix(opts).render(),
|
||||
InitShell::Powershell => shell::Powershell(opts).render(),
|
||||
InitShell::Xonsh => shell::Xonsh(opts).render(),
|
||||
InitShell::Zsh => shell::Zsh(opts).render(),
|
||||
InitShell::Bash => Bash(opts).render(),
|
||||
InitShell::Elvish => Elvish(opts).render(),
|
||||
InitShell::Fish => Fish(opts).render(),
|
||||
InitShell::Nushell => Nushell(opts).render(),
|
||||
InitShell::Posix => Posix(opts).render(),
|
||||
InitShell::Powershell => Powershell(opts).render(),
|
||||
InitShell::Xonsh => Xonsh(opts).render(),
|
||||
InitShell::Zsh => Zsh(opts).render(),
|
||||
}
|
||||
.context("could not render template")?;
|
||||
writeln!(io::stdout(), "{}", source).pipe_exit("stdout")
|
||||
writeln!(io::stdout(), "{source}").pipe_exit("stdout")
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod _cmd;
|
||||
mod add;
|
||||
mod cmd;
|
||||
mod edit;
|
||||
mod import;
|
||||
mod init;
|
||||
mod query;
|
||||
@ -7,7 +8,7 @@ mod remove;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
pub use crate::cmd::_cmd::*;
|
||||
pub use crate::cmd::cmd::*;
|
||||
|
||||
pub trait Run {
|
||||
fn run(&self) -> Result<()>;
|
||||
@ -17,6 +18,7 @@ impl Run for Cmd {
|
||||
fn run(&self) -> Result<()> {
|
||||
match self {
|
||||
Cmd::Add(cmd) => cmd.run(),
|
||||
Cmd::Edit(cmd) => cmd.run(),
|
||||
Cmd::Import(cmd) => cmd.run(),
|
||||
Cmd::Init(cmd) => cmd.run(),
|
||||
Cmd::Query(cmd) => cmd.run(),
|
||||
|
124
src/cmd/query.rs
124
src/cmd/query.rs
@ -1,18 +1,16 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{bail, Context, Result};
|
||||
|
||||
use crate::cmd::{Query, Run};
|
||||
use crate::config;
|
||||
use crate::db::{Database, DatabaseFile};
|
||||
use crate::db::{Database, Epoch, Stream};
|
||||
use crate::error::BrokenPipeHandler;
|
||||
use crate::util::{self, Fzf};
|
||||
use crate::util::{self, Fzf, FzfChild};
|
||||
|
||||
impl Run for Query {
|
||||
fn run(&self) -> Result<()> {
|
||||
let data_dir = config::data_dir()?;
|
||||
let mut db = DatabaseFile::new(data_dir);
|
||||
let mut db = db.open()?;
|
||||
let mut db = crate::db::Database::open()?;
|
||||
self.query(&mut db).and(db.save())
|
||||
}
|
||||
}
|
||||
@ -20,7 +18,50 @@ impl Run for Query {
|
||||
impl Query {
|
||||
fn query(&self, db: &mut Database) -> Result<()> {
|
||||
let now = util::current_time()?;
|
||||
let mut stream = self.get_stream(db, now);
|
||||
|
||||
if self.interactive {
|
||||
let mut fzf = Self::get_fzf()?;
|
||||
let selection = loop {
|
||||
match stream.next() {
|
||||
Some(dir) => {
|
||||
if let Some(selection) = fzf.write(dir, now)? {
|
||||
break selection;
|
||||
}
|
||||
}
|
||||
None => break fzf.wait()?,
|
||||
}
|
||||
};
|
||||
|
||||
if self.score {
|
||||
print!("{selection}");
|
||||
} else {
|
||||
let path = selection.get(7..).context("could not read selection from fzf")?;
|
||||
print!("{path}");
|
||||
}
|
||||
} else if self.list {
|
||||
let handle = &mut io::stdout().lock();
|
||||
while let Some(dir) = stream.next() {
|
||||
let dir = if self.score { dir.display().with_score(now) } else { dir.display() };
|
||||
writeln!(handle, "{dir}").pipe_exit("stdout")?;
|
||||
}
|
||||
} else {
|
||||
let handle = &mut io::stdout();
|
||||
let Some(dir) = stream.next() else {
|
||||
bail!(if stream.did_exclude() {
|
||||
"you are already in the only match"
|
||||
} else {
|
||||
"no match found"
|
||||
});
|
||||
};
|
||||
let dir = if self.score { dir.display().with_score(now) } else { dir.display() };
|
||||
writeln!(handle, "{dir}").pipe_exit("stdout")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_stream<'a>(&self, db: &'a mut Database, now: Epoch) -> Stream<'a> {
|
||||
let mut stream = db.stream(now).with_keywords(&self.keywords);
|
||||
if !self.all {
|
||||
let resolve_symlinks = config::resolve_symlinks();
|
||||
@ -29,51 +70,36 @@ impl Query {
|
||||
if let Some(path) = &self.exclude {
|
||||
stream = stream.with_exclude(path);
|
||||
}
|
||||
stream
|
||||
}
|
||||
|
||||
if self.interactive {
|
||||
let mut fzf = Fzf::new(false)?;
|
||||
let stdin = fzf.stdin();
|
||||
|
||||
let selection = loop {
|
||||
let dir = match stream.next() {
|
||||
Some(dir) => dir,
|
||||
None => break fzf.select()?,
|
||||
};
|
||||
|
||||
match writeln!(stdin, "{}", dir.display_score(now)) {
|
||||
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break fzf.select()?,
|
||||
result => result.context("could not write to fzf")?,
|
||||
}
|
||||
};
|
||||
|
||||
if self.score {
|
||||
print!("{}", selection);
|
||||
} else {
|
||||
let path = selection.get(5..).context("could not read selection from fzf")?;
|
||||
print!("{}", path);
|
||||
}
|
||||
} else if self.list {
|
||||
let stdout = io::stdout();
|
||||
let handle = &mut stdout.lock();
|
||||
while let Some(dir) = stream.next() {
|
||||
if self.score {
|
||||
writeln!(handle, "{}", dir.display_score(now))
|
||||
} else {
|
||||
writeln!(handle, "{}", dir.display())
|
||||
}
|
||||
.pipe_exit("stdout")?;
|
||||
}
|
||||
handle.flush().pipe_exit("stdout")?;
|
||||
fn get_fzf() -> Result<FzfChild> {
|
||||
let mut fzf = Fzf::new()?;
|
||||
if let Some(fzf_opts) = config::fzf_opts() {
|
||||
fzf.env("FZF_DEFAULT_OPTS", fzf_opts)
|
||||
} else {
|
||||
let dir = stream.next().context("no match found")?;
|
||||
if self.score {
|
||||
writeln!(io::stdout(), "{}", dir.display_score(now))
|
||||
} else {
|
||||
writeln!(io::stdout(), "{}", dir.display())
|
||||
}
|
||||
.pipe_exit("stdout")?;
|
||||
fzf.args([
|
||||
// Search mode
|
||||
"--exact",
|
||||
// Search result
|
||||
"--no-sort",
|
||||
// Interface
|
||||
"--bind=ctrl-z:ignore,btab:up,tab:down",
|
||||
"--cycle",
|
||||
"--keep-right",
|
||||
// Layout
|
||||
"--border=sharp", // rounded edges don't display correctly on some terminals
|
||||
"--height=45%",
|
||||
"--info=inline",
|
||||
"--layout=reverse",
|
||||
// Display
|
||||
"--tabstop=1",
|
||||
// Scripting
|
||||
"--exit-0",
|
||||
"--select-1",
|
||||
])
|
||||
.enable_preview()
|
||||
}
|
||||
|
||||
Ok(())
|
||||
.spawn()
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,19 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use crate::cmd::{Remove, Run};
|
||||
use crate::config;
|
||||
use crate::db::DatabaseFile;
|
||||
use crate::util::{self, Fzf};
|
||||
use crate::db::Database;
|
||||
use crate::util;
|
||||
|
||||
impl Run for Remove {
|
||||
fn run(&self) -> Result<()> {
|
||||
let data_dir = config::data_dir()?;
|
||||
let mut db = DatabaseFile::new(data_dir);
|
||||
let mut db = db.open()?;
|
||||
let mut db = Database::open()?;
|
||||
|
||||
if self.interactive {
|
||||
let keywords = &self.paths;
|
||||
let now = util::current_time()?;
|
||||
let mut stream = db.stream(now).with_keywords(keywords);
|
||||
|
||||
let mut fzf = Fzf::new(true)?;
|
||||
let stdin = fzf.stdin();
|
||||
|
||||
let selection = loop {
|
||||
let dir = match stream.next() {
|
||||
Some(dir) => dir,
|
||||
None => break fzf.select()?,
|
||||
};
|
||||
|
||||
match writeln!(stdin, "{}", dir.display_score(now)) {
|
||||
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break fzf.select()?,
|
||||
result => result.context("could not write to fzf")?,
|
||||
}
|
||||
};
|
||||
|
||||
let paths = selection.lines().filter_map(|line| line.get(5..));
|
||||
for path in paths {
|
||||
if !db.remove(path) {
|
||||
bail!("path not found in database: {}", path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for path in &self.paths {
|
||||
if !db.remove(path) {
|
||||
let path_abs = util::resolve_path(path)?;
|
||||
let path_abs = util::path_to_str(&path_abs)?;
|
||||
if path_abs != path && !db.remove(path_abs) {
|
||||
bail!("path not found in database: {} ({})", path, path_abs)
|
||||
}
|
||||
for path in &self.paths {
|
||||
if !db.remove(path) {
|
||||
let path_abs = util::resolve_path(path)?;
|
||||
let path_abs = util::path_to_str(&path_abs)?;
|
||||
if path_abs == path || !db.remove(path_abs) {
|
||||
bail!("path not found in database: {path}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result};
|
||||
use glob::Pattern;
|
||||
|
||||
use crate::db::Rank;
|
||||
@ -10,45 +10,35 @@ use crate::db::Rank;
|
||||
pub fn data_dir() -> Result<PathBuf> {
|
||||
let path = match env::var_os("_ZO_DATA_DIR") {
|
||||
Some(path) => PathBuf::from(path),
|
||||
None => match dirs::data_local_dir() {
|
||||
Some(mut path) => {
|
||||
path.push("zoxide");
|
||||
path
|
||||
}
|
||||
None => bail!("could not find data directory, please set _ZO_DATA_DIR manually"),
|
||||
},
|
||||
None => dirs::data_local_dir()
|
||||
.context("could not find data directory, please set _ZO_DATA_DIR manually")?
|
||||
.join("zoxide"),
|
||||
};
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn echo() -> bool {
|
||||
match env::var_os("_ZO_ECHO") {
|
||||
Some(var) => var == "1",
|
||||
None => false,
|
||||
}
|
||||
env::var_os("_ZO_ECHO").map_or(false, |var| var == "1")
|
||||
}
|
||||
|
||||
pub fn exclude_dirs() -> Result<Vec<Pattern>> {
|
||||
env::var_os("_ZO_EXCLUDE_DIRS").map_or_else(
|
||||
|| {
|
||||
match env::var_os("_ZO_EXCLUDE_DIRS") {
|
||||
Some(paths) => env::split_paths(&paths)
|
||||
.map(|path| {
|
||||
let pattern = path.to_str().context("invalid unicode in _ZO_EXCLUDE_DIRS")?;
|
||||
Pattern::new(pattern)
|
||||
.with_context(|| format!("invalid glob in _ZO_EXCLUDE_DIRS: {pattern}"))
|
||||
})
|
||||
.collect(),
|
||||
None => {
|
||||
let pattern = (|| {
|
||||
let home = dirs::home_dir()?;
|
||||
let home = home.to_str()?;
|
||||
let home = Pattern::escape(home);
|
||||
let home = Pattern::escape(home.to_str()?);
|
||||
Pattern::new(&home).ok()
|
||||
})();
|
||||
Ok(pattern.into_iter().collect())
|
||||
},
|
||||
|paths| {
|
||||
env::split_paths(&paths)
|
||||
.map(|path| {
|
||||
let pattern = path.to_str().context("invalid unicode in _ZO_EXCLUDE_DIRS")?;
|
||||
Pattern::new(pattern).with_context(|| format!("invalid glob in _ZO_EXCLUDE_DIRS: {}", pattern))
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fzf_opts() -> Option<OsString> {
|
||||
@ -56,20 +46,15 @@ pub fn fzf_opts() -> Option<OsString> {
|
||||
}
|
||||
|
||||
pub fn maxage() -> Result<Rank> {
|
||||
match env::var_os("_ZO_MAXAGE") {
|
||||
Some(maxage) => {
|
||||
let maxage = maxage.to_str().context("invalid unicode in _ZO_MAXAGE")?;
|
||||
let maxage =
|
||||
maxage.parse::<u64>().with_context(|| format!("unable to parse _ZO_MAXAGE as integer: {}", maxage))?;
|
||||
Ok(maxage as Rank)
|
||||
}
|
||||
None => Ok(10000.0),
|
||||
}
|
||||
env::var_os("_ZO_MAXAGE").map_or(Ok(10_000.0), |maxage| {
|
||||
let maxage = maxage.to_str().context("invalid unicode in _ZO_MAXAGE")?;
|
||||
let maxage = maxage
|
||||
.parse::<u32>()
|
||||
.with_context(|| format!("unable to parse _ZO_MAXAGE as integer: {maxage}"))?;
|
||||
Ok(maxage as Rank)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn resolve_symlinks() -> bool {
|
||||
match env::var_os("_ZO_RESOLVE_SYMLINKS") {
|
||||
Some(var) => var == "1",
|
||||
None => false,
|
||||
}
|
||||
env::var_os("_ZO_RESOLVE_SYMLINKS").map_or(false, |var| var == "1")
|
||||
}
|
||||
|
152
src/db/dir.rs
152
src/db/dir.rs
@ -1,83 +1,9 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use bincode::Options as _;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct DirList<'a>(#[serde(borrow)] pub Vec<Dir<'a>>);
|
||||
|
||||
impl DirList<'_> {
|
||||
const VERSION: u32 = 3;
|
||||
|
||||
pub fn new() -> DirList<'static> {
|
||||
DirList(Vec::new())
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<DirList> {
|
||||
// Assume a maximum size for the database. This prevents bincode from throwing strange
|
||||
// errors when it encounters invalid data.
|
||||
const MAX_SIZE: u64 = 32 << 20; // 32 MiB
|
||||
let deserializer = &mut bincode::options().with_fixint_encoding().with_limit(MAX_SIZE);
|
||||
|
||||
// Split bytes into sections.
|
||||
let version_size = deserializer.serialized_size(&Self::VERSION).unwrap() as _;
|
||||
if bytes.len() < version_size {
|
||||
bail!("could not deserialize database: corrupted data");
|
||||
}
|
||||
let (bytes_version, bytes_dirs) = bytes.split_at(version_size);
|
||||
|
||||
// Deserialize sections.
|
||||
(|| {
|
||||
let version = deserializer.deserialize(bytes_version)?;
|
||||
match version {
|
||||
Self::VERSION => Ok(deserializer.deserialize(bytes_dirs)?),
|
||||
version => {
|
||||
bail!("unsupported version (got {}, supports {})", version, Self::VERSION,)
|
||||
}
|
||||
}
|
||||
})()
|
||||
.context("could not deserialize database")
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>> {
|
||||
(|| -> bincode::Result<_> {
|
||||
// Preallocate buffer with combined size of sections.
|
||||
let version_size = bincode::serialized_size(&Self::VERSION)?;
|
||||
let dirs_size = bincode::serialized_size(&self)?;
|
||||
let buffer_size = version_size + dirs_size;
|
||||
let mut buffer = Vec::with_capacity(buffer_size as _);
|
||||
|
||||
// Serialize sections into buffer.
|
||||
bincode::serialize_into(&mut buffer, &Self::VERSION)?;
|
||||
bincode::serialize_into(&mut buffer, &self)?;
|
||||
Ok(buffer)
|
||||
})()
|
||||
.context("could not serialize database")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for DirList<'a> {
|
||||
type Target = Vec<Dir<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for DirList<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Vec<Dir<'a>>> for DirList<'a> {
|
||||
fn from(dirs: Vec<Dir<'a>>) -> Self {
|
||||
DirList(dirs)
|
||||
}
|
||||
}
|
||||
use crate::util::{DAY, HOUR, WEEK};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Dir<'a> {
|
||||
@ -88,11 +14,11 @@ pub struct Dir<'a> {
|
||||
}
|
||||
|
||||
impl Dir<'_> {
|
||||
pub fn score(&self, now: Epoch) -> Rank {
|
||||
const HOUR: Epoch = 60 * 60;
|
||||
const DAY: Epoch = 24 * HOUR;
|
||||
const WEEK: Epoch = 7 * DAY;
|
||||
pub fn display(&self) -> DirDisplay<'_> {
|
||||
DirDisplay::new(self)
|
||||
}
|
||||
|
||||
pub fn score(&self, now: Epoch) -> Rank {
|
||||
// The older the entry, the lesser its importance.
|
||||
let duration = now.saturating_sub(self.last_accessed);
|
||||
if duration < HOUR {
|
||||
@ -105,63 +31,39 @@ impl Dir<'_> {
|
||||
self.rank * 0.25
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&self) -> DirDisplay {
|
||||
DirDisplay { dir: self }
|
||||
}
|
||||
|
||||
pub fn display_score(&self, now: Epoch) -> DirDisplayScore {
|
||||
DirDisplayScore { dir: self, now }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DirDisplay<'a> {
|
||||
dir: &'a Dir<'a>,
|
||||
now: Option<Epoch>,
|
||||
separator: char,
|
||||
}
|
||||
|
||||
impl<'a> DirDisplay<'a> {
|
||||
fn new(dir: &'a Dir) -> Self {
|
||||
Self { dir, separator: ' ', now: None }
|
||||
}
|
||||
|
||||
pub fn with_score(mut self, now: Epoch) -> Self {
|
||||
self.now = Some(now);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_separator(mut self, separator: char) -> Self {
|
||||
self.separator = separator;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DirDisplay<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if let Some(now) = self.now {
|
||||
let score = self.dir.score(now).clamp(0.0, 9999.0);
|
||||
write!(f, "{score:>6.1}{}", self.separator)?;
|
||||
}
|
||||
write!(f, "{}", self.dir.path)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DirDisplayScore<'a> {
|
||||
dir: &'a Dir<'a>,
|
||||
now: Epoch,
|
||||
}
|
||||
|
||||
impl Display for DirDisplayScore<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let score = self.dir.score(self.now);
|
||||
let score = if score > 9999.0 {
|
||||
9999
|
||||
} else if score > 0.0 {
|
||||
score as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
write!(f, "{:>4} {}", score, self.dir.path)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Rank = f64;
|
||||
pub type Epoch = u64;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::{Dir, DirList};
|
||||
|
||||
#[test]
|
||||
fn zero_copy() {
|
||||
let dirs = DirList(vec![Dir { path: "/".into(), rank: 0.0, last_accessed: 0 }]);
|
||||
|
||||
let bytes = dirs.to_bytes().unwrap();
|
||||
let dirs = DirList::from_bytes(&bytes).unwrap();
|
||||
|
||||
for dir in dirs.iter() {
|
||||
assert!(matches!(dir.path, Cow::Borrowed(_)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
358
src/db/mod.rs
358
src/db/mod.rs
@ -1,152 +1,237 @@
|
||||
mod dir;
|
||||
mod stream;
|
||||
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{fs, io};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
pub use dir::{Dir, DirList, Epoch, Rank};
|
||||
pub use stream::Stream;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use bincode::Options;
|
||||
use ouroboros::self_referencing;
|
||||
|
||||
use crate::util;
|
||||
pub use crate::db::dir::{Dir, Epoch, Rank};
|
||||
pub use crate::db::stream::Stream;
|
||||
use crate::{config, util};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Database<'file> {
|
||||
pub dirs: DirList<'file>,
|
||||
pub modified: bool,
|
||||
pub data_dir: &'file Path,
|
||||
#[self_referencing]
|
||||
pub struct Database {
|
||||
path: PathBuf,
|
||||
bytes: Vec<u8>,
|
||||
#[borrows(bytes)]
|
||||
#[covariant]
|
||||
pub dirs: Vec<Dir<'this>>,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl<'file> Database<'file> {
|
||||
impl Database {
|
||||
const VERSION: u32 = 3;
|
||||
|
||||
pub fn open() -> Result<Self> {
|
||||
let data_dir = config::data_dir()?;
|
||||
Self::open_dir(data_dir)
|
||||
}
|
||||
|
||||
pub fn open_dir(data_dir: impl AsRef<Path>) -> Result<Self> {
|
||||
let data_dir = data_dir.as_ref();
|
||||
let path = data_dir.join("db.zo");
|
||||
|
||||
match fs::read(&path) {
|
||||
Ok(bytes) => Self::try_new(path, bytes, |bytes| Self::deserialize(bytes), false),
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => {
|
||||
// Create data directory, but don't create any file yet. The file will be
|
||||
// created later by [`Database::save`] if any data is modified.
|
||||
fs::create_dir_all(data_dir).with_context(|| {
|
||||
format!("unable to create data directory: {}", data_dir.display())
|
||||
})?;
|
||||
Ok(Self::new(path, Vec::new(), |_| Vec::new(), false))
|
||||
}
|
||||
Err(e) => {
|
||||
Err(e).with_context(|| format!("could not read from database: {}", path.display()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&mut self) -> Result<()> {
|
||||
if !self.modified {
|
||||
// Only write to disk if the database is modified.
|
||||
if !self.dirty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let buffer = self.dirs.to_bytes()?;
|
||||
let path = db_path(&self.data_dir);
|
||||
util::write(&path, &buffer).context("could not write to database")?;
|
||||
self.modified = false;
|
||||
let bytes = Self::serialize(self.dirs())?;
|
||||
util::write(self.borrow_path(), bytes).context("could not write to database")?;
|
||||
self.with_dirty_mut(|dirty| *dirty = false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a new directory or increments its rank. Also updates its last accessed time.
|
||||
pub fn add<S: AsRef<str>>(&mut self, path: S, now: Epoch) {
|
||||
let path = path.as_ref();
|
||||
|
||||
match self.dirs.iter_mut().find(|dir| dir.path == path) {
|
||||
/// Increments the rank of a directory, or creates it if it does not exist.
|
||||
pub fn add(&mut self, path: impl AsRef<str> + Into<String>, by: Rank, now: Epoch) {
|
||||
self.with_dirs_mut(|dirs| match dirs.iter_mut().find(|dir| dir.path == path.as_ref()) {
|
||||
Some(dir) => dir.rank = (dir.rank + by).max(0.0),
|
||||
None => {
|
||||
self.dirs.push(Dir { path: path.to_string().into(), last_accessed: now, rank: 1.0 });
|
||||
dirs.push(Dir { path: path.into().into(), rank: by.max(0.0), last_accessed: now })
|
||||
}
|
||||
Some(dir) => {
|
||||
dir.last_accessed = now;
|
||||
dir.rank += 1.0;
|
||||
}
|
||||
};
|
||||
});
|
||||
self.with_dirty_mut(|dirty| *dirty = true);
|
||||
}
|
||||
|
||||
self.modified = true;
|
||||
/// Creates a new directory. This will create a duplicate entry if this
|
||||
/// directory is always in the database, it is expected that the user either
|
||||
/// does a check before calling this, or calls `dedup()` afterward.
|
||||
pub fn add_unchecked(&mut self, path: impl AsRef<str> + Into<String>, rank: Rank, now: Epoch) {
|
||||
self.with_dirs_mut(|dirs| {
|
||||
dirs.push(Dir { path: path.into().into(), rank, last_accessed: now })
|
||||
});
|
||||
self.with_dirty_mut(|dirty| *dirty = true);
|
||||
}
|
||||
|
||||
/// Increments the rank and updates the last_accessed of a directory, or
|
||||
/// creates it if it does not exist.
|
||||
pub fn add_update(&mut self, path: impl AsRef<str> + Into<String>, by: Rank, now: Epoch) {
|
||||
self.with_dirs_mut(|dirs| match dirs.iter_mut().find(|dir| dir.path == path.as_ref()) {
|
||||
Some(dir) => {
|
||||
dir.rank = (dir.rank + by).max(0.0);
|
||||
dir.last_accessed = now;
|
||||
}
|
||||
None => {
|
||||
dirs.push(Dir { path: path.into().into(), rank: by.max(0.0), last_accessed: now })
|
||||
}
|
||||
});
|
||||
self.with_dirty_mut(|dirty| *dirty = true);
|
||||
}
|
||||
|
||||
/// Removes the directory with `path` from the store. This does not preserve
|
||||
/// ordering, but is O(1).
|
||||
pub fn remove(&mut self, path: impl AsRef<str>) -> bool {
|
||||
match self.dirs().iter().position(|dir| dir.path == path.as_ref()) {
|
||||
Some(idx) => {
|
||||
self.swap_remove(idx);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_remove(&mut self, idx: usize) {
|
||||
self.with_dirs_mut(|dirs| dirs.swap_remove(idx));
|
||||
self.with_dirty_mut(|dirty| *dirty = true);
|
||||
}
|
||||
|
||||
pub fn age(&mut self, max_age: Rank) {
|
||||
let mut dirty = false;
|
||||
self.with_dirs_mut(|dirs| {
|
||||
let total_age = dirs.iter().map(|dir| dir.rank).sum::<Rank>();
|
||||
if total_age > max_age {
|
||||
let factor = 0.9 * max_age / total_age;
|
||||
for idx in (0..dirs.len()).rev() {
|
||||
let dir = &mut dirs[idx];
|
||||
dir.rank *= factor;
|
||||
if dir.rank < 1.0 {
|
||||
dirs.swap_remove(idx);
|
||||
}
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
});
|
||||
self.with_dirty_mut(|dirty_prev| *dirty_prev |= dirty);
|
||||
}
|
||||
|
||||
pub fn stream(&mut self, now: Epoch) -> Stream {
|
||||
Stream::new(self, now)
|
||||
}
|
||||
|
||||
pub fn dedup(&mut self) {
|
||||
// Sort by path, so that equal paths are next to each other.
|
||||
self.dirs.sort_by(|dir1, dir2| dir1.path.cmp(&dir2.path));
|
||||
self.sort_by_path();
|
||||
|
||||
for idx in (1..self.dirs.len()).rev() {
|
||||
// Check if curr_dir and next_dir have equal paths.
|
||||
let curr_dir = &self.dirs[idx];
|
||||
let next_dir = &self.dirs[idx - 1];
|
||||
if next_dir.path != curr_dir.path {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Merge curr_dir's rank and last_accessed into next_dir.
|
||||
let rank = curr_dir.rank;
|
||||
let last_accessed = curr_dir.last_accessed;
|
||||
let next_dir = &mut self.dirs[idx - 1];
|
||||
next_dir.last_accessed = next_dir.last_accessed.max(last_accessed);
|
||||
next_dir.rank += rank;
|
||||
|
||||
// Delete curr_dir.
|
||||
self.dirs.swap_remove(idx);
|
||||
self.modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Streaming iterator for directories.
|
||||
pub fn stream(&mut self, now: Epoch) -> Stream<'_, 'file> {
|
||||
Stream::new(self, now)
|
||||
}
|
||||
|
||||
/// Removes the directory with `path` from the store. This does not preserve ordering, but is
|
||||
/// O(1).
|
||||
pub fn remove<S: AsRef<str>>(&mut self, path: S) -> bool {
|
||||
let path = path.as_ref();
|
||||
|
||||
if let Some(idx) = self.dirs.iter().position(|dir| dir.path == path) {
|
||||
self.dirs.swap_remove(idx);
|
||||
self.modified = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn age(&mut self, max_age: Rank) {
|
||||
let sum_age = self.dirs.iter().map(|dir| dir.rank).sum::<Rank>();
|
||||
|
||||
if sum_age > max_age {
|
||||
let factor = 0.9 * max_age / sum_age;
|
||||
|
||||
for idx in (0..self.dirs.len()).rev() {
|
||||
let dir = &mut self.dirs[idx];
|
||||
dir.rank *= factor;
|
||||
if dir.rank < 1.0 {
|
||||
self.dirs.swap_remove(idx);
|
||||
let mut dirty = false;
|
||||
self.with_dirs_mut(|dirs| {
|
||||
for idx in (1..dirs.len()).rev() {
|
||||
// Check if curr_dir and next_dir have equal paths.
|
||||
let curr_dir = &dirs[idx];
|
||||
let next_dir = &dirs[idx - 1];
|
||||
if next_dir.path != curr_dir.path {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
self.modified = true;
|
||||
// Merge curr_dir's rank and last_accessed into next_dir.
|
||||
let rank = curr_dir.rank;
|
||||
let last_accessed = curr_dir.last_accessed;
|
||||
let next_dir = &mut dirs[idx - 1];
|
||||
next_dir.last_accessed = next_dir.last_accessed.max(last_accessed);
|
||||
next_dir.rank += rank;
|
||||
|
||||
// Delete curr_dir.
|
||||
dirs.swap_remove(idx);
|
||||
dirty = true;
|
||||
}
|
||||
});
|
||||
self.with_dirty_mut(|dirty_prev| *dirty_prev |= dirty);
|
||||
}
|
||||
|
||||
pub fn sort_by_path(&mut self) {
|
||||
self.with_dirs_mut(|dirs| dirs.sort_unstable_by(|dir1, dir2| dir1.path.cmp(&dir2.path)));
|
||||
self.with_dirty_mut(|dirty| *dirty = true);
|
||||
}
|
||||
|
||||
pub fn sort_by_score(&mut self, now: Epoch) {
|
||||
self.with_dirs_mut(|dirs| {
|
||||
dirs.sort_unstable_by(|dir1: &Dir, dir2: &Dir| {
|
||||
dir1.score(now).total_cmp(&dir2.score(now))
|
||||
})
|
||||
});
|
||||
self.with_dirty_mut(|dirty| *dirty = true);
|
||||
}
|
||||
|
||||
pub fn dirty(&self) -> bool {
|
||||
*self.borrow_dirty()
|
||||
}
|
||||
|
||||
pub fn dirs(&self) -> &[Dir] {
|
||||
self.borrow_dirs()
|
||||
}
|
||||
|
||||
fn serialize(dirs: &[Dir<'_>]) -> Result<Vec<u8>> {
|
||||
(|| -> bincode::Result<_> {
|
||||
// Preallocate buffer with combined size of sections.
|
||||
let buffer_size =
|
||||
bincode::serialized_size(&Self::VERSION)? + bincode::serialized_size(&dirs)?;
|
||||
let mut buffer = Vec::with_capacity(buffer_size as usize);
|
||||
|
||||
// Serialize sections into buffer.
|
||||
bincode::serialize_into(&mut buffer, &Self::VERSION)?;
|
||||
bincode::serialize_into(&mut buffer, &dirs)?;
|
||||
|
||||
Ok(buffer)
|
||||
})()
|
||||
.context("could not serialize database")
|
||||
}
|
||||
|
||||
fn deserialize(bytes: &[u8]) -> Result<Vec<Dir>> {
|
||||
// Assume a maximum size for the database. This prevents bincode from throwing
|
||||
// strange errors when it encounters invalid data.
|
||||
const MAX_SIZE: u64 = 32 << 20; // 32 MiB
|
||||
let deserializer = &mut bincode::options().with_fixint_encoding().with_limit(MAX_SIZE);
|
||||
|
||||
// Split bytes into sections.
|
||||
let version_size = deserializer.serialized_size(&Self::VERSION).unwrap() as _;
|
||||
if bytes.len() < version_size {
|
||||
bail!("could not deserialize database: corrupted data");
|
||||
}
|
||||
}
|
||||
}
|
||||
let (bytes_version, bytes_dirs) = bytes.split_at(version_size);
|
||||
|
||||
pub struct DatabaseFile {
|
||||
buffer: Vec<u8>,
|
||||
data_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl DatabaseFile {
|
||||
pub fn new<P: Into<PathBuf>>(data_dir: P) -> Self {
|
||||
DatabaseFile { buffer: Vec::new(), data_dir: data_dir.into() }
|
||||
}
|
||||
|
||||
pub fn open(&mut self) -> Result<Database> {
|
||||
// Read the entire database to memory. For smaller files, this is faster than
|
||||
// mmap / streaming, and allows for zero-copy deserialization.
|
||||
let path = db_path(&self.data_dir);
|
||||
match fs::read(&path) {
|
||||
Ok(buffer) => {
|
||||
self.buffer = buffer;
|
||||
let dirs = DirList::from_bytes(&self.buffer)
|
||||
.with_context(|| format!("could not deserialize database: {}", path.display()))?;
|
||||
Ok(Database { dirs, modified: false, data_dir: &self.data_dir })
|
||||
// Deserialize sections.
|
||||
let version = deserializer.deserialize(bytes_version)?;
|
||||
let dirs = match version {
|
||||
Self::VERSION => {
|
||||
deserializer.deserialize(bytes_dirs).context("could not deserialize database")?
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => {
|
||||
// Create data directory, but don't create any file yet. The file will be created
|
||||
// later by [`Database::save`] if any data is modified.
|
||||
fs::create_dir_all(&self.data_dir)
|
||||
.with_context(|| format!("unable to create data directory: {}", self.data_dir.display()))?;
|
||||
Ok(Database { dirs: DirList::new(), modified: false, data_dir: &self.data_dir })
|
||||
version => {
|
||||
bail!("unsupported version (got {version}, supports {})", Self::VERSION)
|
||||
}
|
||||
Err(e) => Err(e).with_context(|| format!("could not read from database: {}", path.display())),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn db_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
|
||||
const DB_FILENAME: &str = "db.zo";
|
||||
data_dir.as_ref().join(DB_FILENAME)
|
||||
Ok(dirs)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -155,50 +240,49 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn add() {
|
||||
let data_dir = tempfile::tempdir().unwrap();
|
||||
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
||||
let now = 946684800;
|
||||
|
||||
let data_dir = tempfile::tempdir().unwrap();
|
||||
{
|
||||
let mut db = DatabaseFile::new(data_dir.path());
|
||||
let mut db = db.open().unwrap();
|
||||
db.add(path, now);
|
||||
db.add(path, now);
|
||||
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||
db.add(path, 1.0, now);
|
||||
db.add(path, 1.0, now);
|
||||
db.save().unwrap();
|
||||
}
|
||||
{
|
||||
let mut db = DatabaseFile::new(data_dir.path());
|
||||
let db = db.open().unwrap();
|
||||
assert_eq!(db.dirs.len(), 1);
|
||||
|
||||
let dir = &db.dirs[0];
|
||||
{
|
||||
let db = Database::open_dir(data_dir.path()).unwrap();
|
||||
assert_eq!(db.dirs().len(), 1);
|
||||
|
||||
let dir = &db.dirs()[0];
|
||||
assert_eq!(dir.path, path);
|
||||
assert!((dir.rank - 2.0).abs() < 0.01);
|
||||
assert_eq!(dir.last_accessed, now);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove() {
|
||||
let data_dir = tempfile::tempdir().unwrap();
|
||||
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
||||
let now = 946684800;
|
||||
|
||||
let data_dir = tempfile::tempdir().unwrap();
|
||||
{
|
||||
let mut db = DatabaseFile::new(data_dir.path());
|
||||
let mut db = db.open().unwrap();
|
||||
db.add(path, now);
|
||||
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||
db.add(path, 1.0, now);
|
||||
db.save().unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let mut db = DatabaseFile::new(data_dir.path());
|
||||
let mut db = db.open().unwrap();
|
||||
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||
assert!(db.remove(path));
|
||||
db.save().unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let mut db = DatabaseFile::new(data_dir.path());
|
||||
let mut db = db.open().unwrap();
|
||||
assert!(db.dirs.is_empty());
|
||||
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||
assert!(db.dirs().is_empty());
|
||||
assert!(!db.remove(path));
|
||||
db.save().unwrap();
|
||||
}
|
||||
|
@ -2,36 +2,36 @@ use std::iter::Rev;
|
||||
use std::ops::Range;
|
||||
use std::{fs, path};
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
use crate::db::{Database, Dir, Epoch};
|
||||
use crate::util::{self, MONTH};
|
||||
|
||||
use super::{Database, Dir, Epoch};
|
||||
use crate::util;
|
||||
|
||||
pub struct Stream<'db, 'file> {
|
||||
db: &'db mut Database<'file>,
|
||||
pub struct Stream<'a> {
|
||||
// State
|
||||
db: &'a mut Database,
|
||||
idxs: Rev<Range<usize>>,
|
||||
did_exclude: bool,
|
||||
|
||||
// Configuration
|
||||
keywords: Vec<String>,
|
||||
|
||||
check_exists: bool,
|
||||
expire_below: Epoch,
|
||||
resolve_symlinks: bool,
|
||||
|
||||
exclude_path: Option<String>,
|
||||
}
|
||||
|
||||
impl<'db, 'file> Stream<'db, 'file> {
|
||||
pub fn new(db: &'db mut Database<'file>, now: Epoch) -> Self {
|
||||
// Iterate in descending order of score.
|
||||
db.dirs.sort_unstable_by_key(|dir| OrderedFloat(dir.score(now)));
|
||||
let idxs = (0..db.dirs.len()).rev();
|
||||
impl<'a> Stream<'a> {
|
||||
pub fn new(db: &'a mut Database, now: Epoch) -> Self {
|
||||
db.sort_by_score(now);
|
||||
let idxs = (0..db.dirs().len()).rev();
|
||||
|
||||
// If a directory is deleted and hasn't been used for 90 days, delete it from the database.
|
||||
let expire_below = now.saturating_sub(90 * 24 * 60 * 60);
|
||||
// If a directory is deleted and hasn't been used for 3 months, delete
|
||||
// it from the database.
|
||||
let expire_below = now.saturating_sub(3 * MONTH);
|
||||
|
||||
Stream {
|
||||
db,
|
||||
idxs,
|
||||
did_exclude: false,
|
||||
keywords: Vec::new(),
|
||||
check_exists: false,
|
||||
expire_below,
|
||||
@ -40,7 +40,7 @@ impl<'db, 'file> Stream<'db, 'file> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_exclude<S: Into<String>>(mut self, path: S) -> Self {
|
||||
pub fn with_exclude(mut self, path: impl Into<String>) -> Self {
|
||||
self.exclude_path = Some(path.into());
|
||||
self
|
||||
}
|
||||
@ -51,14 +51,14 @@ impl<'db, 'file> Stream<'db, 'file> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_keywords<S: AsRef<str>>(mut self, keywords: &[S]) -> Self {
|
||||
pub fn with_keywords(mut self, keywords: &[impl AsRef<str>]) -> Self {
|
||||
self.keywords = keywords.iter().map(util::to_lowercase).collect();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<&Dir<'file>> {
|
||||
pub fn next(&mut self) -> Option<&Dir> {
|
||||
while let Some(idx) = self.idxs.next() {
|
||||
let dir = &self.db.dirs[idx];
|
||||
let dir = &self.db.dirs()[idx];
|
||||
|
||||
if !self.matches_keywords(&dir.path) {
|
||||
continue;
|
||||
@ -66,32 +66,36 @@ impl<'db, 'file> Stream<'db, 'file> {
|
||||
|
||||
if !self.matches_exists(&dir.path) {
|
||||
if dir.last_accessed < self.expire_below {
|
||||
self.db.dirs.swap_remove(idx);
|
||||
self.db.modified = true;
|
||||
self.db.swap_remove(idx);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if Some(dir.path.as_ref()) == self.exclude_path.as_deref() {
|
||||
self.did_exclude = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
let dir = &self.db.dirs[idx];
|
||||
let dir = &self.db.dirs()[idx];
|
||||
return Some(dir);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn matches_exists<S: AsRef<str>>(&self, path: S) -> bool {
|
||||
pub fn did_exclude(&self) -> bool {
|
||||
self.did_exclude
|
||||
}
|
||||
|
||||
fn matches_exists(&self, path: &str) -> bool {
|
||||
if !self.check_exists {
|
||||
return true;
|
||||
}
|
||||
let resolver = if self.resolve_symlinks { fs::symlink_metadata } else { fs::metadata };
|
||||
resolver(path.as_ref()).map(|m| m.is_dir()).unwrap_or_default()
|
||||
resolver(path).map(|m| m.is_dir()).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn matches_keywords<S: AsRef<str>>(&self, path: S) -> bool {
|
||||
fn matches_keywords(&self, path: &str) -> bool {
|
||||
let (keywords_last, keywords) = match self.keywords.split_last() {
|
||||
Some(split) => split,
|
||||
None => return true,
|
||||
@ -126,7 +130,7 @@ mod tests {
|
||||
|
||||
use rstest::rstest;
|
||||
|
||||
use super::Database;
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
// Case normalization
|
||||
@ -149,8 +153,8 @@ mod tests {
|
||||
#[case(&["/foo/", "/bar"], "/foo/bar", false)]
|
||||
#[case(&["/foo/", "/bar"], "/foo/baz/bar", true)]
|
||||
fn query(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) {
|
||||
let mut db = Database { dirs: Vec::new().into(), modified: false, data_dir: &PathBuf::new() };
|
||||
let stream = db.stream(0).with_keywords(keywords);
|
||||
let db = &mut Database::new(PathBuf::new(), Vec::new(), |_| Vec::new(), false);
|
||||
let stream = Stream::new(db, 0).with_keywords(keywords);
|
||||
assert_eq!(is_match, stream.matches_keywords(path));
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use anyhow::{bail, Context, Result};
|
||||
/// Custom error type for early exit.
|
||||
#[derive(Debug)]
|
||||
pub struct SilentExit {
|
||||
pub code: i32,
|
||||
pub code: u8,
|
||||
}
|
||||
|
||||
impl Display for SilentExit {
|
||||
@ -23,7 +23,7 @@ impl BrokenPipeHandler for io::Result<()> {
|
||||
fn pipe_exit(self, device: &str) -> Result<()> {
|
||||
match self {
|
||||
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => bail!(SilentExit { code: 0 }),
|
||||
result => result.with_context(|| format!("could not write to {}", device)),
|
||||
result => result.with_context(|| format!("could not write to {device}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
src/main.rs
20
src/main.rs
@ -1,7 +1,7 @@
|
||||
#![allow(clippy::single_component_path_imports)]
|
||||
|
||||
// rstest_reuse must be imported at the top of the crate.
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, feature = "nix-dev"))]
|
||||
use rstest_reuse;
|
||||
|
||||
mod cmd;
|
||||
@ -11,26 +11,28 @@ mod error;
|
||||
mod shell;
|
||||
mod util;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
use std::{env, process};
|
||||
use std::process::ExitCode;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use crate::cmd::{Cmd, Run};
|
||||
use crate::error::SilentExit;
|
||||
|
||||
pub fn main() {
|
||||
pub fn main() -> ExitCode {
|
||||
// Forcibly disable backtraces.
|
||||
env::remove_var("RUST_LIB_BACKTRACE");
|
||||
env::remove_var("RUST_BACKTRACE");
|
||||
|
||||
if let Err(e) = Cmd::parse().run() {
|
||||
match e.downcast::<SilentExit>() {
|
||||
Ok(SilentExit { code }) => process::exit(code),
|
||||
match Cmd::parse().run() {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => match e.downcast::<SilentExit>() {
|
||||
Ok(SilentExit { code }) => code.into(),
|
||||
Err(e) => {
|
||||
let _ = writeln!(io::stderr(), "zoxide: {:?}", e);
|
||||
process::exit(1);
|
||||
_ = writeln!(io::stderr(), "zoxide: {e:?}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
61
src/shell.rs
61
src/shell.rs
@ -57,7 +57,7 @@ mod tests {
|
||||
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
||||
let source = Bash(&opts).render().unwrap();
|
||||
Command::new("bash")
|
||||
.args(&["--noprofile", "--norc", "-e", "-u", "-o", "pipefail", "-c", &source])
|
||||
.args(["--noprofile", "--norc", "-e", "-u", "-o", "pipefail", "-c", &source])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("")
|
||||
@ -70,7 +70,7 @@ mod tests {
|
||||
let source = Bash(&opts).render().unwrap();
|
||||
|
||||
Command::new("shellcheck")
|
||||
.args(&["--enable", "all", "--shell", "bash", "-"])
|
||||
.args(["--enable", "all", "--shell", "bash", "-"])
|
||||
.write_stdin(source)
|
||||
.assert()
|
||||
.success()
|
||||
@ -85,7 +85,7 @@ mod tests {
|
||||
source.push('\n');
|
||||
|
||||
Command::new("shfmt")
|
||||
.args(&["-d", "-s", "-ln", "bash", "-i", "4", "-ci", "-"])
|
||||
.args(["--diff", "--indent=4", "--language-dialect=bash", "--simplify", "-"])
|
||||
.write_stdin(source)
|
||||
.assert()
|
||||
.success()
|
||||
@ -96,16 +96,21 @@ mod tests {
|
||||
#[apply(opts)]
|
||||
fn elvish_elvish(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {
|
||||
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
||||
let mut source = String::new();
|
||||
let mut source = String::default();
|
||||
|
||||
// Filter out lines using edit:*, since those functions are only available in the
|
||||
// interactive editor.
|
||||
for line in Elvish(&opts).render().unwrap().split('\n').filter(|line| !line.contains("edit:")) {
|
||||
// Filter out lines using edit:*, since those functions are only available in
|
||||
// the interactive editor.
|
||||
for line in Elvish(&opts).render().unwrap().lines().filter(|line| !line.contains("edit:")) {
|
||||
source.push_str(line);
|
||||
source.push('\n');
|
||||
}
|
||||
|
||||
Command::new("elvish").args(&["-c", &source, "-norc"]).assert().success().stdout("").stderr("");
|
||||
Command::new("elvish")
|
||||
.args(["-c", &source, "-norc"])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
#[apply(opts)]
|
||||
@ -118,7 +123,7 @@ mod tests {
|
||||
|
||||
Command::new("fish")
|
||||
.env("HOME", tempdir)
|
||||
.args(&["--command", &source, "--private"])
|
||||
.args(["--command", &source, "--no-config", "--private"])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("")
|
||||
@ -149,10 +154,14 @@ mod tests {
|
||||
let source = Nushell(&opts).render().unwrap();
|
||||
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let tempdir = tempdir.path().to_str().unwrap();
|
||||
let tempdir = tempdir.path();
|
||||
|
||||
let assert =
|
||||
Command::new("nu").env("HOME", tempdir).args(&["--commands", &source]).assert().success().stderr("");
|
||||
let assert = Command::new("nu")
|
||||
.env("HOME", tempdir)
|
||||
.args(["--commands", &source])
|
||||
.assert()
|
||||
.success()
|
||||
.stderr("");
|
||||
|
||||
if opts.hook != InitHook::Pwd {
|
||||
assert.stdout("");
|
||||
@ -165,7 +174,7 @@ mod tests {
|
||||
let source = Posix(&opts).render().unwrap();
|
||||
|
||||
let assert = Command::new("bash")
|
||||
.args(&["--posix", "--noprofile", "--norc", "-e", "-u", "-o", "pipefail", "-c", &source])
|
||||
.args(["--posix", "--noprofile", "--norc", "-e", "-u", "-o", "pipefail", "-c", &source])
|
||||
.assert()
|
||||
.success()
|
||||
.stderr("");
|
||||
@ -179,7 +188,8 @@ mod tests {
|
||||
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
||||
let source = Posix(&opts).render().unwrap();
|
||||
|
||||
let assert = Command::new("dash").args(&["-e", "-u", "-c", &source]).assert().success().stderr("");
|
||||
let assert =
|
||||
Command::new("dash").args(["-e", "-u", "-c", &source]).assert().success().stderr("");
|
||||
if opts.hook != InitHook::Pwd {
|
||||
assert.stdout("");
|
||||
}
|
||||
@ -191,7 +201,7 @@ mod tests {
|
||||
let source = Posix(&opts).render().unwrap();
|
||||
|
||||
Command::new("shellcheck")
|
||||
.args(&["--enable", "all", "--shell", "sh", "-"])
|
||||
.args(["--enable", "all", "--shell", "sh", "-"])
|
||||
.write_stdin(source)
|
||||
.assert()
|
||||
.success()
|
||||
@ -206,7 +216,7 @@ mod tests {
|
||||
source.push('\n');
|
||||
|
||||
Command::new("shfmt")
|
||||
.args(&["-d", "-s", "-ln", "posix", "-i", "4", "-ci", "-"])
|
||||
.args(["--diff", "--indent=4", "--language-dialect=posix", "--simplify", "-"])
|
||||
.write_stdin(source)
|
||||
.assert()
|
||||
.success()
|
||||
@ -221,7 +231,7 @@ mod tests {
|
||||
Powershell(&opts).render_into(&mut source).unwrap();
|
||||
|
||||
Command::new("pwsh")
|
||||
.args(&["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", &source])
|
||||
.args(["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", &source])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("")
|
||||
@ -234,7 +244,12 @@ mod tests {
|
||||
let mut source = Xonsh(&opts).render().unwrap();
|
||||
source.push('\n');
|
||||
|
||||
Command::new("black").args(&["--check", "--diff", "-"]).write_stdin(source).assert().success().stdout("");
|
||||
Command::new("black")
|
||||
.args(["--check", "--diff", "-"])
|
||||
.write_stdin(source)
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("");
|
||||
}
|
||||
|
||||
#[apply(opts)]
|
||||
@ -242,7 +257,7 @@ mod tests {
|
||||
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
||||
let source = Xonsh(&opts).render().unwrap();
|
||||
|
||||
Command::new("mypy").args(&["--command", &source, "--strict"]).assert().success().stderr("");
|
||||
Command::new("mypy").args(["--command", &source, "--strict"]).assert().success().stderr("");
|
||||
}
|
||||
|
||||
#[apply(opts)]
|
||||
@ -252,7 +267,7 @@ mod tests {
|
||||
source.push('\n');
|
||||
|
||||
Command::new("pylint")
|
||||
.args(&["--from-stdin", "--persistent=n", "zoxide"])
|
||||
.args(["--from-stdin", "--persistent=n", "zoxide"])
|
||||
.write_stdin(source)
|
||||
.assert()
|
||||
.success()
|
||||
@ -268,7 +283,7 @@ mod tests {
|
||||
let tempdir = tempdir.path().to_str().unwrap();
|
||||
|
||||
Command::new("xonsh")
|
||||
.args(&["-c", &source, "--no-rc"])
|
||||
.args(["-c", &source, "--no-rc"])
|
||||
.env("HOME", tempdir)
|
||||
.assert()
|
||||
.success()
|
||||
@ -283,7 +298,7 @@ mod tests {
|
||||
|
||||
// ShellCheck doesn't support zsh yet: https://github.com/koalaman/shellcheck/issues/809
|
||||
Command::new("shellcheck")
|
||||
.args(&["--enable", "all", "--shell", "bash", "-"])
|
||||
.args(["--enable", "all", "--shell", "bash", "-"])
|
||||
.write_stdin(source)
|
||||
.assert()
|
||||
.success()
|
||||
@ -297,7 +312,7 @@ mod tests {
|
||||
let source = Zsh(&opts).render().unwrap();
|
||||
|
||||
Command::new("zsh")
|
||||
.args(&["-e", "-u", "-o", "pipefail", "--no-globalrcs", "--no-rcs", "-c", &source])
|
||||
.args(["-e", "-u", "-o", "pipefail", "--no-globalrcs", "--no-rcs", "-c", &source])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("")
|
||||
|
295
src/util.rs
295
src/util.rs
@ -1,83 +1,149 @@
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::mem;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::process::{Child, ChildStdin, Stdio};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::time::SystemTime;
|
||||
use std::{env, mem};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
#[cfg(windows)]
|
||||
use anyhow::anyhow;
|
||||
use anyhow::{bail, Context, Result};
|
||||
|
||||
use crate::config;
|
||||
use crate::db::Epoch;
|
||||
use crate::db::{Dir, Epoch};
|
||||
use crate::error::SilentExit;
|
||||
|
||||
pub struct Fzf {
|
||||
child: Child,
|
||||
}
|
||||
pub const SECOND: Epoch = 1;
|
||||
pub const MINUTE: Epoch = 60 * SECOND;
|
||||
pub const HOUR: Epoch = 60 * MINUTE;
|
||||
pub const DAY: Epoch = 24 * HOUR;
|
||||
pub const WEEK: Epoch = 7 * DAY;
|
||||
pub const MONTH: Epoch = 30 * DAY;
|
||||
|
||||
pub struct Fzf(Command);
|
||||
|
||||
impl Fzf {
|
||||
const ERR_NOT_FOUND: &'static str = "could not find fzf, is it installed?";
|
||||
const ERR_FZF_NOT_FOUND: &str = "could not find fzf, is it installed?";
|
||||
|
||||
pub fn new(multiple: bool) -> Result<Self> {
|
||||
let bin = if cfg!(windows) { "fzf.exe" } else { "fzf" };
|
||||
let mut command = get_command(bin).map_err(|_| anyhow!(Self::ERR_NOT_FOUND))?;
|
||||
if multiple {
|
||||
command.arg("-m");
|
||||
}
|
||||
command.arg("-n2..").stdin(Stdio::piped()).stdout(Stdio::piped());
|
||||
if let Some(fzf_opts) = config::fzf_opts() {
|
||||
command.env("FZF_DEFAULT_OPTS", fzf_opts);
|
||||
} else {
|
||||
command.args(&[
|
||||
// Search result
|
||||
"--no-sort",
|
||||
// Interface
|
||||
"--keep-right",
|
||||
// Layout
|
||||
"--height=40%",
|
||||
"--info=inline",
|
||||
"--layout=reverse",
|
||||
// Scripting
|
||||
"--exit-0",
|
||||
"--select-1",
|
||||
// Key/Event bindings
|
||||
"--bind=ctrl-z:ignore",
|
||||
]);
|
||||
if cfg!(unix) {
|
||||
command.env("SHELL", "sh");
|
||||
command.arg(r"--preview=\command -p ls -p {2..}");
|
||||
}
|
||||
}
|
||||
pub fn new() -> Result<Self> {
|
||||
// On Windows, CreateProcess implicitly searches the current working
|
||||
// directory for the executable, which is a potential security issue.
|
||||
// Instead, we resolve the path to the executable and then pass it to
|
||||
// CreateProcess.
|
||||
#[cfg(windows)]
|
||||
let program = which::which("fzf.exe").map_err(|_| anyhow!(Self::ERR_FZF_NOT_FOUND))?;
|
||||
#[cfg(not(windows))]
|
||||
let program = "fzf";
|
||||
|
||||
let child = match command.spawn() {
|
||||
Ok(child) => child,
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => bail!(Self::ERR_NOT_FOUND),
|
||||
Err(e) => Err(e).context("could not launch fzf")?,
|
||||
};
|
||||
// TODO: check version of fzf here.
|
||||
|
||||
Ok(Fzf { child })
|
||||
let mut cmd = Command::new(program);
|
||||
cmd.args([
|
||||
// Search mode
|
||||
"--delimiter=\t",
|
||||
"--nth=2",
|
||||
// Scripting
|
||||
"--read0",
|
||||
])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped());
|
||||
|
||||
Ok(Fzf(cmd))
|
||||
}
|
||||
|
||||
pub fn stdin(&mut self) -> &mut ChildStdin {
|
||||
self.child.stdin.as_mut().unwrap()
|
||||
pub fn enable_preview(&mut self) -> &mut Self {
|
||||
// Previews are only supported on UNIX.
|
||||
if !cfg!(unix) {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.args([
|
||||
// Non-POSIX args are only available on certain operating systems.
|
||||
if cfg!(target_os = "linux") {
|
||||
r"--preview=\command -p ls -Cp --color=always --group-directories-first {2..}"
|
||||
} else {
|
||||
r"--preview=\command -p ls -Cp {2..}"
|
||||
},
|
||||
// Rounded edges don't display correctly on some terminals.
|
||||
"--preview-window=down,30%,sharp",
|
||||
])
|
||||
.envs([
|
||||
// Enables colorized `ls` output on macOS / FreeBSD.
|
||||
("CLICOLOR", "1"),
|
||||
// Forces colorized `ls` output when the output is not a
|
||||
// TTY (like in fzf's preview window) on macOS /
|
||||
// FreeBSD.
|
||||
("CLICOLOR_FORCE", "1"),
|
||||
// Ensures that the preview command is run in a
|
||||
// POSIX-compliant shell, regardless of what shell the
|
||||
// user has selected.
|
||||
("SHELL", "sh"),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn select(mut self) -> Result<String> {
|
||||
pub fn args<I, S>(&mut self, args: I) -> &mut Self
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
self.0.args(args);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
self.0.env(key, val);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
|
||||
where
|
||||
I: IntoIterator<Item = (K, V)>,
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
self.0.envs(vars);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn spawn(&mut self) -> Result<FzfChild> {
|
||||
match self.0.spawn() {
|
||||
Ok(child) => Ok(FzfChild(child)),
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => bail!(Self::ERR_FZF_NOT_FOUND),
|
||||
Err(e) => Err(e).context("could not launch fzf"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FzfChild(Child);
|
||||
|
||||
impl FzfChild {
|
||||
pub fn write(&mut self, dir: &Dir, now: Epoch) -> Result<Option<String>> {
|
||||
let handle = self.0.stdin.as_mut().unwrap();
|
||||
match write!(handle, "{}\0", dir.display().with_score(now).with_separator('\t')) {
|
||||
Ok(()) => Ok(None),
|
||||
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => self.wait().map(Some),
|
||||
Err(e) => Err(e).context("could not write to fzf"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait(&mut self) -> Result<String> {
|
||||
// Drop stdin to prevent deadlock.
|
||||
mem::drop(self.child.stdin.take());
|
||||
mem::drop(self.0.stdin.take());
|
||||
|
||||
let mut stdout = self.child.stdout.take().unwrap();
|
||||
let mut output = String::new();
|
||||
let mut stdout = self.0.stdout.take().unwrap();
|
||||
let mut output = String::default();
|
||||
stdout.read_to_string(&mut output).context("failed to read from fzf")?;
|
||||
|
||||
let status = self.child.wait().context("wait failed on fzf")?;
|
||||
let status = self.0.wait().context("wait failed on fzf")?;
|
||||
match status.code() {
|
||||
Some(0) => Ok(output),
|
||||
Some(1) => bail!("no match found"),
|
||||
Some(2) => bail!("fzf returned an error"),
|
||||
Some(code @ 130) => bail!(SilentExit { code }),
|
||||
Some(130) => bail!(SilentExit { code: 130 }),
|
||||
Some(128..=254) | None => bail!("fzf was terminated"),
|
||||
_ => bail!("fzf returned an unknown error"),
|
||||
}
|
||||
@ -85,7 +151,7 @@ impl Fzf {
|
||||
}
|
||||
|
||||
/// Similar to [`fs::write`], but atomic (best effort on Windows).
|
||||
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
|
||||
pub fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<()> {
|
||||
let path = path.as_ref();
|
||||
let contents = contents.as_ref();
|
||||
let dir = path.parent().unwrap();
|
||||
@ -94,19 +160,22 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()>
|
||||
let (mut tmp_file, tmp_path) = tmpfile(dir)?;
|
||||
let result = (|| {
|
||||
// Write to the tmpfile.
|
||||
let _ = tmp_file.set_len(contents.len() as u64);
|
||||
tmp_file.write_all(contents).with_context(|| format!("could not write to file: {}", tmp_path.display()))?;
|
||||
_ = tmp_file.set_len(contents.len() as u64);
|
||||
tmp_file
|
||||
.write_all(contents)
|
||||
.with_context(|| format!("could not write to file: {}", tmp_path.display()))?;
|
||||
|
||||
// Set the owner of the tmpfile (UNIX only).
|
||||
#[cfg(unix)]
|
||||
if let Ok(metadata) = path.metadata() {
|
||||
use nix::unistd::{self, Gid, Uid};
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use nix::unistd::{self, Gid, Uid};
|
||||
|
||||
let uid = Uid::from_raw(metadata.uid());
|
||||
let gid = Gid::from_raw(metadata.gid());
|
||||
let _ = unistd::fchown(tmp_file.as_raw_fd(), Some(uid), Some(gid));
|
||||
_ = unistd::fchown(tmp_file.as_raw_fd(), Some(uid), Some(gid));
|
||||
}
|
||||
|
||||
// Close and rename the tmpfile.
|
||||
@ -115,13 +184,13 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()>
|
||||
})();
|
||||
// In case of an error, delete the tmpfile.
|
||||
if result.is_err() {
|
||||
let _ = fs::remove_file(&tmp_path);
|
||||
_ = fs::remove_file(&tmp_path);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Atomically create a tmpfile in the given directory.
|
||||
fn tmpfile<P: AsRef<Path>>(dir: P) -> Result<(File, PathBuf)> {
|
||||
fn tmpfile(dir: impl AsRef<Path>) -> Result<(File, PathBuf)> {
|
||||
const MAX_ATTEMPTS: usize = 5;
|
||||
const TMP_NAME_LEN: usize = 16;
|
||||
let dir = dir.as_ref();
|
||||
@ -141,35 +210,39 @@ fn tmpfile<P: AsRef<Path>>(dir: P) -> Result<(File, PathBuf)> {
|
||||
// Atomically create the tmpfile.
|
||||
match OpenOptions::new().write(true).create_new(true).open(&path) {
|
||||
Ok(file) => break Ok((file, path)),
|
||||
Err(e) if e.kind() == io::ErrorKind::AlreadyExists && attempts < MAX_ATTEMPTS => (),
|
||||
Err(e) => break Err(e).with_context(|| format!("could not create file: {}", path.display())),
|
||||
Err(e) if e.kind() == io::ErrorKind::AlreadyExists && attempts < MAX_ATTEMPTS => {}
|
||||
Err(e) => {
|
||||
break Err(e).with_context(|| format!("could not create file: {}", path.display()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to [`fs::rename`], but retries on Windows.
|
||||
fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
|
||||
const MAX_ATTEMPTS: usize = 5;
|
||||
/// Similar to [`fs::rename`], but with retries on Windows.
|
||||
fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
||||
let from = from.as_ref();
|
||||
let to = to.as_ref();
|
||||
|
||||
if cfg!(windows) {
|
||||
let mut attempts = 0;
|
||||
loop {
|
||||
attempts += 1;
|
||||
match fs::rename(from, to) {
|
||||
Err(e) if e.kind() == io::ErrorKind::PermissionDenied && attempts < MAX_ATTEMPTS => (),
|
||||
result => break result,
|
||||
const MAX_ATTEMPTS: usize = if cfg!(windows) { 5 } else { 1 };
|
||||
let mut attempts = 0;
|
||||
|
||||
loop {
|
||||
match fs::rename(from, to) {
|
||||
Err(e) if e.kind() == io::ErrorKind::PermissionDenied && attempts < MAX_ATTEMPTS => {
|
||||
attempts += 1
|
||||
}
|
||||
result => {
|
||||
break result.with_context(|| {
|
||||
format!("could not rename file: {} -> {}", from.display(), to.display())
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fs::rename(from, to)
|
||||
}
|
||||
.with_context(|| format!("could not rename file: {} -> {}", from.display(), to.display()))
|
||||
}
|
||||
|
||||
pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||
dunce::canonicalize(path).with_context(|| format!("could not resolve path: {}", path.as_ref().display()))
|
||||
pub fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {
|
||||
dunce::canonicalize(&path)
|
||||
.with_context(|| format!("could not resolve path: {}", path.as_ref().display()))
|
||||
}
|
||||
|
||||
pub fn current_dir() -> Result<PathBuf> {
|
||||
@ -177,49 +250,22 @@ pub fn current_dir() -> Result<PathBuf> {
|
||||
}
|
||||
|
||||
pub fn current_time() -> Result<Epoch> {
|
||||
let current_time =
|
||||
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).context("system clock set to invalid time")?.as_secs();
|
||||
let current_time = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.context("system clock set to invalid time")?
|
||||
.as_secs();
|
||||
|
||||
Ok(current_time)
|
||||
}
|
||||
|
||||
/// Constructs a new [`Command`] for launching the program with the name
|
||||
/// `program`.
|
||||
///
|
||||
/// On Windows, CreateProcess implicitly searches the current working directory
|
||||
/// for the executable, which is a potential security issue. Instead, we resolve
|
||||
/// the path to the executable and then pass it to CreateProcess.
|
||||
///
|
||||
/// On other platforms, this is a no-op.
|
||||
///
|
||||
pub fn get_command<P: AsRef<Path>>(program: P) -> Result<Command> {
|
||||
let program = program.as_ref();
|
||||
if !cfg!(windows) {
|
||||
return Ok(Command::new(program));
|
||||
}
|
||||
|
||||
let paths = env::var_os("PATH").context("PATH environment variable not set")?;
|
||||
for path in env::split_paths(&paths) {
|
||||
if path.as_os_str().is_empty() {
|
||||
continue;
|
||||
}
|
||||
let path = path.join(program);
|
||||
if path.metadata().map_or(false, |m| !m.is_dir()) {
|
||||
return Ok(Command::new(path));
|
||||
}
|
||||
}
|
||||
|
||||
bail!("executable not found in PATH: {}", program.display());
|
||||
}
|
||||
|
||||
pub fn path_to_str<P: AsRef<Path>>(path: &P) -> Result<&str> {
|
||||
pub fn path_to_str(path: &impl AsRef<Path>) -> Result<&str> {
|
||||
let path = path.as_ref();
|
||||
path.to_str().with_context(|| format!("invalid unicode in path: {}", path.display()))
|
||||
}
|
||||
|
||||
/// Returns the absolute version of a path. Like [`std::path::Path::canonicalize`], but doesn't
|
||||
/// resolve symlinks.
|
||||
pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||
/// Returns the absolute version of a path. Like
|
||||
/// [`std::path::Path::canonicalize`], but doesn't resolve symlinks.
|
||||
pub fn resolve_path(path: impl AsRef<Path>) -> Result<PathBuf> {
|
||||
let path = path.as_ref();
|
||||
let base_path;
|
||||
|
||||
@ -230,13 +276,15 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||
if cfg!(windows) {
|
||||
use std::path::Prefix;
|
||||
|
||||
fn get_drive_letter<P: AsRef<Path>>(path: P) -> Option<u8> {
|
||||
fn get_drive_letter(path: impl AsRef<Path>) -> Option<u8> {
|
||||
let path = path.as_ref();
|
||||
let mut components = path.components();
|
||||
|
||||
match components.next() {
|
||||
Some(Component::Prefix(prefix)) => match prefix.kind() {
|
||||
Prefix::Disk(drive_letter) | Prefix::VerbatimDisk(drive_letter) => Some(drive_letter),
|
||||
Prefix::Disk(drive_letter) | Prefix::VerbatimDisk(drive_letter) => {
|
||||
Some(drive_letter)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
@ -289,8 +337,9 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||
components.next();
|
||||
|
||||
let current_dir = env::current_dir()?;
|
||||
let drive_letter = get_drive_letter(¤t_dir)
|
||||
.with_context(|| format!("could not get drive letter: {}", current_dir.display()))?;
|
||||
let drive_letter = get_drive_letter(¤t_dir).with_context(|| {
|
||||
format!("could not get drive letter: {}", current_dir.display())
|
||||
})?;
|
||||
base_path = get_drive_path(drive_letter);
|
||||
stack.extend(base_path.components());
|
||||
}
|
||||
@ -310,7 +359,7 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Normal(_) => stack.push(component),
|
||||
Component::CurDir => (),
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
if stack.last() != Some(&Component::RootDir) {
|
||||
stack.pop();
|
||||
@ -324,11 +373,7 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||
}
|
||||
|
||||
/// Convert a string to lowercase, with a fast path for ASCII strings.
|
||||
pub fn to_lowercase<S: AsRef<str>>(s: S) -> String {
|
||||
pub fn to_lowercase(s: impl AsRef<str>) -> String {
|
||||
let s = s.as_ref();
|
||||
if s.is_ascii() {
|
||||
s.to_ascii_lowercase()
|
||||
} else {
|
||||
s.to_lowercase()
|
||||
}
|
||||
if s.is_ascii() { s.to_ascii_lowercase() } else { s.to_lowercase() }
|
||||
}
|
||||
|
@ -7,7 +7,9 @@
|
||||
|
||||
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
||||
function __zoxide_pwd() {
|
||||
{%- if resolve_symlinks %}
|
||||
{%- if cfg!(windows) %}
|
||||
\command cygpath -w "$(\builtin pwd -P)"
|
||||
{%- else if resolve_symlinks %}
|
||||
\builtin pwd -P
|
||||
{%- else %}
|
||||
\builtin pwd -L
|
||||
@ -32,7 +34,8 @@ function __zoxide_cd() {
|
||||
{%- if hook == InitHook::Prompt %}
|
||||
function __zoxide_hook() {
|
||||
\builtin local -r retval="$?"
|
||||
\command zoxide add -- "$(__zoxide_pwd || \builtin true)"
|
||||
# shellcheck disable=SC2312
|
||||
\command zoxide add -- "$(__zoxide_pwd)"
|
||||
return "${retval}"
|
||||
}
|
||||
{%- else if hook == InitHook::Pwd %}
|
||||
@ -58,8 +61,7 @@ fi
|
||||
{% endif -%}
|
||||
|
||||
{{ section }}
|
||||
# When using zoxide with --no-aliases, alias these internal functions as
|
||||
# desired.
|
||||
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||
#
|
||||
|
||||
__zoxide_z_prefix='z#'
|
||||
@ -73,13 +75,14 @@ function __zoxide_z() {
|
||||
__zoxide_cd "${OLDPWD}"
|
||||
elif [[ $# -eq 1 && -d $1 ]]; then
|
||||
__zoxide_cd "$1"
|
||||
elif [[ ${@: -1} == "${__zoxide_z_prefix}"* ]]; then
|
||||
elif [[ ${@: -1} == "${__zoxide_z_prefix}"?* ]]; then
|
||||
# shellcheck disable=SC2124
|
||||
\builtin local result="${@: -1}"
|
||||
__zoxide_cd "{{ "${result:${#__zoxide_z_prefix}}" }}"
|
||||
else
|
||||
\builtin local result
|
||||
result="$(\command zoxide query --exclude "$(__zoxide_pwd || \builtin true)" -- "$@")" &&
|
||||
# shellcheck disable=SC2312
|
||||
result="$(\command zoxide query --exclude "$(__zoxide_pwd)" -- "$@")" &&
|
||||
__zoxide_cd "${result}"
|
||||
fi
|
||||
}
|
||||
@ -87,29 +90,22 @@ function __zoxide_z() {
|
||||
# Jump to a directory using interactive search.
|
||||
function __zoxide_zi() {
|
||||
\builtin local result
|
||||
result="$(\command zoxide query -i -- "$@")" && __zoxide_cd "${result}"
|
||||
result="$(\command zoxide query --interactive -- "$@")" && __zoxide_cd "${result}"
|
||||
}
|
||||
|
||||
{{ section }}
|
||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
||||
# Commands for zoxide. Disable these using --no-cmd.
|
||||
#
|
||||
|
||||
{%- match cmd %}
|
||||
{%- when Some with (cmd) %}
|
||||
|
||||
# Remove definitions.
|
||||
function __zoxide_unset() {
|
||||
\builtin unset -f "$@" &>/dev/null
|
||||
\builtin unset -v "$@" &>/dev/null
|
||||
\builtin unalias "$@" &>/dev/null || \builtin :
|
||||
}
|
||||
|
||||
__zoxide_unset {{cmd}}
|
||||
\builtin unalias {{cmd}} &>/dev/null || \builtin true
|
||||
function {{cmd}}() {
|
||||
__zoxide_z "$@"
|
||||
}
|
||||
|
||||
__zoxide_unset {{cmd}}i
|
||||
\builtin unalias {{cmd}}i &>/dev/null || \builtin true
|
||||
function {{cmd}}i() {
|
||||
__zoxide_zi "$@"
|
||||
}
|
||||
@ -130,18 +126,21 @@ if [[ ${BASH_VERSINFO[0]:-0} -eq 4 && ${BASH_VERSINFO[1]:-0} -ge 4 || ${BASH_VER
|
||||
|
||||
# If there is only one argument, use `cd` completions.
|
||||
if [[ {{ "${#COMP_WORDS[@]}" }} -eq 2 ]]; then
|
||||
\builtin mapfile -t COMPREPLY < \
|
||||
<(\builtin compgen -A directory -S / -- "${COMP_WORDS[-1]}" || \builtin true)
|
||||
\builtin mapfile -t COMPREPLY < <(
|
||||
\builtin compgen -A directory -- "${COMP_WORDS[-1]}" || \builtin true
|
||||
)
|
||||
# If there is a space after the last word, use interactive selection.
|
||||
elif [[ -z ${COMP_WORDS[-1]} ]]; then
|
||||
elif [[ -z ${COMP_WORDS[-1]} ]] && [[ ${COMP_WORDS[-2]} != "${__zoxide_z_prefix}"?* ]]; then
|
||||
\builtin local result
|
||||
result="$(\command zoxide query -i -- "{{ "${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}" }}")" &&
|
||||
COMPREPLY=("${__zoxide_z_prefix}${result@Q}")
|
||||
# shellcheck disable=SC2312
|
||||
result="$(\command zoxide query --exclude "$(__zoxide_pwd)" --interactive -- "{{ "${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}" }}")" &&
|
||||
COMPREPLY=("${__zoxide_z_prefix}${result}/")
|
||||
\builtin printf '\e[5n'
|
||||
fi
|
||||
}
|
||||
|
||||
\builtin complete -F __zoxide_z_complete -o nospace -- {{cmd}}
|
||||
\builtin complete -F __zoxide_z_complete -o filenames -- {{cmd}}
|
||||
\builtin complete -r {{cmd}}i &>/dev/null || \builtin true
|
||||
fi
|
||||
|
||||
{%- when None %}
|
||||
|
@ -41,8 +41,7 @@ if (builtin:not (builtin:eq $E:__zoxide_shlvl $E:SHLVL)) {
|
||||
{%- endif %}
|
||||
|
||||
{{ section }}
|
||||
# When using zoxide with --no-aliases, alias these internal functions as
|
||||
# desired.
|
||||
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||
#
|
||||
|
||||
# Jump to a directory using only keywords.
|
||||
@ -57,7 +56,7 @@ fn __zoxide_z {|@rest|
|
||||
var path
|
||||
try {
|
||||
set path = (zoxide query --exclude $pwd -- $@rest)
|
||||
} except {
|
||||
} catch {
|
||||
} else {
|
||||
__zoxide_cd $path
|
||||
}
|
||||
@ -69,8 +68,8 @@ edit:add-var __zoxide_z~ $__zoxide_z~
|
||||
fn __zoxide_zi {|@rest|
|
||||
var path
|
||||
try {
|
||||
set path = (zoxide query -i -- $@rest)
|
||||
} except {
|
||||
set path = (zoxide query --interactive -- $@rest)
|
||||
} catch {
|
||||
} else {
|
||||
__zoxide_cd $path
|
||||
}
|
||||
@ -78,7 +77,7 @@ fn __zoxide_zi {|@rest|
|
||||
edit:add-var __zoxide_zi~ $__zoxide_zi~
|
||||
|
||||
{{ section }}
|
||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
||||
# Commands for zoxide. Disable these using --no-cmd.
|
||||
#
|
||||
|
||||
{%- match cmd %}
|
||||
@ -88,8 +87,10 @@ edit:add-var {{cmd}}~ $__zoxide_z~
|
||||
edit:add-var {{cmd}}i~ $__zoxide_zi~
|
||||
|
||||
# Load completions.
|
||||
{# zoxide-based completions are currently not possible, because Elvish only
|
||||
# prints a completion if the current token is a prefix of it. -#}
|
||||
{#-
|
||||
zoxide-based completions are currently not possible, because Elvish only prints
|
||||
a completion if the current token is a prefix of it.
|
||||
#}
|
||||
fn __zoxide_z_complete {|@rest|
|
||||
if (!= (builtin:count $rest) 2) {
|
||||
builtin:return
|
||||
@ -116,4 +117,4 @@ set edit:completion:arg-completer[{{cmd}}] = $__zoxide_z_complete~
|
||||
#
|
||||
# eval (zoxide init elvish | slurp)
|
||||
#
|
||||
# Note: zoxide only supports elvish v0.17.0 and above.
|
||||
# Note: zoxide only supports elvish v0.18.0 and above.
|
||||
|
@ -7,7 +7,9 @@
|
||||
|
||||
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
||||
function __zoxide_pwd
|
||||
{%- if resolve_symlinks %}
|
||||
{%- if cfg!(windows) %}
|
||||
command cygpath -w (builtin pwd -P)
|
||||
{%- else if resolve_symlinks %}
|
||||
builtin pwd -P
|
||||
{%- else %}
|
||||
builtin pwd -L
|
||||
@ -16,9 +18,9 @@ end
|
||||
|
||||
# A copy of fish's internal cd function. This makes it possible to use
|
||||
# `alias cd=z` without causing an infinite loop.
|
||||
if ! builtin functions -q __zoxide_cd_internal
|
||||
if builtin functions -q cd
|
||||
builtin functions -c cd __zoxide_cd_internal
|
||||
if ! builtin functions --query __zoxide_cd_internal
|
||||
if builtin functions --query cd
|
||||
builtin functions --copy cd __zoxide_cd_internal
|
||||
else
|
||||
alias __zoxide_cd_internal='builtin cd'
|
||||
end
|
||||
@ -26,7 +28,11 @@ end
|
||||
|
||||
# cd + custom logic based on the value of _ZO_ECHO.
|
||||
function __zoxide_cd
|
||||
{%- if cfg!(windows) %}
|
||||
__zoxide_cd_internal (cygpath -u $argv)
|
||||
{%- else %}
|
||||
__zoxide_cd_internal $argv
|
||||
{%- endif %}
|
||||
{%- if echo %}
|
||||
and __zoxide_pwd
|
||||
{%- endif %}
|
||||
@ -53,68 +59,67 @@ end
|
||||
{%- endif %}
|
||||
|
||||
{{ section }}
|
||||
# When using zoxide with --no-aliases, alias these internal functions as
|
||||
# desired.
|
||||
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||
#
|
||||
|
||||
if test -z $__zoxide_z_prefix
|
||||
set __zoxide_z_prefix 'z!'
|
||||
end
|
||||
set __zoxide_z_prefix_regex ^(string escape --style=regex $__zoxide_z_prefix)
|
||||
|
||||
# Jump to a directory using only keywords.
|
||||
function __zoxide_z
|
||||
set argc (count $argv)
|
||||
set -l argc (count $argv)
|
||||
if test $argc -eq 0
|
||||
__zoxide_cd $HOME
|
||||
else if test "$argv" = -
|
||||
__zoxide_cd -
|
||||
else if test $argc -eq 1 -a -d $argv[1]
|
||||
__zoxide_cd $argv[1]
|
||||
else if set -l result (string replace --regex $__zoxide_z_prefix_regex '' $argv[-1]); and test -n $result
|
||||
__zoxide_cd $result
|
||||
else
|
||||
set -l result (command zoxide query --exclude (__zoxide_pwd) -- $argv)
|
||||
and __zoxide_cd $result
|
||||
end
|
||||
end
|
||||
|
||||
# Completions for `z`.
|
||||
# Completions.
|
||||
function __zoxide_z_complete
|
||||
set -l tokens (commandline --current-process --tokenize)
|
||||
set -l curr_tokens (commandline --cut-at-cursor --current-process --tokenize)
|
||||
|
||||
if test (count $tokens) -le 2 -a (count $curr_tokens) -eq 1
|
||||
# If there are < 2 arguments, use `cd` completions.
|
||||
__fish_complete_directories "$tokens[2]" ''
|
||||
else if test (count $tokens) -eq (count $curr_tokens)
|
||||
# If the last argument is empty, use interactive selection.
|
||||
complete --do-complete "'' "(commandline --cut-at-cursor --current-token) | string match --regex '.*/$'
|
||||
else if test (count $tokens) -eq (count $curr_tokens); and ! string match --quiet --regex $__zoxide_z_prefix_regex. $tokens[-1]
|
||||
# If the last argument is empty and the one before doesn't start with
|
||||
# $__zoxide_z_prefix, use interactive selection.
|
||||
set -l query $tokens[2..-1]
|
||||
set -l result (zoxide query -i -- $query)
|
||||
commandline --current-process "$tokens[1] "(string escape $result)
|
||||
set -l result (zoxide query --exclude (__zoxide_pwd) --interactive -- $query)
|
||||
and echo $__zoxide_z_prefix$result
|
||||
commandline --function repaint
|
||||
end
|
||||
end
|
||||
complete --command __zoxide_z --no-files --arguments '(__zoxide_z_complete)'
|
||||
|
||||
# Jump to a directory using interactive search.
|
||||
function __zoxide_zi
|
||||
set -l result (command zoxide query -i -- $argv)
|
||||
set -l result (command zoxide query --interactive -- $argv)
|
||||
and __zoxide_cd $result
|
||||
end
|
||||
|
||||
{{ section }}
|
||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
||||
# Commands for zoxide. Disable these using --no-cmd.
|
||||
#
|
||||
|
||||
{%- match cmd %}
|
||||
{%- when Some with (cmd) %}
|
||||
|
||||
# Remove definitions.
|
||||
function __zoxide_unset
|
||||
set --erase $argv >/dev/null 2>&1
|
||||
abbr --erase $argv >/dev/null 2>&1
|
||||
builtin functions --erase $argv >/dev/null 2>&1
|
||||
end
|
||||
|
||||
__zoxide_unset {{cmd}}
|
||||
abbr --erase {{cmd}} &>/dev/null
|
||||
alias {{cmd}}=__zoxide_z
|
||||
complete -c {{cmd}} -e
|
||||
complete -c {{cmd}} -f -a '(__zoxide_z_complete)'
|
||||
|
||||
__zoxide_unset {{cmd}}i
|
||||
abbr --erase {{cmd}}i &>/dev/null
|
||||
alias {{cmd}}i=__zoxide_zi
|
||||
|
||||
{%- when None %}
|
||||
@ -127,4 +132,4 @@ alias {{cmd}}i=__zoxide_zi
|
||||
# To initialize zoxide, add this to your configuration (usually
|
||||
# ~/.config/fish/config.fish):
|
||||
#
|
||||
# zoxide init fish | source
|
||||
# zoxide init fish | source
|
||||
|
@ -3,90 +3,65 @@
|
||||
|
||||
# Code generated by zoxide. DO NOT EDIT.
|
||||
|
||||
{{ section }}
|
||||
# Utility functions for zoxide.
|
||||
#
|
||||
|
||||
# Default prompt for Nushell.
|
||||
def __zoxide_prompt [] {
|
||||
let git = $'(do -i {git rev-parse --abbrev-ref HEAD} | str trim -rc (char newline))'
|
||||
let git = (if ($git | str length) == 0 { '' } {
|
||||
build-string (char lparen) (ansi cb) $git (ansi reset) (char rparen)
|
||||
})
|
||||
build-string (ansi gb) (pwd) (ansi reset) $git '> '
|
||||
}
|
||||
|
||||
{{ section }}
|
||||
# Hook configuration for zoxide.
|
||||
#
|
||||
|
||||
# Hook to add new entries to the database.
|
||||
{%- match hook %}
|
||||
{%- when InitHook::None %}
|
||||
{% if hook == InitHook::None -%}
|
||||
{{ not_configured }}
|
||||
|
||||
{%- when InitHook::Prompt %}
|
||||
def __zoxide_hook [] {
|
||||
shells | where active == $true && name == filesystem | get path | each {
|
||||
zoxide add -- $it
|
||||
}
|
||||
{%- else -%}
|
||||
# Initialize hook to add new entries to the database.
|
||||
if (not ($env | default false __zoxide_hooked | get __zoxide_hooked)) {
|
||||
$env.__zoxide_hooked = true
|
||||
{%- if hook == InitHook::Prompt %}
|
||||
$env.config = ($env | default {} config).config
|
||||
$env.config = ($env.config | default {} hooks)
|
||||
$env.config = ($env.config | update hooks ($env.config.hooks | default [] pre_prompt))
|
||||
$env.config = ($env.config | update hooks.pre_prompt ($env.config.hooks.pre_prompt | append { ||
|
||||
zoxide add -- $env.PWD
|
||||
}))
|
||||
{%- else if hook == InitHook::Pwd %}
|
||||
$env.config = ($env | default {} config).config
|
||||
$env.config = ($env.config | default {} hooks)
|
||||
$env.config = ($env.config | update hooks ($env.config.hooks | default {} env_change))
|
||||
$env.config = ($env.config | update hooks.env_change ($env.config.hooks.env_change | default [] PWD))
|
||||
$env.config = ($env.config | update hooks.env_change.PWD ($env.config.hooks.env_change.PWD | append {|_, dir|
|
||||
zoxide add -- $dir
|
||||
}))
|
||||
{%- endif %}
|
||||
}
|
||||
|
||||
# Initialize hook.
|
||||
let-env PROMPT_COMMAND = (
|
||||
let prompt = (if ($nu.env | select PROMPT_COMMAND | empty?) {
|
||||
if ($nu.config | select prompt | empty?) { '__zoxide_prompt' } { $nu.config.prompt }
|
||||
} { $nu.env.PROMPT_COMMAND });
|
||||
if ($prompt | str contains '__zoxide_hook') { $prompt } { $'__zoxide_hook;($prompt)' }
|
||||
)
|
||||
|
||||
{%- when InitHook::Pwd %}
|
||||
$'zoxide: PWD hooks are not supported on Nushell.(char nl)Use (char sq)zoxide init nushell --hook prompt(char sq) instead.(char nl)'
|
||||
{%- endmatch %}
|
||||
{%- endif %}
|
||||
|
||||
{{ section }}
|
||||
# When using zoxide with --no-aliases, alias these internal functions as
|
||||
# desired.
|
||||
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||
#
|
||||
|
||||
# Jump to a directory using only keywords.
|
||||
def __zoxide_z [...rest:string] {
|
||||
if (shells | where active == $true | get name) != filesystem {
|
||||
if ($rest | length) > 1 {
|
||||
$'zoxide: can only jump directories on filesystem(char nl)'
|
||||
} {
|
||||
cd $rest
|
||||
def-env __zoxide_z [...rest:string] {
|
||||
let arg0 = ($rest | append '~').0
|
||||
let path = if (($rest | length) <= 1) and ($arg0 == '-' or ($arg0 | path expand | path type) == dir) {
|
||||
$arg0
|
||||
} else {
|
||||
(zoxide query --exclude $env.PWD -- $rest | str trim -r -c "\n")
|
||||
}
|
||||
cd $path
|
||||
{%- if echo %}
|
||||
pwd
|
||||
echo $env.PWD
|
||||
{%- endif %}
|
||||
}
|
||||
} {
|
||||
let arg0 = ($rest | append '~' | first 1);
|
||||
if ($rest | length) <= 1 && ($arg0 == '-' || ($arg0 | path expand | path exists)) {
|
||||
cd $arg0
|
||||
} {
|
||||
cd $'(zoxide query --exclude (pwd) -- $rest | str trim -rc (char newline))'
|
||||
}
|
||||
{%- if echo %}
|
||||
pwd
|
||||
{%- endif %}
|
||||
}
|
||||
}
|
||||
|
||||
# Jump to a directory using interactive search.
|
||||
def __zoxide_zi [...rest:string] {
|
||||
if (shells | where active == $true | get name) != filesystem {
|
||||
$'zoxide: can only jump directories on filesystem(char nl)'
|
||||
} {
|
||||
cd $'(zoxide query -i -- $rest | str trim -rc (char newline))'
|
||||
def-env __zoxide_zi [...rest:string] {
|
||||
cd $'(zoxide query --interactive -- $rest | str trim -r -c "\n")'
|
||||
{%- if echo %}
|
||||
pwd
|
||||
echo $env.PWD
|
||||
{%- endif %}
|
||||
}
|
||||
}
|
||||
|
||||
{{ section }}
|
||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
||||
# Commands for zoxide. Disable these using --no-cmd.
|
||||
#
|
||||
|
||||
{%- match cmd %}
|
||||
@ -102,9 +77,13 @@ alias {{cmd}}i = __zoxide_zi
|
||||
{%- endmatch %}
|
||||
|
||||
{{ section }}
|
||||
# To initialize zoxide, add this to your configuration (find it by running
|
||||
# `config path` in Nushell):
|
||||
# Add this to your env file (find it by running `$nu.env-path` in Nushell):
|
||||
#
|
||||
# startup = ['zoxide init nushell --hook prompt | save ~/.zoxide.nu', 'source ~/.zoxide.nu']
|
||||
# zoxide init nushell | save -f ~/.zoxide.nu
|
||||
#
|
||||
# Note: zoxide only supports Nushell v0.37.0 and above.
|
||||
# Now, add this to the end of your config file (find it by running
|
||||
# `$nu.config-path` in Nushell):
|
||||
#
|
||||
# source ~/.zoxide.nu
|
||||
#
|
||||
# Note: zoxide only supports Nushell v0.73.0 and above.
|
||||
|
@ -7,7 +7,9 @@
|
||||
|
||||
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
||||
__zoxide_pwd() {
|
||||
{%- if resolve_symlinks %}
|
||||
{%- if cfg!(windows) %}
|
||||
\command cygpath -w "$(\builtin pwd -P)"
|
||||
{%- else if resolve_symlinks %}
|
||||
\command pwd -P
|
||||
{%- else %}
|
||||
\command pwd -L
|
||||
@ -47,8 +49,7 @@ fi
|
||||
{%- endmatch %}
|
||||
|
||||
{{ section }}
|
||||
# When using zoxide with --no-aliases, alias these internal functions as
|
||||
# desired.
|
||||
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||
#
|
||||
|
||||
# Jump to a directory using only keywords.
|
||||
@ -73,30 +74,22 @@ __zoxide_z() {
|
||||
|
||||
# Jump to a directory using interactive search.
|
||||
__zoxide_zi() {
|
||||
__zoxide_result="$(\command zoxide query -i -- "$@")" && __zoxide_cd "${__zoxide_result}"
|
||||
__zoxide_result="$(\command zoxide query --interactive -- "$@")" && __zoxide_cd "${__zoxide_result}"
|
||||
}
|
||||
|
||||
{{ section }}
|
||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
||||
# Commands for zoxide. Disable these using --no-cmd.
|
||||
#
|
||||
|
||||
{%- match cmd %}
|
||||
{%- when Some with (cmd) %}
|
||||
|
||||
# Remove definitions.
|
||||
__zoxide_unset() {
|
||||
\command unset -f "$@" >/dev/null 2>&1
|
||||
\command unset -v "$@" >/dev/null 2>&1
|
||||
# shellcheck disable=SC1001
|
||||
\command unalias "$@" >/dev/null 2>&1 || \:
|
||||
}
|
||||
|
||||
__zoxide_unset '{{cmd}}'
|
||||
\command unalias {{cmd}} >/dev/null 2>&1 || \true
|
||||
{{cmd}}() {
|
||||
__zoxide_z "$@"
|
||||
}
|
||||
|
||||
__zoxide_unset '{{cmd}}i'
|
||||
\command unalias {{cmd}}i >/dev/null 2>&1 || \true
|
||||
{{cmd}}i() {
|
||||
__zoxide_zi "$@"
|
||||
}
|
||||
|
@ -5,8 +5,20 @@
|
||||
# Utility functions for zoxide.
|
||||
#
|
||||
|
||||
# Call zoxide binary, returning the output as UTF-8.
|
||||
function global:__zoxide_bin {
|
||||
$encoding = [Console]::OutputEncoding
|
||||
try {
|
||||
[Console]::OutputEncoding = [System.Text.Utf8Encoding]::new()
|
||||
$result = zoxide @args
|
||||
return $result
|
||||
} finally {
|
||||
[Console]::OutputEncoding = $encoding
|
||||
}
|
||||
}
|
||||
|
||||
# pwd based on zoxide's format.
|
||||
function __zoxide_pwd {
|
||||
function global:__zoxide_pwd {
|
||||
$cwd = Get-Location
|
||||
if ($cwd.Provider.Name -eq "FileSystem") {
|
||||
$cwd.ProviderPath
|
||||
@ -14,11 +26,19 @@ function __zoxide_pwd {
|
||||
}
|
||||
|
||||
# cd + custom logic based on the value of _ZO_ECHO.
|
||||
function __zoxide_cd($dir, $literal) {
|
||||
function global:__zoxide_cd($dir, $literal) {
|
||||
$dir = if ($literal) {
|
||||
Set-Location -LiteralPath $dir -Passthru -ErrorAction Stop
|
||||
} else {
|
||||
Set-Location -Path $dir -Passthru -ErrorAction Stop
|
||||
if ($dir -eq '-' -and ($PSVersionTable.PSVersion -lt 6.1)) {
|
||||
Write-Error "cd - is not supported below PowerShell 6.1. Please upgrade your version of PowerShell."
|
||||
}
|
||||
elseif ($dir -eq '+' -and ($PSVersionTable.PSVersion -lt 6.2)) {
|
||||
Write-Error "cd + is not supported below PowerShell 6.2. Please upgrade your version of PowerShell."
|
||||
}
|
||||
else {
|
||||
Set-Location -Path $dir -Passthru -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
{%- if echo %}
|
||||
Write-Output $dir.Path
|
||||
@ -29,66 +49,73 @@ function __zoxide_cd($dir, $literal) {
|
||||
# Hook configuration for zoxide.
|
||||
#
|
||||
|
||||
{% if hook == InitHook::None -%}
|
||||
{{ not_configured }}
|
||||
|
||||
{%- else -%}
|
||||
{#-
|
||||
Initialize $__zoxide_hooked if it does not exist. Removing this will cause an
|
||||
unset variable error in StrictMode.
|
||||
-#}
|
||||
{%- if hook == InitHook::Prompt -%}
|
||||
# Hook to add new entries to the database.
|
||||
function __zoxide_hook {
|
||||
function global:__zoxide_hook {
|
||||
$result = __zoxide_pwd
|
||||
if ($null -ne $result) {
|
||||
zoxide add -- $result
|
||||
}
|
||||
}
|
||||
{%- else if hook == InitHook::Pwd -%}
|
||||
# Hook to add new entries to the database.
|
||||
$global:__zoxide_oldpwd = __zoxide_pwd
|
||||
function global:__zoxide_hook {
|
||||
$result = __zoxide_pwd
|
||||
if ($result -ne $global:__zoxide_oldpwd) {
|
||||
if ($null -ne $result) {
|
||||
zoxide add -- $result
|
||||
}
|
||||
$global:__zoxide_oldpwd = $result
|
||||
}
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
# Initialize hook.
|
||||
{# Initialize $__zoxide_hooked if it does not exist. Removing this will cause
|
||||
# an unset variable error in StrictMode. #}
|
||||
$__zoxide_hooked = (Get-Variable __zoxide_hooked -ValueOnly -ErrorAction SilentlyContinue)
|
||||
if ($__zoxide_hooked -ne 1) {
|
||||
$__zoxide_hooked = 1
|
||||
{%- match hook %}
|
||||
{%- when InitHook::None %}
|
||||
{{ not_configured }}
|
||||
{%- when InitHook::Prompt %}
|
||||
$prompt_old = $function:prompt
|
||||
function prompt {
|
||||
$null = __zoxide_hook
|
||||
& $prompt_old
|
||||
}
|
||||
{%- when InitHook::Pwd %}
|
||||
if ($PSVersionTable.PSVersion.Major -ge 6) {
|
||||
$ExecutionContext.InvokeCommand.LocationChangedAction = {
|
||||
$null = __zoxide_hook
|
||||
$global:__zoxide_hooked = (Get-Variable __zoxide_hooked -ErrorAction SilentlyContinue -ValueOnly)
|
||||
if ($global:__zoxide_hooked -ne 1) {
|
||||
$global:__zoxide_hooked = 1
|
||||
$global:__zoxide_prompt_old = $function:prompt
|
||||
|
||||
function global:prompt {
|
||||
if ($null -ne $__zoxide_prompt_old) {
|
||||
& $__zoxide_prompt_old
|
||||
}
|
||||
$null = __zoxide_hook
|
||||
}
|
||||
else {
|
||||
Write-Error ("`n" +
|
||||
"zoxide: PWD hooks are not supported below powershell 6.`n" +
|
||||
" Use 'zoxide init powershell --hook prompt' instead.")
|
||||
}
|
||||
{%- endmatch %}
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
{{ section }}
|
||||
# When using zoxide with --no-aliases, alias these internal functions as
|
||||
# desired.
|
||||
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||
#
|
||||
|
||||
# Jump to a directory using only keywords.
|
||||
function __zoxide_z {
|
||||
function global:__zoxide_z {
|
||||
if ($args.Length -eq 0) {
|
||||
__zoxide_cd ~ $true
|
||||
}
|
||||
elseif (
|
||||
$args.Length -eq 1 -and
|
||||
(($args[0] -eq '-' -or $args[0] -eq '+') -or (Test-Path $args[0] -PathType Container))
|
||||
) {
|
||||
elseif ($args.Length -eq 1 -and ($args[0] -eq '-' -or $args[0] -eq '+')) {
|
||||
__zoxide_cd $args[0] $false
|
||||
}
|
||||
elseif ($args.Length -eq 1 -and (Test-Path $args[0] -PathType Container)) {
|
||||
__zoxide_cd $args[0] $true
|
||||
}
|
||||
else {
|
||||
$result = __zoxide_pwd
|
||||
if ($null -ne $result) {
|
||||
$result = zoxide query --exclude $result -- @args
|
||||
$result = __zoxide_bin query --exclude $result -- @args
|
||||
}
|
||||
else {
|
||||
$result = zoxide query -- @args
|
||||
$result = __zoxide_bin query -- @args
|
||||
}
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
__zoxide_cd $result $true
|
||||
@ -97,15 +124,15 @@ function __zoxide_z {
|
||||
}
|
||||
|
||||
# Jump to a directory using interactive search.
|
||||
function __zoxide_zi {
|
||||
$result = zoxide query -i -- @args
|
||||
function global:__zoxide_zi {
|
||||
$result = __zoxide_bin query -i -- @args
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
__zoxide_cd $result $true
|
||||
}
|
||||
}
|
||||
|
||||
{{ section }}
|
||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
||||
# Commands for zoxide. Disable these using --no-cmd.
|
||||
#
|
||||
|
||||
{%- match cmd %}
|
||||
@ -124,4 +151,4 @@ Set-Alias -Name {{cmd}}i -Value __zoxide_zi -Option AllScope -Scope Global -Forc
|
||||
# To initialize zoxide, add this to your configuration (find it by running
|
||||
# `echo $profile` in PowerShell):
|
||||
#
|
||||
# Invoke-Expression (& { $hook = if ($PSVersionTable.PSVersion.Major -ge 6) { 'pwd' } else { 'prompt' } (zoxide init powershell --hook $hook | Out-String) })
|
||||
# Invoke-Expression (& { (zoxide init powershell | Out-String) })
|
||||
|
@ -108,8 +108,7 @@ if "__zoxide_hook" not in globals():
|
||||
{% endif %}
|
||||
|
||||
{{ section }}
|
||||
# When using zoxide with --no-aliases, alias these internal functions as
|
||||
# desired.
|
||||
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||
#
|
||||
|
||||
|
||||
@ -157,7 +156,7 @@ def __zoxide_zi(args: typing.List[str]) -> None:
|
||||
|
||||
|
||||
{{ section }}
|
||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
||||
# Commands for zoxide. Disable these using --no-cmd.
|
||||
#
|
||||
|
||||
{%- match cmd %}
|
||||
|
@ -7,7 +7,9 @@
|
||||
|
||||
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
||||
function __zoxide_pwd() {
|
||||
{%- if resolve_symlinks %}
|
||||
{%- if cfg!(windows) %}
|
||||
\command cygpath -w "$(\builtin pwd -P)"
|
||||
{%- else if resolve_symlinks %}
|
||||
\builtin pwd -P
|
||||
{%- else %}
|
||||
\builtin pwd -L
|
||||
@ -30,7 +32,8 @@ function __zoxide_cd() {
|
||||
{% else -%}
|
||||
# Hook to add new entries to the database.
|
||||
function __zoxide_hook() {
|
||||
\command zoxide add -- "$(__zoxide_pwd || \builtin true)"
|
||||
# shellcheck disable=SC2312
|
||||
\command zoxide add -- "$(__zoxide_pwd)"
|
||||
}
|
||||
|
||||
# Initialize hook.
|
||||
@ -46,8 +49,7 @@ fi
|
||||
{%- endif %}
|
||||
|
||||
{{ section }}
|
||||
# When using zoxide with --no-aliases, alias these internal functions as
|
||||
# desired.
|
||||
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||
#
|
||||
|
||||
__zoxide_z_prefix='z#'
|
||||
@ -57,23 +59,16 @@ function __zoxide_z() {
|
||||
# shellcheck disable=SC2199
|
||||
if [[ "$#" -eq 0 ]]; then
|
||||
__zoxide_cd ~
|
||||
elif [[ "$#" -eq 1 ]] && [[ "$1" = '-' ]]; then
|
||||
if [[ -n "${OLDPWD}" ]]; then
|
||||
__zoxide_cd "${OLDPWD}"
|
||||
else
|
||||
# shellcheck disable=SC2016
|
||||
\builtin printf 'zoxide: $OLDPWD is not set'
|
||||
return 1
|
||||
fi
|
||||
elif [[ "$#" -eq 1 ]] && [[ -d "$1" ]]; then
|
||||
elif [[ "$#" -eq 1 ]] && { [[ -d "$1" ]] || [[ "$1" = '-' ]] || [[ "$1" =~ ^[-+][0-9]$ ]]; }; then
|
||||
__zoxide_cd "$1"
|
||||
elif [[ "$@[-1]" == "${__zoxide_z_prefix}"* ]]; then
|
||||
elif [[ "$@[-1]" == "${__zoxide_z_prefix}"?* ]]; then
|
||||
# shellcheck disable=SC2124
|
||||
\builtin local result="${@[-1]}"
|
||||
__zoxide_cd "{{ "${result:${#__zoxide_z_prefix}}" }}"
|
||||
else
|
||||
\builtin local result
|
||||
result="$(\command zoxide query --exclude "$(__zoxide_pwd || \builtin true)" -- "$@")" &&
|
||||
# shellcheck disable=SC2312
|
||||
result="$(\command zoxide query --exclude "$(__zoxide_pwd)" -- "$@")" &&
|
||||
__zoxide_cd "${result}"
|
||||
fi
|
||||
}
|
||||
@ -81,69 +76,44 @@ function __zoxide_z() {
|
||||
# Jump to a directory using interactive search.
|
||||
function __zoxide_zi() {
|
||||
\builtin local result
|
||||
result="$(\command zoxide query -i -- "$@")" && __zoxide_cd "${result}"
|
||||
result="$(\command zoxide query --interactive -- "$@")" && __zoxide_cd "${result}"
|
||||
}
|
||||
|
||||
# Completions.
|
||||
if [[ -o zle ]]; then
|
||||
function __zoxide_z_complete() {
|
||||
# Only show completions when the cursor is at the end of the line.
|
||||
# shellcheck disable=SC2154
|
||||
[[ "{{ "${#words[@]}" }}" -eq "${CURRENT}" ]] || return 0
|
||||
|
||||
if [[ "{{ "${#words[@]}" }}" -eq 2 ]]; then
|
||||
_files -/
|
||||
elif [[ "${words[-1]}" == '' ]] && [[ "${words[-2]}" != "${__zoxide_z_prefix}"?* ]]; then
|
||||
\builtin local result
|
||||
# shellcheck disable=SC2086,SC2312
|
||||
if result="$(\command zoxide query --exclude "$(__zoxide_pwd)" --interactive -- ${words[2,-1]})"; then
|
||||
result="${__zoxide_z_prefix}${result}"
|
||||
# shellcheck disable=SC2296
|
||||
compadd -Q "${(q-)result}"
|
||||
fi
|
||||
\builtin printf '\e[5n'
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
\builtin bindkey '\e[0n' 'reset-prompt'
|
||||
[[ "${+functions[compdef]}" -ne 0 ]] && \compdef __zoxide_z_complete __zoxide_z
|
||||
fi
|
||||
|
||||
{{ section }}
|
||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
||||
# Commands for zoxide. Disable these using --no-cmd.
|
||||
#
|
||||
|
||||
{%- match cmd %}
|
||||
{%- when Some with (cmd) %}
|
||||
|
||||
# Remove definitions.
|
||||
function __zoxide_unset() {
|
||||
\builtin unalias "$@" &>/dev/null || \builtin true
|
||||
\builtin unfunction "$@" &>/dev/null || \builtin true
|
||||
\builtin unset "$@" &>/dev/null
|
||||
}
|
||||
|
||||
__zoxide_unset {{cmd}}
|
||||
function {{cmd}}() {
|
||||
__zoxide_z "$@"
|
||||
}
|
||||
|
||||
__zoxide_unset {{cmd}}i
|
||||
function {{cmd}}i() {
|
||||
__zoxide_zi "$@"
|
||||
}
|
||||
|
||||
if [[ -o zle ]]; then
|
||||
__zoxide_unset __zoxide_z_complete
|
||||
function __zoxide_z_complete() {
|
||||
# Only show completions when the cursor is at the end of the line.
|
||||
# shellcheck disable=SC2154
|
||||
[[ "{{ "${#words[@]}" }}" -eq "${CURRENT}" ]] || return
|
||||
|
||||
if [[ "{{ "${#words[@]}" }}" -eq 2 ]]; then
|
||||
_files -/
|
||||
elif [[ "${words[-1]}" == '' ]]; then
|
||||
\builtin local result
|
||||
# shellcheck disable=SC2086
|
||||
if result="$(\command zoxide query -i -- ${words[2,-1]})"; then
|
||||
__zoxide_result="${result}"
|
||||
else
|
||||
__zoxide_result=''
|
||||
fi
|
||||
\builtin printf '\e[5n'
|
||||
fi
|
||||
}
|
||||
|
||||
__zoxide_unset __zoxide_z_complete_helper
|
||||
function __zoxide_z_complete_helper() {
|
||||
\builtin local result="${__zoxide_z_prefix}${__zoxide_result}"
|
||||
# shellcheck disable=SC2296
|
||||
[[ -n "${__zoxide_result}" ]] && LBUFFER="${LBUFFER}${(q-)result}"
|
||||
\builtin zle reset-prompt
|
||||
}
|
||||
|
||||
\builtin zle -N __zoxide_z_complete_helper
|
||||
\builtin bindkey "\e[0n" __zoxide_z_complete_helper
|
||||
if [[ "${+functions[compdef]}" -ne 0 ]]; then
|
||||
\compdef -d {{cmd}}
|
||||
\compdef __zoxide_z_complete {{cmd}}
|
||||
fi
|
||||
fi
|
||||
\builtin alias {{cmd}}=__zoxide_z
|
||||
\builtin alias {{cmd}}i=__zoxide_zi
|
||||
|
||||
{%- when None %}
|
||||
|
||||
|
@ -6,12 +6,17 @@ use assert_cmd::Command;
|
||||
#[test]
|
||||
fn completions_bash() {
|
||||
let source = include_str!("../contrib/completions/zoxide.bash");
|
||||
Command::new("bash").args(&["--noprofile", "--norc", "-c", source]).assert().success().stdout("").stderr("");
|
||||
Command::new("bash")
|
||||
.args(["--noprofile", "--norc", "-c", source])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("")
|
||||
.stderr("");
|
||||
}
|
||||
|
||||
// Elvish: the completions file uses editor commands to add completions to the shell. However,
|
||||
// Elvish does not support running editor commands from a script, so we can't create a test for
|
||||
// this. See: https://github.com/elves/elvish/issues/1299
|
||||
// Elvish: the completions file uses editor commands to add completions to the
|
||||
// shell. However, Elvish does not support running editor commands from a
|
||||
// script, so we can't create a test for this. See: https://github.com/elves/elvish/issues/1299
|
||||
|
||||
#[test]
|
||||
fn completions_fish() {
|
||||
@ -21,7 +26,7 @@ fn completions_fish() {
|
||||
|
||||
Command::new("fish")
|
||||
.env("HOME", tempdir)
|
||||
.args(&["--command", source, "--private"])
|
||||
.args(["--command", source, "--private"])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("")
|
||||
@ -32,7 +37,7 @@ fn completions_fish() {
|
||||
fn completions_powershell() {
|
||||
let source = include_str!("../contrib/completions/_zoxide.ps1");
|
||||
Command::new("pwsh")
|
||||
.args(&["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", source])
|
||||
.args(["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", source])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout("")
|
||||
@ -50,5 +55,5 @@ fn completions_zsh() {
|
||||
compinit -u
|
||||
"#;
|
||||
|
||||
Command::new("zsh").args(&["-c", source, "--no-rcs"]).assert().success().stdout("").stderr("");
|
||||
Command::new("zsh").args(["-c", source, "--no-rcs"]).assert().success().stdout("").stderr("");
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.32"
|
||||
clap = { version = "3.1.0", features = ["derive"] }
|
||||
ignore = "0.4.18"
|
||||
shell-words = "1.0.0"
|
@ -1,154 +0,0 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::Parser;
|
||||
use ignore::Walk;
|
||||
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{self, Command};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let nix_enabled = enable_nix();
|
||||
|
||||
let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let dir = dir.parent().with_context(|| format!("could not find workspace root: {}", dir.display()))?;
|
||||
env::set_current_dir(dir).with_context(|| format!("could not set current directory: {}", dir.display()))?;
|
||||
|
||||
let app = App::parse();
|
||||
match app {
|
||||
App::CI => run_ci(nix_enabled)?,
|
||||
App::Fmt { check } => run_fmt(nix_enabled, check)?,
|
||||
App::Lint => run_lint(nix_enabled)?,
|
||||
App::Test { name } => run_tests(nix_enabled, &name)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
enum App {
|
||||
CI,
|
||||
Fmt {
|
||||
#[clap(long)]
|
||||
check: bool,
|
||||
},
|
||||
Lint,
|
||||
Test {
|
||||
#[clap(default_value = "")]
|
||||
name: String,
|
||||
},
|
||||
}
|
||||
|
||||
trait CommandExt {
|
||||
fn _run(self) -> Result<()>;
|
||||
}
|
||||
|
||||
impl CommandExt for &mut Command {
|
||||
fn _run(self) -> Result<()> {
|
||||
println!(">>> {:?}", self);
|
||||
let status = self.status().with_context(|| format!("command failed to start: {:?}", self))?;
|
||||
if !status.success() {
|
||||
bail!("command failed: {:?} with status: {:?}", self, status);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn run_ci(nix_enabled: bool) -> Result<()> {
|
||||
let color: &[&str] = if is_ci() { &["--color=always"] } else { &[] };
|
||||
Command::new("cargo").args(&["check", "--all-features"]).args(color)._run()?;
|
||||
|
||||
run_fmt(nix_enabled, true)?;
|
||||
run_lint(nix_enabled)?;
|
||||
run_tests(nix_enabled, "")
|
||||
}
|
||||
|
||||
fn run_fmt(nix_enabled: bool, check: bool) -> Result<()> {
|
||||
// Run cargo-fmt.
|
||||
let color: &[&str] = if is_ci() { &["--color=always"] } else { &[] };
|
||||
let check_args: &[&str] = if check { &["--check", "--files-with-diff"] } else { &[] };
|
||||
Command::new("cargo").args(&["fmt", "--all", "--"]).args(color).args(check_args)._run()?;
|
||||
|
||||
// Run nixfmt.
|
||||
if nix_enabled {
|
||||
for result in Walk::new("./") {
|
||||
let entry = result.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_file() && path.extension() == Some(OsStr::new("nix")) {
|
||||
let check_args: &[&str] = if check { &["--check"] } else { &[] };
|
||||
Command::new("nixfmt").args(check_args).arg("--").arg(path)._run()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_lint(nix_enabled: bool) -> Result<()> {
|
||||
// Run cargo-clippy.
|
||||
let color: &[&str] = if is_ci() { &["--color=always"] } else { &[] };
|
||||
Command::new("cargo")
|
||||
.args(&["clippy", "--all-features", "--all-targets"])
|
||||
.args(color)
|
||||
.args(&["--", "-Dwarnings"])
|
||||
._run()?;
|
||||
|
||||
if nix_enabled {
|
||||
// Run cargo-audit.
|
||||
let color: &[&str] = if is_ci() { &["--color=always"] } else { &[] };
|
||||
Command::new("cargo").args(&["audit", "--deny=warnings"]).args(color)._run()?;
|
||||
|
||||
// Run markdownlint.
|
||||
for result in Walk::new("./") {
|
||||
let entry = result.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_file() && path.extension() == Some(OsStr::new("md")) {
|
||||
Command::new("markdownlint").arg(path)._run()?;
|
||||
}
|
||||
}
|
||||
|
||||
// Run mandoc with linting enabled.
|
||||
for result in Walk::new("./man/") {
|
||||
let entry = result.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_file() && path.extension() == Some(OsStr::new("1")) {
|
||||
Command::new("mandoc").args(&["-man", "-Wall", "-Tlint", "--"]).arg(path)._run()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_tests(nix_enabled: bool, name: &str) -> Result<()> {
|
||||
let color: &[&str] = if is_ci() { &["--color=always"] } else { &[] };
|
||||
let features: &[&str] = if nix_enabled { &["--all-features"] } else { &[] };
|
||||
Command::new("cargo").args(&["test", "--no-fail-fast", "--workspace"]).args(color).args(features).arg(name)._run()
|
||||
}
|
||||
|
||||
fn is_ci() -> bool {
|
||||
env::var_os("CI").is_some()
|
||||
}
|
||||
|
||||
fn enable_nix() -> bool {
|
||||
let nix_supported = cfg!(any(target_os = "linux", target_os = "macos"));
|
||||
if !nix_supported {
|
||||
return false;
|
||||
}
|
||||
let nix_enabled = env::var_os("IN_NIX_SHELL").unwrap_or_default() == "pure";
|
||||
if nix_enabled {
|
||||
return true;
|
||||
}
|
||||
let nix_detected = Command::new("nix-shell").arg("--version").status().map(|s| s.success()).unwrap_or(false);
|
||||
if !nix_detected {
|
||||
return false;
|
||||
}
|
||||
|
||||
println!("Detected Nix in environment, re-running in Nix.");
|
||||
let args = env::args();
|
||||
let cmd = shell_words::join(args);
|
||||
let mut nix_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
nix_path.push("../shell.nix");
|
||||
let status = Command::new("nix-shell").args(&["--pure", "--run", &cmd, "--"]).arg(nix_path).status().unwrap();
|
||||
process::exit(status.code().unwrap_or(1));
|
||||
}
|
Loading…
Reference in New Issue
Block a user