mirror of
https://github.com/Llewellynvdm/zoxide.git
synced 2024-06-14 05:02:21 +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]
|
[alias]
|
||||||
xtask = "run --package xtask --"
|
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]]
|
[[analyzers]]
|
||||||
name = "rust"
|
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
|
Examples of behavior that contributes to a positive environment for our
|
||||||
community include:
|
community include:
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
- Demonstrating empathy and kindness toward other people
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
* Giving and gracefully accepting constructive feedback
|
- Giving and gracefully accepting constructive feedback
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
and learning from the experience
|
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
|
overall community
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
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
|
advances of any kind
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or email
|
- Publishing others' private information, such as a physical or email
|
||||||
address, without their explicit permission
|
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
|
professional setting
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
## Enforcement Responsibilities
|
||||||
|
@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
|
||||||
### 4. Permanent Ban
|
### 4. Permanent Ban
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
**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.
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
**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]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
env:
|
||||||
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
name: ${{ matrix.os }}
|
name: ${{ matrix.os }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
if: ${{ matrix.os == 'windows-latest' }}
|
if: ${{ matrix.os == 'windows-latest' }}
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
components: clippy
|
||||||
components: rustfmt, clippy
|
|
||||||
profile: minimal
|
profile: minimal
|
||||||
override: true
|
toolchain: stable
|
||||||
- uses: cachix/install-nix-action@v15
|
- 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' }}
|
if: ${{ matrix.os != 'windows-latest' }}
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixpkgs-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
- uses: cachix/cachix-action@v12
|
||||||
- run: cargo xtask ci
|
if: ${{ matrix.os != 'windows-latest' && env.CACHIX_AUTH_TOKEN != '' }}
|
||||||
if: ${{ matrix.os == 'windows-latest' }}
|
with:
|
||||||
- run: nix-shell --cores 0 --pure --run 'rm -rf ~/.cargo/bin; cargo xtask ci'
|
authToken: ${{ env.CACHIX_AUTH_TOKEN }}
|
||||||
if: ${{ matrix.os != 'windows-latest' }}
|
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
|
name: release
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
|
||||||
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
env:
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: ${{ matrix.target }}
|
name: ${{ matrix.target }}
|
||||||
|
@ -16,38 +16,33 @@ jobs:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-musl
|
target: x86_64-unknown-linux-musl
|
||||||
|
deb: true
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: arm-unknown-linux-musleabihf
|
target: arm-unknown-linux-musleabihf
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: armv7-unknown-linux-musleabihf
|
target: armv7-unknown-linux-musleabihf
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
target: aarch64-unknown-linux-musl
|
target: aarch64-unknown-linux-musl
|
||||||
|
deb: true
|
||||||
- os: macos-11
|
- os: macos-11
|
||||||
target: x86_64-apple-darwin
|
target: x86_64-apple-darwin
|
||||||
- os: macos-11
|
- os: macos-11
|
||||||
target: aarch64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
|
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
target: x86_64-pc-windows-msvc
|
target: x86_64-pc-windows-msvc
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
target: aarch64-pc-windows-msvc
|
target: aarch64-pc-windows-msvc
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
- name: Get version
|
||||||
- name: Set artifact name
|
id: get_version
|
||||||
shell: bash
|
uses: SebRollen/toml-action@v1.0.2
|
||||||
run: |
|
with:
|
||||||
version="$(git describe --tags --match='v*.*.*' --always)"
|
file: Cargo.toml
|
||||||
name="zoxide-$version-${{ matrix.target }}"
|
field: package.version
|
||||||
echo "ARTIFACT_NAME=$name" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
echo "version: $version"
|
|
||||||
echo "artifact: $name"
|
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
|
@ -55,45 +50,58 @@ jobs:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
override: true
|
override: true
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
|
- name: Setup cache
|
||||||
|
uses: Swatinem/rust-cache@v2.7.0
|
||||||
|
with:
|
||||||
|
key: ${{ matrix.target }}
|
||||||
- name: Build binary
|
- name: Build binary
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --release --locked --target=${{ matrix.target }} --color=always --verbose
|
args: --release --locked --target=${{ matrix.target }} --color=always --verbose
|
||||||
use-cross: ${{ runner.os == 'Linux' }}
|
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)
|
- name: Package (*nix)
|
||||||
if: runner.os != 'Windows'
|
if: runner.os != 'Windows'
|
||||||
run: >
|
run: |
|
||||||
tar -cv
|
tar -cv CHANGELOG.md LICENSE README.md man/ \
|
||||||
CHANGELOG.md LICENSE README.md
|
-C contrib/ completions/ -C ../ \
|
||||||
man/
|
-C target/${{ matrix.target }}/release/ zoxide |
|
||||||
-C contrib/ completions/ -C ../
|
gzip --best > \
|
||||||
-C target/${{ matrix.target }}/release/ zoxide
|
zoxide-${{ steps.get_version.outputs.value }}-${{ matrix.target }}.tar.gz
|
||||||
| gzip --best > '${{ env.ARTIFACT_NAME }}.tar.gz'
|
|
||||||
- name: Package (Windows)
|
- name: Package (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: >
|
run: |
|
||||||
7z a ${{ env.ARTIFACT_NAME }}.zip
|
7z a zoxide-${{ steps.get_version.outputs.value }}-${{ matrix.target }}.zip `
|
||||||
CHANGELOG.md LICENSE README.md
|
CHANGELOG.md LICENSE README.md ./man/ ./contrib/completions/ `
|
||||||
./man/
|
./target/${{ matrix.target }}/release/zoxide.exe
|
||||||
./contrib/completions/
|
|
||||||
./target/${{ matrix.target }}/release/zoxide.exe
|
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.target }}
|
name: ${{ matrix.target }}
|
||||||
path: |
|
path: |
|
||||||
*.zip
|
*.deb
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
|
*.zip
|
||||||
- name: Create release
|
- 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
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
files: |
|
files: |
|
||||||
*.zip
|
*.deb
|
||||||
*.tar.gz
|
*.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
|
# Compiled files and executables
|
||||||
debug/
|
debug/
|
||||||
target/
|
target/
|
||||||
|
target_nix/
|
||||||
|
|
||||||
# Backup files generated by rustfmt
|
# Backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.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/),
|
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).
|
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
|
### Changed
|
||||||
|
|
||||||
- Manpages: moved to `man/man1/*.1`.
|
- 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
|
### Fixed
|
||||||
|
|
||||||
- Bash/Zsh: rename `_z` completion function to avoid conflicts with other shell
|
- Bash/Zsh: rename `_z` completion function to avoid conflicts with other shell
|
||||||
plugins.
|
plugins.
|
||||||
- Elvish: upgrade to new lambda syntax.
|
- Fzf: added `--keep-right` option by default, upgrade minimum supported version
|
||||||
- Fzf: added `--keep-right` option by default, upgraded minimum version to
|
to v0.21.0.
|
||||||
v0.21.0.
|
|
||||||
- Bash: only enable completions on 4.4+.
|
- Bash: only enable completions on 4.4+.
|
||||||
- Fzf: bypass `ls` alias in preview window.
|
- Fzf: bypass `ls` alias in preview window.
|
||||||
- Retain ownership of database file.
|
- Retain ownership of database file.
|
||||||
|
- `zoxide query --interactive` should not conflict with `--score`.
|
||||||
|
|
||||||
## [0.8.0] - 2021-12-25
|
## [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
|
### Fixed
|
||||||
|
|
||||||
- PowerShell: Hook not initializing correctly.
|
- PowerShell: hook not initializing correctly.
|
||||||
|
|
||||||
## [0.7.6] - 2021-10-13
|
## [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.
|
- GitHub Actions pipeline to build and upload releases.
|
||||||
- Support for zsh.
|
- 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.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.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
|
[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"]
|
categories = ["command-line-utilities", "filesystem"]
|
||||||
description = "A smarter cd command for your terminal"
|
description = "A smarter cd command for your terminal"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
keywords = ["cli"]
|
homepage = "https://github.com/ajeetdsouza/zoxide"
|
||||||
|
keywords = ["cli", "filesystem", "shell", "tool", "utility"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "zoxide"
|
name = "zoxide"
|
||||||
|
readme = "README.md"
|
||||||
repository = "https://github.com/ajeetdsouza/zoxide"
|
repository = "https://github.com/ajeetdsouza/zoxide"
|
||||||
rust-version = "1.59"
|
rust-version = "1.65"
|
||||||
version = "0.8.0"
|
version = "0.9.2"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[workspace]
|
|
||||||
members = ["xtask/"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.32"
|
anyhow = "1.0.32"
|
||||||
askama = { version = "0.11.0", default-features = false }
|
askama = { version = "0.12.0", default-features = false }
|
||||||
bincode = "1.3.1"
|
bincode = "1.3.1"
|
||||||
clap = { version = "3.1.0", features = ["derive"] }
|
clap = { version = "4.3.0", features = ["derive"] }
|
||||||
dirs = "4.0.0"
|
color-print = "0.3.4"
|
||||||
|
dirs = "5.0.0"
|
||||||
dunce = "1.0.1"
|
dunce = "1.0.1"
|
||||||
fastrand = "1.7.0"
|
fastrand = "2.0.0"
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
ordered-float = "2.0.0"
|
ouroboros = "0.17.2"
|
||||||
serde = { version = "1.0.116", features = ["derive"] }
|
serde = { version = "1.0.116", features = ["derive"] }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[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]
|
[build-dependencies]
|
||||||
clap = { version = "3.1.0", features = ["derive"] }
|
clap = { version = "4.3.0", features = ["derive"] }
|
||||||
clap_complete = "3.1.0"
|
clap_complete = "4.3.0"
|
||||||
clap_complete_fig = "3.1.0"
|
clap_complete_fig = "4.3.0"
|
||||||
|
color-print = "0.3.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "2.0.0"
|
assert_cmd = "2.0.0"
|
||||||
rstest = "0.12.0"
|
rstest = { version = "0.18.0", default-features = false }
|
||||||
rstest_reuse = "0.3.0"
|
rstest_reuse = "0.6.0"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -48,5 +55,56 @@ nix-dev = []
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
debug = 0
|
||||||
lto = true
|
lto = true
|
||||||
strip = 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">
|
<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
|
# zoxide
|
||||||
|
|
||||||
[![crates.io][crates.io-badge]][crates.io]
|
[![crates.io][crates.io-badge]][crates.io]
|
||||||
|
@ -51,240 +65,300 @@ Read more about the matching algorithm [here][algorithm-matching].
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### *Step 1: Install zoxide*
|
zoxide can be installed in 4 easy steps:
|
||||||
|
|
||||||
zoxide runs on most major platforms. If your platform isn't listed below,
|
1. **Install binary**
|
||||||
please [open an issue][issues].
|
|
||||||
|
zoxide runs on most major platforms. If your platform isn't listed below,
|
||||||
<details>
|
please [open an issue][issues].
|
||||||
<summary>Linux</summary>
|
|
||||||
|
<details>
|
||||||
To install zoxide, run this command in your terminal:
|
<summary>Linux</summary>
|
||||||
|
|
||||||
```sh
|
> The recommended way to install zoxide is via the install script:
|
||||||
curl -sS https://webinstall.dev/zoxide | bash
|
>
|
||||||
```
|
> ```sh
|
||||||
|
> curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
|
||||||
Alternatively, you can use a package manager:
|
> ```
|
||||||
|
>
|
||||||
| Distribution | Repository | Instructions |
|
> Or, you can use a package manager:
|
||||||
| ------------------ | ----------------------- | ---------------------------------------------------------------------------------------------- |
|
>
|
||||||
| ***Any*** | **[crates.io]** | `cargo install zoxide --locked` |
|
> | Distribution | Repository | Instructions |
|
||||||
| *Any* | [conda-forge] | `conda install -c conda-forge zoxide` |
|
> | ------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||||
| *Any* | [Linuxbrew] | `brew install zoxide` |
|
> | **_Any_** | **[crates.io]** | `cargo install zoxide --locked` |
|
||||||
| Alpine Linux 3.13+ | [Alpine Linux Packages] | `apk add zoxide` |
|
> | _Any_ | [asdf] | `asdf plugin add zoxide https://github.com/nyrst/asdf-zoxide.git` <br /> `asdf install zoxide latest` |
|
||||||
| Arch Linux | [Arch Linux Community] | `pacman -S zoxide` |
|
> | _Any_ | [conda-forge] | `conda install -c conda-forge zoxide` |
|
||||||
| CentOS 7+ | [Copr] | `dnf copr enable atim/zoxide` <br /> `dnf install zoxide` |
|
> | _Any_ | [Linuxbrew] | `brew install zoxide` |
|
||||||
| Debian 11+ | [Debian Packages] | `apt install zoxide` |
|
> | _Any_ | [nixpkgs] | `nix-env -iA nixpkgs.zoxide` |
|
||||||
| Devuan 4.0+ | [Devuan Packages] | `apt install zoxide` |
|
> | Alpine Linux 3.13+ | [Alpine Linux Packages] | `apk add zoxide` |
|
||||||
| Fedora 32+ | [Fedora Packages] | `dnf install zoxide` |
|
> | Arch Linux | [Arch Linux Extra] | `pacman -S zoxide` |
|
||||||
| Gentoo | [GURU Overlay] | `eselect repository enable guru` <br /> `emerge --sync guru` <br /> `emerge app-shells/zoxide` |
|
> | CentOS 7+ | [Copr] | `dnf copr enable atim/zoxide` <br /> `dnf install zoxide` |
|
||||||
| Manjaro | | `pacman -S zoxide` |
|
> | Debian 11+[^1] | [Debian Packages] | `apt install zoxide` |
|
||||||
| NixOS | [nixpkgs] | `nix-env -iA nixpkgs.zoxide` |
|
> | Devuan 4.0+[^1] | [Devuan Packages] | `apt install zoxide` |
|
||||||
| Parrot OS | | `apt install zoxide` |
|
> | Fedora 32+ | [Fedora Packages] | `dnf install zoxide` |
|
||||||
| Raspbian 11+ | [Raspbian Packages] | `apt install zoxide` |
|
> | Gentoo | [GURU Overlay] | `eselect repository enable guru` <br /> `emerge --sync guru` <br /> `emerge app-shells/zoxide` |
|
||||||
| Ubuntu 21.04+ | [Ubuntu Packages] | `apt install zoxide` |
|
> | Manjaro | | `pacman -S zoxide` |
|
||||||
| Void Linux | [Void Linux Packages] | `xbps-install -S zoxide` |
|
> | openSUSE Tumbleweed | [openSUSE Factory] | `zypper install zoxide` |
|
||||||
|
> | Parrot OS[^1] | | `apt install zoxide` |
|
||||||
</details>
|
> | Raspbian 11+[^1] | [Raspbian Packages] | `apt install zoxide` |
|
||||||
|
> | Rhino Linux | [Pacstall Packages] | `pacstall -I zoxide-deb` |
|
||||||
<details>
|
> | Slackware 15.0+ | [SlackBuilds] | [Instructions][slackbuilds-howto] |
|
||||||
<summary>macOS</summary>
|
> | Ubuntu 21.04+[^1] | [Ubuntu Packages] | `apt install zoxide` |
|
||||||
|
> | Void Linux | [Void Linux Packages] | `xbps-install -S zoxide` |
|
||||||
To install zoxide, use a package manager:
|
|
||||||
|
</details>
|
||||||
| Repository | Instructions |
|
|
||||||
| --------------- | ------------------------------------- |
|
<details>
|
||||||
| **[crates.io]** | `cargo install zoxide --locked` |
|
<summary>macOS</summary>
|
||||||
| [conda-forge] | `conda install -c conda-forge zoxide` |
|
|
||||||
| [Homebrew] | `brew install zoxide` |
|
> To install zoxide, use a package manager:
|
||||||
| [MacPorts] | `port install zoxide` |
|
>
|
||||||
|
> | Repository | Instructions |
|
||||||
</details>
|
> | --------------- | ----------------------------------------------------------------------------------------------------- |
|
||||||
|
> | **[crates.io]** | `cargo install zoxide --locked` |
|
||||||
<details>
|
> | **[Homebrew]** | `brew install zoxide` |
|
||||||
<summary>Windows</summary>
|
> | [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` |
|
||||||
To install zoxide, run this command in your command prompt:
|
> | [MacPorts] | `port install zoxide` |
|
||||||
|
> | [nixpkgs] | `nix-env -iA nixpkgs.zoxide` |
|
||||||
```sh
|
>
|
||||||
curl.exe -A "MS" https://webinstall.dev/zoxide | powershell
|
> Or, run this command in your terminal:
|
||||||
```
|
>
|
||||||
|
> ```sh
|
||||||
Alternatively, you can use a package manager:
|
> curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
|
||||||
|
> ```
|
||||||
| Repository | Instructions |
|
|
||||||
| --------------- | ------------------------------------- |
|
</details>
|
||||||
| **[crates.io]** | `cargo install zoxide --locked` |
|
|
||||||
| [Chocolatey] | `choco install zoxide` |
|
<details>
|
||||||
| [conda-forge] | `conda install -c conda-forge zoxide` |
|
<summary>Windows</summary>
|
||||||
| [Scoop] | `scoop install zoxide` |
|
|
||||||
|
> The recommended way to install zoxide is via `winget`:
|
||||||
</details>
|
>
|
||||||
|
> ```sh
|
||||||
<details>
|
> winget install ajeetdsouza.zoxide
|
||||||
<summary>BSD</summary>
|
> ```
|
||||||
|
>
|
||||||
To install zoxide, use a package manager:
|
> Or, you can use an alternative package manager:
|
||||||
|
>
|
||||||
| Distribution | Repository | Instructions |
|
> | Repository | Instructions |
|
||||||
| ------------- | --------------- | ------------------------------- |
|
> | --------------- | ------------------------------------- |
|
||||||
| ***Any*** | **[crates.io]** | `cargo install zoxide --locked` |
|
> | **[crates.io]** | `cargo install zoxide --locked` |
|
||||||
| DragonFly BSD | [DPorts] | `pkg install zoxide` |
|
> | [Chocolatey] | `choco install zoxide` |
|
||||||
| FreeBSD | [FreshPorts] | `pkg install zoxide` |
|
> | [conda-forge] | `conda install -c conda-forge zoxide` |
|
||||||
| NetBSD | [pkgsrc] | `pkgin install zoxide` |
|
> | [Scoop] | `scoop install zoxide` |
|
||||||
|
>
|
||||||
</details>
|
> If you're using Cygwin, Git Bash, or MSYS2, use the install script instead:
|
||||||
|
>
|
||||||
<details>
|
> ```sh
|
||||||
<summary>Android</summary>
|
> curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
|
||||||
|
> ```
|
||||||
To install zoxide, use a package manager:
|
|
||||||
|
</details>
|
||||||
| Repository | Instructions |
|
|
||||||
| ---------- | -------------------- |
|
<details>
|
||||||
| [Termux] | `pkg install zoxide` |
|
<summary>BSD</summary>
|
||||||
|
|
||||||
</details>
|
> To install zoxide, use a package manager:
|
||||||
|
>
|
||||||
### *Step 2: Add zoxide to your shell*
|
> | Distribution | Repository | Instructions |
|
||||||
|
> | ------------- | --------------- | ------------------------------- |
|
||||||
To start using zoxide, add it to your shell.
|
> | **_Any_** | **[crates.io]** | `cargo install zoxide --locked` |
|
||||||
|
> | DragonFly BSD | [DPorts] | `pkg install zoxide` |
|
||||||
<details>
|
> | FreeBSD | [FreshPorts] | `pkg install zoxide` |
|
||||||
<summary>Bash</summary>
|
> | NetBSD | [pkgsrc] | `pkgin install zoxide` |
|
||||||
|
|
||||||
Add this to your configuration (usually `~/.bashrc`):
|
</details>
|
||||||
|
|
||||||
```sh
|
<details>
|
||||||
eval "$(zoxide init bash)"
|
<summary>Android</summary>
|
||||||
```
|
|
||||||
|
> To install zoxide, use a package manager:
|
||||||
</details>
|
>
|
||||||
|
> | Repository | Instructions |
|
||||||
<details>
|
> | ---------- | -------------------- |
|
||||||
<summary>Elvish</summary>
|
> | [Termux] | `pkg install zoxide` |
|
||||||
|
|
||||||
Add this to your configuration (usually `~/.elvish/rc.elv`):
|
</details>
|
||||||
|
|
||||||
```sh
|
2. **Setup zoxide on your shell**
|
||||||
eval (zoxide init elvish | slurp)
|
|
||||||
```
|
To start using zoxide, add it to your shell.
|
||||||
|
|
||||||
Note: zoxide only supports elvish v0.16.0 and above.
|
<details>
|
||||||
|
<summary>Bash</summary>
|
||||||
</details>
|
|
||||||
|
> Add this to the **end** of your config file (usually `~/.bashrc`):
|
||||||
<details>
|
>
|
||||||
<summary>Fish</summary>
|
> ```sh
|
||||||
|
> eval "$(zoxide init bash)"
|
||||||
Add this to your configuration (usually `~/.config/fish/config.fish`):
|
> ```
|
||||||
|
|
||||||
```fish
|
</details>
|
||||||
zoxide init fish | source
|
|
||||||
```
|
<details>
|
||||||
|
<summary>Elvish</summary>
|
||||||
</details>
|
|
||||||
|
> Add this to the **end** of your config file (usually `~/.elvish/rc.elv`):
|
||||||
<details>
|
>
|
||||||
<summary>Nushell</summary>
|
> ```sh
|
||||||
|
> eval (zoxide init elvish | slurp)
|
||||||
Add this to your configuration (find it by running `config path` in Nushell):
|
> ```
|
||||||
|
>
|
||||||
```toml
|
> **Note**
|
||||||
startup = ["zoxide init nushell --hook prompt | save ~/.zoxide.nu", "source ~/.zoxide.nu"]
|
> zoxide only supports elvish v0.18.0 and above.
|
||||||
```
|
|
||||||
|
</details>
|
||||||
Note: zoxide only supports Nushell v0.37.0 and above.
|
|
||||||
|
<details>
|
||||||
</details>
|
<summary>Fish</summary>
|
||||||
|
|
||||||
<details>
|
> Add this to the **end** of your config file (usually
|
||||||
<summary>PowerShell</summary>
|
> `~/.config/fish/config.fish`):
|
||||||
|
>
|
||||||
Add this to your configuration (find it by running `echo $profile` in
|
> ```fish
|
||||||
PowerShell):
|
> zoxide init fish | source
|
||||||
|
> ```
|
||||||
```powershell
|
|
||||||
# For zoxide v0.8.0+
|
</details>
|
||||||
Invoke-Expression (& {
|
|
||||||
$hook = if ($PSVersionTable.PSVersion.Major -lt 6) { 'prompt' } else { 'pwd' }
|
<details>
|
||||||
(zoxide init --hook $hook powershell | Out-String)
|
<summary>Nushell</summary>
|
||||||
})
|
|
||||||
|
> Add this to the **end** of your env file (find it by running `$nu.env-path`
|
||||||
# For older versions of zoxide
|
> in Nushell):
|
||||||
Invoke-Expression (& {
|
>
|
||||||
$hook = if ($PSVersionTable.PSVersion.Major -lt 6) { 'prompt' } else { 'pwd' }
|
> ```sh
|
||||||
(zoxide init --hook $hook powershell) -join "`n"
|
> zoxide init nushell | save -f ~/.zoxide.nu
|
||||||
})
|
> ```
|
||||||
```
|
>
|
||||||
|
> Now, add this to the **end** of your config file (find it by running
|
||||||
</details>
|
> `$nu.config-path` in Nushell):
|
||||||
|
>
|
||||||
<details>
|
> ```sh
|
||||||
<summary>Xonsh</summary>
|
> source ~/.zoxide.nu
|
||||||
|
> ```
|
||||||
Add this to your configuration (usually `~/.xonshrc`):
|
>
|
||||||
|
> **Note**
|
||||||
```python
|
> zoxide only supports Nushell v0.73.0 and above.
|
||||||
execx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')
|
|
||||||
```
|
</details>
|
||||||
|
|
||||||
</details>
|
<details>
|
||||||
|
<summary>PowerShell</summary>
|
||||||
<details>
|
|
||||||
<summary>Zsh</summary>
|
> Add this to the **end** of your config file (find it by running
|
||||||
|
> `echo $profile` in PowerShell):
|
||||||
Add this to your configuration (usually `~/.zshrc`):
|
>
|
||||||
|
> ```powershell
|
||||||
```sh
|
> Invoke-Expression (& { (zoxide init powershell | Out-String) })
|
||||||
eval "$(zoxide init zsh)"
|
> ```
|
||||||
```
|
|
||||||
|
</details>
|
||||||
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>
|
||||||
|
<summary>Xonsh</summary>
|
||||||
</details>
|
|
||||||
|
> Add this to the **end** of your config file (usually `~/.xonshrc`):
|
||||||
<details>
|
>
|
||||||
<summary>Any POSIX shell</summary>
|
> ```python
|
||||||
|
> execx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')
|
||||||
Add this to your configuration:
|
> ```
|
||||||
|
|
||||||
```sh
|
</details>
|
||||||
eval "$(zoxide init posix --hook prompt)"
|
|
||||||
```
|
<details>
|
||||||
|
<summary>Zsh</summary>
|
||||||
</details>
|
|
||||||
|
> Add this to the **end** of your config file (usually `~/.zshrc`):
|
||||||
### *Step 3: Install fzf (optional)*
|
>
|
||||||
|
> ```sh
|
||||||
[fzf] is a command-line fuzzy finder, used by zoxide for interactive
|
> eval "$(zoxide init zsh)"
|
||||||
selection. It can be installed from [here][fzf-installation]. zoxide supports
|
> ```
|
||||||
fzf v0.21.0+.
|
>
|
||||||
|
> For completions to work, the above line must be added _after_ `compinit` is
|
||||||
### *Step 4: Import your data (optional)*
|
> called. You may have to rebuild your completions cache by running
|
||||||
|
> `rm ~/.zcompdump*; compinit`.
|
||||||
If you currently use any of the following utilities, you may want to import
|
|
||||||
your data into zoxide:
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>autojump</summary>
|
<summary>Any POSIX shell</summary>
|
||||||
|
|
||||||
```sh
|
> Add this to the **end** of your config file:
|
||||||
zoxide import --from autojump path/to/db
|
>
|
||||||
```
|
> ```sh
|
||||||
|
> eval "$(zoxide init posix --hook prompt)"
|
||||||
</details>
|
> ```
|
||||||
|
|
||||||
<details>
|
</details>
|
||||||
<summary>z, z.lua, or zsh-z</summary>
|
|
||||||
|
3. **Install fzf** <sup>(optional)</sup>
|
||||||
```sh
|
|
||||||
zoxide import --from z path/to/db
|
[fzf] is a command-line fuzzy finder, used by zoxide for completions /
|
||||||
```
|
interactive selection. It can be installed from [here][fzf-installation].
|
||||||
|
|
||||||
</details>
|
> **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
|
## Configuration
|
||||||
|
|
||||||
|
@ -293,8 +367,8 @@ zoxide import --from z path/to/db
|
||||||
When calling `zoxide init`, the following flags are available:
|
When calling `zoxide init`, the following flags are available:
|
||||||
|
|
||||||
- `--cmd`
|
- `--cmd`
|
||||||
- Changes the prefix of predefined aliases (`z`, `zi`).
|
- Changes the prefix of the `z` and `zi` commands.
|
||||||
- `--cmd j` would change the aliases to (`j`, `ji`).
|
- `--cmd j` would change the commands to (`j`, `ji`).
|
||||||
- `--cmd cd` would replace the `cd` command (doesn't work on Nushell / POSIX shells).
|
- `--cmd cd` would replace the `cd` command (doesn't work on Nushell / POSIX shells).
|
||||||
- `--hook <HOOK>`
|
- `--hook <HOOK>`
|
||||||
- Changes how often zoxide increments a directory's score:
|
- Changes how often zoxide increments a directory's score:
|
||||||
|
@ -303,15 +377,15 @@ When calling `zoxide init`, the following flags are available:
|
||||||
| `none` | Never |
|
| `none` | Never |
|
||||||
| `prompt` | At every shell prompt |
|
| `prompt` | At every shell prompt |
|
||||||
| `pwd` | Whenever the directory is changed |
|
| `pwd` | Whenever the directory is changed |
|
||||||
- `--no-aliases`
|
- `--no-cmd`
|
||||||
- Don't define aliases (`z`, `zi`).
|
- Prevents zoxide from defining the `z` and `zi` commands.
|
||||||
- These functions will still be available in your shell as `__zoxide_z` and
|
- These functions will still be available in your shell as `__zoxide_z` and
|
||||||
`__zoxide_zi`, should you choose to redefine them.
|
`__zoxide_zi`, should you choose to redefine them.
|
||||||
|
|
||||||
### Environment variables
|
### Environment variables
|
||||||
|
|
||||||
Environment variables<sup>[?][wiki-env]</sup> can be used for configuration.
|
Environment variables[^2] can be used for configuration. They must be set before
|
||||||
They must be set before `zoxide init` is called.
|
`zoxide init` is called.
|
||||||
|
|
||||||
- `_ZO_DATA_DIR`
|
- `_ZO_DATA_DIR`
|
||||||
- Specifies the directory in which the database is stored.
|
- 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
|
## Third-party integrations
|
||||||
|
|
||||||
| Application | Description | Plugin |
|
| Application | Description | Plugin |
|
||||||
| ------------------ | -------------------------------------------- | -------------------------- |
|
| --------------------- | -------------------------------------------- | -------------------------- |
|
||||||
| [clink] | Improved cmd.exe for Windows | [clink-zoxide] |
|
| [aerc] | Email client | Natively supported |
|
||||||
| [emacs] | Text editor | [zoxide.el] |
|
| [clink] | Improved cmd.exe for Windows | [clink-zoxide] |
|
||||||
| [nnn] | File manager | [nnn-autojump] |
|
| [emacs] | Text editor | [zoxide.el] |
|
||||||
| [ranger] | File manager | [ranger-zoxide] |
|
| [felix] | File manager | Natively supported |
|
||||||
| [telescope.nvim] | Fuzzy finder for Neovim | [telescope-zoxide] |
|
| [joshuto] | File manager | Natively supported |
|
||||||
| [vim] | Text editor | [zoxide.vim] |
|
| [lf] | File manager | See the [wiki][lf-wiki] |
|
||||||
| [xplr] | File manager | [zoxide.xplr] |
|
| [nnn] | File manager | [nnn-autojump] |
|
||||||
| [xxh] | Transports shell configuration over SSH | [xxh-plugin-prerun-zoxide] |
|
| [ranger] | File manager | [ranger-zoxide] |
|
||||||
| [zabb] | Finds the shortest possible query for a path | Natively supported |
|
| [telescope.nvim] | Fuzzy finder for Neovim | [telescope-zoxide] |
|
||||||
| [zsh-autocomplete] | Realtime completions for zsh | Natively supported |
|
| [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-aging]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#aging
|
||||||
[algorithm-matching]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#matching
|
[algorithm-matching]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#matching
|
||||||
[alpine linux packages]: https://pkgs.alpinelinux.org/packages?name=zoxide
|
[alpine linux packages]: https://pkgs.alpinelinux.org/packages?name=zoxide
|
||||||
[arch linux community]: https://archlinux.org/packages/community/x86_64/zoxide/
|
[arch linux extra]: https://archlinux.org/packages/extra/x86_64/zoxide/
|
||||||
[builtwithnix-badge]: https://img.shields.io/badge/builtwith-nix-7d81f7?style=flat-square
|
[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/
|
[builtwithnix]: https://builtwithnix.org/
|
||||||
[chocolatey]: https://community.chocolatey.org/packages/zoxide
|
[chocolatey]: https://community.chocolatey.org/packages/zoxide
|
||||||
[clink-zoxide]: https://github.com/shunsambongi/clink-zoxide
|
[clink-zoxide]: https://github.com/shunsambongi/clink-zoxide
|
||||||
[clink]: https://github.com/mridgers/clink
|
[clink]: https://github.com/mridgers/clink
|
||||||
[conda-forge]: https://anaconda.org/conda-forge/zoxide
|
[conda-forge]: https://anaconda.org/conda-forge/zoxide
|
||||||
[copr]: https://copr.fedorainfracloud.org/coprs/atim/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
|
[crates.io]: https://crates.io/crates/zoxide
|
||||||
[debian packages]: https://packages.debian.org/stable/admin/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
|
[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
|
[dports]: https://github.com/DragonFlyBSD/DPorts/tree/master/sysutils/zoxide
|
||||||
[emacs]: https://www.gnu.org/software/emacs/
|
[emacs]: https://www.gnu.org/software/emacs/
|
||||||
[fedora packages]: https://src.fedoraproject.org/rpms/rust-zoxide
|
[fedora packages]: https://src.fedoraproject.org/rpms/rust-zoxide
|
||||||
|
[felix]: https://github.com/kyoheiu/felix
|
||||||
[freshports]: https://www.freshports.org/sysutils/zoxide/
|
[freshports]: https://www.freshports.org/sysutils/zoxide/
|
||||||
[fzf-installation]: https://github.com/junegunn/fzf#installation
|
[fzf-installation]: https://github.com/junegunn/fzf#installation
|
||||||
[fzf-man]: https://manpages.ubuntu.com/manpages/en/man1/fzf.1.html
|
[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
|
[guru overlay]: https://github.com/gentoo-mirror/guru
|
||||||
[homebrew]: https://formulae.brew.sh/formula/zoxide
|
[homebrew]: https://formulae.brew.sh/formula/zoxide
|
||||||
[issues]: https://github.com/ajeetdsouza/zoxide/issues/new
|
[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
|
[linuxbrew]: https://formulae.brew.sh/formula-linux/zoxide
|
||||||
[macports]: https://ports.macports.org/port/zoxide/summary
|
[macports]: https://ports.macports.org/port/zoxide/summary
|
||||||
[neovim]: https://github.com/neovim/neovim
|
[neovim]: https://github.com/neovim/neovim
|
||||||
[nixpkgs]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/misc/zoxide/default.nix
|
[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-autojump]: https://github.com/jarun/nnn/blob/master/plugins/autojump
|
||||||
[nnn]: https://github.com/jarun/nnn
|
[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
|
[pkgsrc]: https://pkgsrc.se/sysutils/zoxide
|
||||||
[ranger-zoxide]: https://github.com/jchook/ranger-zoxide
|
[ranger-zoxide]: https://github.com/jchook/ranger-zoxide
|
||||||
[ranger]: https://github.com/ranger/ranger
|
[ranger]: https://github.com/ranger/ranger
|
||||||
[raspbian packages]: https://archive.raspbian.org/raspbian/pool/main/r/rust-zoxide/
|
[raspbian packages]: https://archive.raspbian.org/raspbian/pool/main/r/rust-zoxide/
|
||||||
[releases]: https://github.com/ajeetdsouza/zoxide/releases
|
[releases]: https://github.com/ajeetdsouza/zoxide/releases
|
||||||
[scoop]: https://github.com/ScoopInstaller/Main/tree/master/bucket/zoxide.json
|
[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-zoxide]: https://github.com/jvgrootveld/telescope-zoxide
|
||||||
[telescope.nvim]: https://github.com/nvim-telescope/telescope.nvim
|
[telescope.nvim]: https://github.com/nvim-telescope/telescope.nvim
|
||||||
[termux]: https://github.com/termux/termux-packages/tree/master/packages/zoxide
|
[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
|
[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
|
[vim]: https://github.com/vim/vim
|
||||||
[void linux packages]: https://github.com/void-linux/void-packages/tree/master/srcpkgs/zoxide
|
[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"
|
[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};
|
use std::{env, io};
|
||||||
|
|
||||||
fn main() {
|
use clap::CommandFactory;
|
||||||
let pkg_version = env!("CARGO_PKG_VERSION");
|
use clap_complete::shells::{Bash, Elvish, Fish, PowerShell, Zsh};
|
||||||
let version = match env::var_os("PROFILE") {
|
use clap_complete_fig::Fig;
|
||||||
Some(profile) if profile == "release" => format!("v{}", pkg_version),
|
use cmd::Cmd;
|
||||||
_ => git_version().unwrap_or_else(|| format!("v{}-unknown", pkg_version)),
|
|
||||||
};
|
|
||||||
println!("cargo:rustc-env=ZOXIDE_VERSION={}", version);
|
|
||||||
|
|
||||||
// Since we are generating completions in the package directory, we need to set this so that
|
fn main() -> io::Result<()> {
|
||||||
// Cargo doesn't rebuild every time.
|
// 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=build.rs");
|
||||||
println!("cargo:rerun-if-changed=src");
|
println!("cargo:rerun-if-changed=src/");
|
||||||
println!("cargo:rerun-if-changed=templates");
|
println!("cargo:rerun-if-changed=templates/");
|
||||||
println!("cargo:rerun-if-changed=tests");
|
println!("cargo:rerun-if-changed=tests/");
|
||||||
|
generate_completions()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_completions() -> io::Result<()> {
|
fn generate_completions() -> io::Result<()> {
|
||||||
#[path = "src/cmd/_cmd.rs"]
|
const BIN_NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
mod cmd;
|
const OUT_DIR: &str = "contrib/completions";
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
let cmd = &mut Cmd::command();
|
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)?;
|
clap_complete::generate_to(Bash, cmd, BIN_NAME, OUT_DIR)?;
|
||||||
generate_to(Elvish, cmd, bin_name, out_dir)?;
|
clap_complete::generate_to(Elvish, cmd, BIN_NAME, OUT_DIR)?;
|
||||||
generate_to(Fig, cmd, bin_name, out_dir)?;
|
clap_complete::generate_to(Fig, cmd, BIN_NAME, OUT_DIR)?;
|
||||||
generate_to(Fish, cmd, bin_name, out_dir)?;
|
clap_complete::generate_to(Fish, cmd, BIN_NAME, OUT_DIR)?;
|
||||||
generate_to(PowerShell, cmd, bin_name, out_dir)?;
|
clap_complete::generate_to(PowerShell, cmd, BIN_NAME, OUT_DIR)?;
|
||||||
generate_to(Zsh, cmd, bin_name, out_dir)?;
|
clap_complete::generate_to(Zsh, cmd, BIN_NAME, OUT_DIR)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
157
contrib/completions/_zoxide
generated
157
contrib/completions/_zoxide
generated
|
@ -15,10 +15,10 @@ _zoxide() {
|
||||||
|
|
||||||
local context curcontext="$curcontext" state line
|
local context curcontext="$curcontext" state line
|
||||||
_arguments "${_arguments_options[@]}" \
|
_arguments "${_arguments_options[@]}" \
|
||||||
'-h[Print help information]' \
|
'-h[Print help]' \
|
||||||
'--help[Print help information]' \
|
'--help[Print help]' \
|
||||||
'-V[Print version information]' \
|
'-V[Print version]' \
|
||||||
'--version[Print version information]' \
|
'--version[Print version]' \
|
||||||
":: :_zoxide_commands" \
|
":: :_zoxide_commands" \
|
||||||
"*::: :->zoxide" \
|
"*::: :->zoxide" \
|
||||||
&& ret=0
|
&& ret=0
|
||||||
|
@ -30,61 +30,115 @@ _zoxide() {
|
||||||
case $line[1] in
|
case $line[1] in
|
||||||
(add)
|
(add)
|
||||||
_arguments "${_arguments_options[@]}" \
|
_arguments "${_arguments_options[@]}" \
|
||||||
'-h[Print help information]' \
|
'-h[Print help]' \
|
||||||
'--help[Print help information]' \
|
'--help[Print help]' \
|
||||||
'-V[Print version information]' \
|
'-V[Print version]' \
|
||||||
'--version[Print version information]' \
|
'--version[Print version]' \
|
||||||
'*::paths:_files -/' \
|
'*::paths:_files -/' \
|
||||||
&& ret=0
|
&& 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)
|
(import)
|
||||||
_arguments "${_arguments_options[@]}" \
|
_arguments "${_arguments_options[@]}" \
|
||||||
'--from=[Application to import from]:FROM:(autojump z)' \
|
'--from=[Application to import from]:FROM:(autojump z)' \
|
||||||
'--merge[Merge into existing database]' \
|
'--merge[Merge into existing database]' \
|
||||||
'-h[Print help information]' \
|
'-h[Print help]' \
|
||||||
'--help[Print help information]' \
|
'--help[Print help]' \
|
||||||
'-V[Print version information]' \
|
'-V[Print version]' \
|
||||||
'--version[Print version information]' \
|
'--version[Print version]' \
|
||||||
':path:_files' \
|
':path:_files' \
|
||||||
&& ret=0
|
&& ret=0
|
||||||
;;
|
;;
|
||||||
(init)
|
(init)
|
||||||
_arguments "${_arguments_options[@]}" \
|
_arguments "${_arguments_options[@]}" \
|
||||||
'--cmd=[Renames the '\''z'\'' command and corresponding aliases]:CMD: ' \
|
'--cmd=[Changes the prefix of the \`z\` and \`zi\` commands]:CMD: ' \
|
||||||
'--hook=[Chooses event upon which an entry is added to the database]:HOOK:(none prompt pwd)' \
|
'--hook=[Changes how often zoxide increments a directory'\''s score]:HOOK:(none prompt pwd)' \
|
||||||
'--no-aliases[Prevents zoxide from defining any commands]' \
|
'--no-cmd[Prevents zoxide from defining the \`z\` and \`zi\` commands]' \
|
||||||
'-h[Print help information]' \
|
'-h[Print help]' \
|
||||||
'--help[Print help information]' \
|
'--help[Print help]' \
|
||||||
'-V[Print version information]' \
|
'-V[Print version]' \
|
||||||
'--version[Print version information]' \
|
'--version[Print version]' \
|
||||||
':shell:(bash elvish fish nushell posix powershell xonsh zsh)' \
|
':shell:(bash elvish fish nushell posix powershell xonsh zsh)' \
|
||||||
&& ret=0
|
&& ret=0
|
||||||
;;
|
;;
|
||||||
(query)
|
(query)
|
||||||
_arguments "${_arguments_options[@]}" \
|
_arguments "${_arguments_options[@]}" \
|
||||||
'--exclude=[Exclude a path from results]:path:_files -/' \
|
'--exclude=[Exclude the current directory]:path:_files -/' \
|
||||||
'--all[Show deleted directories]' \
|
'-a[Show unavailable directories]' \
|
||||||
|
'--all[Show unavailable directories]' \
|
||||||
'(-l --list)-i[Use interactive selection]' \
|
'(-l --list)-i[Use interactive selection]' \
|
||||||
'(-l --list)--interactive[Use interactive selection]' \
|
'(-l --list)--interactive[Use interactive selection]' \
|
||||||
'(-i --interactive)-l[List all matching directories]' \
|
'(-i --interactive)-l[List all matching directories]' \
|
||||||
'(-i --interactive)--list[List all matching directories]' \
|
'(-i --interactive)--list[List all matching directories]' \
|
||||||
'(-i --interactive)-s[Print score with results]' \
|
'-s[Print score with results]' \
|
||||||
'(-i --interactive)--score[Print score with results]' \
|
'--score[Print score with results]' \
|
||||||
'-h[Print help information]' \
|
'-h[Print help]' \
|
||||||
'--help[Print help information]' \
|
'--help[Print help]' \
|
||||||
'-V[Print version information]' \
|
'-V[Print version]' \
|
||||||
'--version[Print version information]' \
|
'--version[Print version]' \
|
||||||
'*::keywords:' \
|
'*::keywords:' \
|
||||||
&& ret=0
|
&& ret=0
|
||||||
;;
|
;;
|
||||||
(remove)
|
(remove)
|
||||||
_arguments "${_arguments_options[@]}" \
|
_arguments "${_arguments_options[@]}" \
|
||||||
'-i[Use interactive selection]' \
|
'-h[Print help]' \
|
||||||
'--interactive[Use interactive selection]' \
|
'--help[Print help]' \
|
||||||
'-h[Print help information]' \
|
'-V[Print version]' \
|
||||||
'--help[Print help information]' \
|
'--version[Print version]' \
|
||||||
'-V[Print version information]' \
|
|
||||||
'--version[Print version information]' \
|
|
||||||
'*::paths:_files -/' \
|
'*::paths:_files -/' \
|
||||||
&& ret=0
|
&& ret=0
|
||||||
;;
|
;;
|
||||||
|
@ -97,6 +151,7 @@ esac
|
||||||
_zoxide_commands() {
|
_zoxide_commands() {
|
||||||
local commands; commands=(
|
local commands; commands=(
|
||||||
'add:Add a new directory or increment its rank' \
|
'add:Add a new directory or increment its rank' \
|
||||||
|
'edit:Edit the database' \
|
||||||
'import:Import entries from another application' \
|
'import:Import entries from another application' \
|
||||||
'init:Generate shell configuration' \
|
'init:Generate shell configuration' \
|
||||||
'query:Search for a directory in the database' \
|
'query:Search for a directory in the database' \
|
||||||
|
@ -109,11 +164,36 @@ _zoxide__add_commands() {
|
||||||
local commands; commands=()
|
local commands; commands=()
|
||||||
_describe -t commands 'zoxide add 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] )) ||
|
(( $+functions[_zoxide__import_commands] )) ||
|
||||||
_zoxide__import_commands() {
|
_zoxide__import_commands() {
|
||||||
local commands; commands=()
|
local commands; commands=()
|
||||||
_describe -t commands 'zoxide import 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] )) ||
|
(( $+functions[_zoxide__init_commands] )) ||
|
||||||
_zoxide__init_commands() {
|
_zoxide__init_commands() {
|
||||||
local commands; commands=()
|
local commands; commands=()
|
||||||
|
@ -124,10 +204,19 @@ _zoxide__query_commands() {
|
||||||
local commands; commands=()
|
local commands; commands=()
|
||||||
_describe -t commands 'zoxide query 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] )) ||
|
(( $+functions[_zoxide__remove_commands] )) ||
|
||||||
_zoxide__remove_commands() {
|
_zoxide__remove_commands() {
|
||||||
local commands; commands=()
|
local commands; commands=()
|
||||||
_describe -t commands 'zoxide remove 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) {
|
$completions = @(switch ($command) {
|
||||||
'zoxide' {
|
'zoxide' {
|
||||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Add a new directory or increment its rank')
|
[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('import', 'import', [CompletionResultType]::ParameterValue, 'Import entries from another application')
|
||||||
[CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Generate shell configuration')
|
[CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Generate shell configuration')
|
||||||
[CompletionResult]::new('query', 'query', [CompletionResultType]::ParameterValue, 'Search for a directory in the database')
|
[CompletionResult]::new('query', 'query', [CompletionResultType]::ParameterValue, 'Search for a directory in the database')
|
||||||
|
@ -33,53 +34,91 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
'zoxide;add' {
|
'zoxide;add' {
|
||||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
[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
|
break
|
||||||
}
|
}
|
||||||
'zoxide;import' {
|
'zoxide;import' {
|
||||||
[CompletionResult]::new('--from', 'from', [CompletionResultType]::ParameterName, 'Application to import from')
|
[CompletionResult]::new('--from', 'from', [CompletionResultType]::ParameterName, 'Application to import from')
|
||||||
[CompletionResult]::new('--merge', 'merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
|
[CompletionResult]::new('--merge', 'merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
|
||||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
'zoxide;init' {
|
'zoxide;init' {
|
||||||
[CompletionResult]::new('--cmd', 'cmd', [CompletionResultType]::ParameterName, 'Renames the ''z'' command and corresponding aliases')
|
[CompletionResult]::new('--cmd', 'cmd', [CompletionResultType]::ParameterName, 'Changes the prefix of the `z` and `zi` commands')
|
||||||
[CompletionResult]::new('--hook', 'hook', [CompletionResultType]::ParameterName, 'Chooses event upon which an entry is added to the database')
|
[CompletionResult]::new('--hook', 'hook', [CompletionResultType]::ParameterName, 'Changes how often zoxide increments a directory''s score')
|
||||||
[CompletionResult]::new('--no-aliases', 'no-aliases', [CompletionResultType]::ParameterName, 'Prevents zoxide from defining any commands')
|
[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 information')
|
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
'zoxide;query' {
|
'zoxide;query' {
|
||||||
[CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'Exclude a path from results')
|
[CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'Exclude the current directory')
|
||||||
[CompletionResult]::new('--all', 'all', [CompletionResultType]::ParameterName, 'Show deleted directories')
|
[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('-i', 'i', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
||||||
[CompletionResult]::new('--interactive', 'interactive', [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('-l', 'l', [CompletionResultType]::ParameterName, 'List all matching directories')
|
||||||
[CompletionResult]::new('--list', 'list', [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('-s', 's', [CompletionResultType]::ParameterName, 'Print score with results')
|
||||||
[CompletionResult]::new('--score', 'score', [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('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
'zoxide;remove' {
|
'zoxide;remove' {
|
||||||
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('--interactive', 'interactive', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
||||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
|
||||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
123
contrib/completions/zoxide.bash
generated
123
contrib/completions/zoxide.bash
generated
|
@ -1,5 +1,5 @@
|
||||||
_zoxide() {
|
_zoxide() {
|
||||||
local i cur prev opts cmds
|
local i cur prev opts cmd
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
@ -8,24 +8,39 @@ _zoxide() {
|
||||||
|
|
||||||
for i in ${COMP_WORDS[@]}
|
for i in ${COMP_WORDS[@]}
|
||||||
do
|
do
|
||||||
case "${i}" in
|
case "${cmd},${i}" in
|
||||||
"$1")
|
",$1")
|
||||||
cmd="zoxide"
|
cmd="zoxide"
|
||||||
;;
|
;;
|
||||||
add)
|
zoxide,add)
|
||||||
cmd+="__add"
|
cmd="zoxide__add"
|
||||||
;;
|
;;
|
||||||
import)
|
zoxide,edit)
|
||||||
cmd+="__import"
|
cmd="zoxide__edit"
|
||||||
;;
|
;;
|
||||||
init)
|
zoxide,import)
|
||||||
cmd+="__init"
|
cmd="zoxide__import"
|
||||||
;;
|
;;
|
||||||
query)
|
zoxide,init)
|
||||||
cmd+="__query"
|
cmd="zoxide__init"
|
||||||
;;
|
;;
|
||||||
remove)
|
zoxide,query)
|
||||||
cmd+="__remove"
|
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
|
case "${cmd}" in
|
||||||
zoxide)
|
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
|
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
|
@ -61,6 +76,76 @@ _zoxide() {
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
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)
|
zoxide__import)
|
||||||
opts="-h -V --from --merge --help --version <PATH>"
|
opts="-h -V --from --merge --help --version <PATH>"
|
||||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||||
|
@ -69,7 +154,7 @@ _zoxide() {
|
||||||
fi
|
fi
|
||||||
case "${prev}" in
|
case "${prev}" in
|
||||||
--from)
|
--from)
|
||||||
COMPREPLY=($(compgen -W "" -- "${cur}"))
|
COMPREPLY=($(compgen -W "autojump z" -- "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
@ -80,7 +165,7 @@ _zoxide() {
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
zoxide__init)
|
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
|
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
|
@ -91,7 +176,7 @@ _zoxide() {
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
--hook)
|
--hook)
|
||||||
COMPREPLY=($(compgen -W "" -- "${cur}"))
|
COMPREPLY=($(compgen -W "none prompt pwd" -- "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
@ -102,7 +187,7 @@ _zoxide() {
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
zoxide__query)
|
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
|
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
|
@ -120,7 +205,7 @@ _zoxide() {
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
zoxide__remove)
|
zoxide__remove)
|
||||||
opts="-i -h -V --interactive --help --version <PATHS>..."
|
opts="-h -V --help --version [PATHS]..."
|
||||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
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 = [
|
var completions = [
|
||||||
&'zoxide'= {
|
&'zoxide'= {
|
||||||
cand -h 'Print help information'
|
cand -h 'Print help'
|
||||||
cand --help 'Print help information'
|
cand --help 'Print help'
|
||||||
cand -V 'Print version information'
|
cand -V 'Print version'
|
||||||
cand --version 'Print version information'
|
cand --version 'Print version'
|
||||||
cand add 'Add a new directory or increment its rank'
|
cand add 'Add a new directory or increment its rank'
|
||||||
|
cand edit 'Edit the database'
|
||||||
cand import 'Import entries from another application'
|
cand import 'Import entries from another application'
|
||||||
cand init 'Generate shell configuration'
|
cand init 'Generate shell configuration'
|
||||||
cand query 'Search for a directory in the database'
|
cand query 'Search for a directory in the database'
|
||||||
cand remove 'Remove a directory from the database'
|
cand remove 'Remove a directory from the database'
|
||||||
}
|
}
|
||||||
&'zoxide;add'= {
|
&'zoxide;add'= {
|
||||||
cand -h 'Print help information'
|
cand -h 'Print help'
|
||||||
cand --help 'Print help information'
|
cand --help 'Print help'
|
||||||
cand -V 'Print version information'
|
cand -V 'Print version'
|
||||||
cand --version 'Print version information'
|
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'= {
|
&'zoxide;import'= {
|
||||||
cand --from 'Application to import from'
|
cand --from 'Application to import from'
|
||||||
cand --merge 'Merge into existing database'
|
cand --merge 'Merge into existing database'
|
||||||
cand -h 'Print help information'
|
cand -h 'Print help'
|
||||||
cand --help 'Print help information'
|
cand --help 'Print help'
|
||||||
cand -V 'Print version information'
|
cand -V 'Print version'
|
||||||
cand --version 'Print version information'
|
cand --version 'Print version'
|
||||||
}
|
}
|
||||||
&'zoxide;init'= {
|
&'zoxide;init'= {
|
||||||
cand --cmd 'Renames the ''z'' command and corresponding aliases'
|
cand --cmd 'Changes the prefix of the `z` and `zi` commands'
|
||||||
cand --hook 'Chooses event upon which an entry is added to the database'
|
cand --hook 'Changes how often zoxide increments a directory''s score'
|
||||||
cand --no-aliases 'Prevents zoxide from defining any commands'
|
cand --no-cmd 'Prevents zoxide from defining the `z` and `zi` commands'
|
||||||
cand -h 'Print help information'
|
cand -h 'Print help'
|
||||||
cand --help 'Print help information'
|
cand --help 'Print help'
|
||||||
cand -V 'Print version information'
|
cand -V 'Print version'
|
||||||
cand --version 'Print version information'
|
cand --version 'Print version'
|
||||||
}
|
}
|
||||||
&'zoxide;query'= {
|
&'zoxide;query'= {
|
||||||
cand --exclude 'Exclude a path from results'
|
cand --exclude 'Exclude the current directory'
|
||||||
cand --all 'Show deleted directories'
|
cand -a 'Show unavailable directories'
|
||||||
|
cand --all 'Show unavailable directories'
|
||||||
cand -i 'Use interactive selection'
|
cand -i 'Use interactive selection'
|
||||||
cand --interactive 'Use interactive selection'
|
cand --interactive 'Use interactive selection'
|
||||||
cand -l 'List all matching directories'
|
cand -l 'List all matching directories'
|
||||||
cand --list 'List all matching directories'
|
cand --list 'List all matching directories'
|
||||||
cand -s 'Print score with results'
|
cand -s 'Print score with results'
|
||||||
cand --score 'Print score with results'
|
cand --score 'Print score with results'
|
||||||
cand -h 'Print help information'
|
cand -h 'Print help'
|
||||||
cand --help 'Print help information'
|
cand --help 'Print help'
|
||||||
cand -V 'Print version information'
|
cand -V 'Print version'
|
||||||
cand --version 'Print version information'
|
cand --version 'Print version'
|
||||||
}
|
}
|
||||||
&'zoxide;remove'= {
|
&'zoxide;remove'= {
|
||||||
cand -i 'Use interactive selection'
|
cand -h 'Print help'
|
||||||
cand --interactive 'Use interactive selection'
|
cand --help 'Print help'
|
||||||
cand -h 'Print help information'
|
cand -V 'Print version'
|
||||||
cand --help 'Print help information'
|
cand --version 'Print version'
|
||||||
cand -V 'Print version information'
|
|
||||||
cand --version 'Print version information'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
$completions[$command]
|
$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 h -l help -d 'Print help'
|
||||||
complete -c zoxide -n "__fish_use_subcommand" -s V -l version -d 'Print version information'
|
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 "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 "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 "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 "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_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 h -l help -d 'Print help'
|
||||||
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 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 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" -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 h -l help -d 'Print help'
|
||||||
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 import" -s V -l version -d 'Print version'
|
||||||
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 cmd -d 'Changes the prefix of the `z` and `zi` commands' -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 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-aliases -d 'Prevents zoxide from defining any commands'
|
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 information'
|
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 information'
|
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 a path from results' -r -f -a "(__fish_complete_directories)"
|
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" -l all -d 'Show deleted 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 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 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 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 h -l help -d 'Print help'
|
||||||
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 query" -s V -l version -d 'Print version'
|
||||||
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'
|
||||||
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'
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from remove" -s V -l version -d 'Print version information'
|
|
||||||
|
|
195
contrib/completions/zoxide.ts
generated
195
contrib/completions/zoxide.ts
generated
|
@ -8,11 +8,11 @@ const completion: Fig.Spec = {
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: ["-h", "--help"],
|
name: ["-h", "--help"],
|
||||||
description: "Print help information",
|
description: "Print help",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-V", "--version"],
|
name: ["-V", "--version"],
|
||||||
description: "Print version information",
|
description: "Print version",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
args: {
|
args: {
|
||||||
|
@ -21,6 +21,87 @@ const completion: Fig.Spec = {
|
||||||
template: "folders",
|
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",
|
name: "import",
|
||||||
description: "Import entries from another application",
|
description: "Import entries from another application",
|
||||||
|
@ -28,16 +109,13 @@ const completion: Fig.Spec = {
|
||||||
{
|
{
|
||||||
name: "--from",
|
name: "--from",
|
||||||
description: "Application to import from",
|
description: "Application to import from",
|
||||||
|
isRepeatable: true,
|
||||||
args: {
|
args: {
|
||||||
name: "from",
|
name: "from",
|
||||||
suggestions: [
|
suggestions: [
|
||||||
{
|
"autojump",
|
||||||
name: "autojump",
|
"z",
|
||||||
},
|
],
|
||||||
{
|
|
||||||
name: "z",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -46,11 +124,11 @@ const completion: Fig.Spec = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-h", "--help"],
|
name: ["-h", "--help"],
|
||||||
description: "Print help information",
|
description: "Print help",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-V", "--version"],
|
name: ["-V", "--version"],
|
||||||
description: "Print version information",
|
description: "Print version",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
args: {
|
args: {
|
||||||
|
@ -64,7 +142,8 @@ const completion: Fig.Spec = {
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: "--cmd",
|
name: "--cmd",
|
||||||
description: "Renames the 'z' command and corresponding aliases",
|
description: "Changes the prefix of the `z` and `zi` commands",
|
||||||
|
isRepeatable: true,
|
||||||
args: {
|
args: {
|
||||||
name: "cmd",
|
name: "cmd",
|
||||||
isOptional: true,
|
isOptional: true,
|
||||||
|
@ -72,64 +151,43 @@ const completion: Fig.Spec = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "--hook",
|
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: {
|
args: {
|
||||||
name: "hook",
|
name: "hook",
|
||||||
isOptional: true,
|
isOptional: true,
|
||||||
suggestions: [
|
suggestions: [
|
||||||
{
|
"none",
|
||||||
name: "none",
|
"prompt",
|
||||||
},
|
"pwd",
|
||||||
{
|
],
|
||||||
name: "prompt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "pwd",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "--no-aliases",
|
name: "--no-cmd",
|
||||||
description: "Prevents zoxide from defining any commands",
|
description: "Prevents zoxide from defining the `z` and `zi` commands",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-h", "--help"],
|
name: ["-h", "--help"],
|
||||||
description: "Print help information",
|
description: "Print help",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-V", "--version"],
|
name: ["-V", "--version"],
|
||||||
description: "Print version information",
|
description: "Print version",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
args: {
|
args: {
|
||||||
name: "shell",
|
name: "shell",
|
||||||
suggestions: [
|
suggestions: [
|
||||||
{
|
"bash",
|
||||||
name: "bash",
|
"elvish",
|
||||||
},
|
"fish",
|
||||||
{
|
"nushell",
|
||||||
name: "elvish",
|
"posix",
|
||||||
},
|
"powershell",
|
||||||
{
|
"xonsh",
|
||||||
name: "fish",
|
"zsh",
|
||||||
},
|
],
|
||||||
{
|
|
||||||
name: "nushell",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "posix",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "powershell",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "xonsh",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "zsh",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -138,7 +196,8 @@ const completion: Fig.Spec = {
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: "--exclude",
|
name: "--exclude",
|
||||||
description: "Exclude a path from results",
|
description: "Exclude the current directory",
|
||||||
|
isRepeatable: true,
|
||||||
args: {
|
args: {
|
||||||
name: "exclude",
|
name: "exclude",
|
||||||
isOptional: true,
|
isOptional: true,
|
||||||
|
@ -146,16 +205,24 @@ const completion: Fig.Spec = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "--all",
|
name: ["-a", "--all"],
|
||||||
description: "Show deleted directories",
|
description: "Show unavailable directories",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-i", "--interactive"],
|
name: ["-i", "--interactive"],
|
||||||
description: "Use interactive selection",
|
description: "Use interactive selection",
|
||||||
|
exclusiveOn: [
|
||||||
|
"-l",
|
||||||
|
"--list",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-l", "--list"],
|
name: ["-l", "--list"],
|
||||||
description: "List all matching directories",
|
description: "List all matching directories",
|
||||||
|
exclusiveOn: [
|
||||||
|
"-i",
|
||||||
|
"--interactive",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-s", "--score"],
|
name: ["-s", "--score"],
|
||||||
|
@ -163,15 +230,16 @@ const completion: Fig.Spec = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-h", "--help"],
|
name: ["-h", "--help"],
|
||||||
description: "Print help information",
|
description: "Print help",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-V", "--version"],
|
name: ["-V", "--version"],
|
||||||
description: "Print version information",
|
description: "Print version",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
args: {
|
args: {
|
||||||
name: "keywords",
|
name: "keywords",
|
||||||
|
isVariadic: true,
|
||||||
isOptional: true,
|
isOptional: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -179,21 +247,18 @@ const completion: Fig.Spec = {
|
||||||
name: "remove",
|
name: "remove",
|
||||||
description: "Remove a directory from the database",
|
description: "Remove a directory from the database",
|
||||||
options: [
|
options: [
|
||||||
{
|
|
||||||
name: ["-i", "--interactive"],
|
|
||||||
description: "Use interactive selection",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: ["-h", "--help"],
|
name: ["-h", "--help"],
|
||||||
description: "Print help information",
|
description: "Print help",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-V", "--version"],
|
name: ["-V", "--version"],
|
||||||
description: "Print version information",
|
description: "Print version",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
args: {
|
args: {
|
||||||
name: "paths",
|
name: "paths",
|
||||||
|
isVariadic: true,
|
||||||
isOptional: true,
|
isOptional: true,
|
||||||
template: "folders",
|
template: "folders",
|
||||||
},
|
},
|
||||||
|
@ -202,11 +267,11 @@ const completion: Fig.Spec = {
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: ["-h", "--help"],
|
name: ["-h", "--help"],
|
||||||
description: "Print help information",
|
description: "Print help",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ["-V", "--version"],
|
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
|
.SH REPORTING BUGS
|
||||||
For any issues, feature requests, or questions, please visit:
|
For any issues, feature requests, or questions, please visit:
|
||||||
.sp
|
.sp
|
||||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||||
.SH AUTHOR
|
.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(|);
|
tab(|);
|
||||||
l l.
|
l l.
|
||||||
\fBautojump\fR
|
\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
|
.TE
|
||||||
.sp
|
.sp
|
||||||
Note: zoxide only imports paths from autojump, since its matching
|
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
|
.SH REPORTING BUGS
|
||||||
For any issues, feature requests, or questions, please visit:
|
For any issues, feature requests, or questions, please visit:
|
||||||
.sp
|
.sp
|
||||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||||
.SH AUTHOR
|
.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:
|
To initialize zoxide on your shell:
|
||||||
.TP
|
.TP
|
||||||
.B bash
|
.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
|
.sp
|
||||||
.nf
|
.nf
|
||||||
\fBeval "$(zoxide init bash)"\fR
|
\fBeval "$(zoxide init bash)"\fR
|
||||||
.fi
|
.fi
|
||||||
.TP
|
.TP
|
||||||
.B elvish
|
.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
|
.sp
|
||||||
.nf
|
.nf
|
||||||
\fBeval $(zoxide init elvish | slurp)\fR
|
\fBeval $(zoxide init elvish | slurp)\fR
|
||||||
.fi
|
.fi
|
||||||
.sp
|
.sp
|
||||||
Note: zoxide only supports elvish v0.16.0 and above.
|
Note: zoxide only supports elvish v0.18.0 and above.
|
||||||
.TP
|
.TP
|
||||||
.B fish
|
.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
|
.sp
|
||||||
.nf
|
.nf
|
||||||
\fBzoxide init fish | source\fR
|
\fBzoxide init fish | source\fR
|
||||||
.fi
|
.fi
|
||||||
.TP
|
.TP
|
||||||
.B nushell
|
.B nushell
|
||||||
Add this to your configuration (find it by running \fBconfig path\fR in
|
Add this to the \fBend\fR of your env file (find it by running
|
||||||
Nushell):
|
\fB$nu.env-path\fR in Nushell):
|
||||||
.sp
|
.sp
|
||||||
.nf
|
.nf
|
||||||
\fBstartup = ["zoxide init nushell --hook prompt | save ~/.zoxide.nu", "source ~/.zoxide.nu"]\fR
|
\fBzoxide init nushell | save -f ~/.zoxide.nu\fR
|
||||||
.fi
|
.fi
|
||||||
.sp
|
.sp
|
||||||
Note: zoxide only supports Nushell v0.37.0 and above.
|
Now, add this to the \fBend\fR of your config file (find it by running
|
||||||
.TP
|
\fB$nu.config-path\fR in Nushell):
|
||||||
.B powershell
|
|
||||||
Add this to your configuration (find it by running \fBecho $profile\fR in
|
|
||||||
PowerShell):
|
|
||||||
.sp
|
.sp
|
||||||
.nf
|
.nf
|
||||||
\fBInvoke-Expression (& {
|
\fBsource ~/.zoxide.nu\fR
|
||||||
$hook = if ($PSVersionTable.PSVersion.Major -lt 6) { 'prompt' } else { 'pwd' }
|
.fi
|
||||||
(zoxide init --hook $hook powershell | Out-String)
|
.sp
|
||||||
})\fR
|
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
|
.fi
|
||||||
.TP
|
.TP
|
||||||
.B xonsh
|
.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
|
.sp
|
||||||
.nf
|
.nf
|
||||||
\fBexecx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')\fR
|
\fBexecx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')\fR
|
||||||
.fi
|
.fi
|
||||||
.TP
|
.TP
|
||||||
.B zsh
|
.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
|
.sp
|
||||||
.nf
|
.nf
|
||||||
\fBeval "$(zoxide init zsh)"\fR
|
\fBeval "$(zoxide init zsh)"\fR
|
||||||
|
@ -66,7 +71,7 @@ Add this to your configuration (usually \fB~/.zshrc\fR):
|
||||||
.TP
|
.TP
|
||||||
.B any POSIX shell
|
.B any POSIX shell
|
||||||
.sp
|
.sp
|
||||||
Add this to your configuration:
|
Add this to the \fBend\fR of your config file:
|
||||||
.sp
|
.sp
|
||||||
.nf
|
.nf
|
||||||
\fBeval "$(zoxide init posix --hook prompt)"\fR
|
\fBeval "$(zoxide init posix --hook prompt)"\fR
|
||||||
|
@ -74,9 +79,9 @@ Add this to your configuration:
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.B --cmd
|
.B --cmd
|
||||||
Changes the prefix of predefined aliases (\fBz\fR, \fBzi\fR).
|
Changes the prefix of the \fBz\fR and \fBzi\fR commands.
|
||||||
.br
|
.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
|
.br
|
||||||
\fB--cmd cd\fR would replace the \fBcd\fR command (doesn't work on Nushell /
|
\fB--cmd cd\fR would replace the \fBcd\fR command (doesn't work on Nushell /
|
||||||
POSIX shells).
|
POSIX shells).
|
||||||
|
@ -94,13 +99,13 @@ l l.
|
||||||
\fBpwd\fR|Whenever the directory is changed
|
\fBpwd\fR|Whenever the directory is changed
|
||||||
.TE
|
.TE
|
||||||
.TP
|
.TP
|
||||||
.B --no-aliases
|
.B --no-cmd
|
||||||
Don't define extra aliases (\fBz\fR, \fBzi\fR). These functions will still be
|
Prevents zoxide from defining the \fBz\fR and \fBzi\fR commands. These functions
|
||||||
available in your shell as \fB__zoxide_z\fR and \fB__zoxide_zi\fR, should you
|
will still be available in your shell as \fB__zoxide_z\fR and \fB__zoxide_zi\fR,
|
||||||
choose to redefine them.
|
should you choose to redefine them.
|
||||||
.SH REPORTING BUGS
|
.SH REPORTING BUGS
|
||||||
For any issues, feature requests, or questions, please visit:
|
For any issues, feature requests, or questions, please visit:
|
||||||
.sp
|
.sp
|
||||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||||
.SH AUTHOR
|
.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
|
.SH REPORTING BUGS
|
||||||
For any issues, feature requests, or questions, please visit:
|
For any issues, feature requests, or questions, please visit:
|
||||||
.sp
|
.sp
|
||||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||||
.SH AUTHOR
|
.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
|
.TP
|
||||||
.B -h, --help
|
.B -h, --help
|
||||||
Print help information.
|
Print help information.
|
||||||
.TP
|
|
||||||
.B -i, --interactive [KEYWORDS]
|
|
||||||
Use interactive selection. This option requires \fBfzf\fR(1).
|
|
||||||
.SH REPORTING BUGS
|
.SH REPORTING BUGS
|
||||||
For any issues, feature requests, or questions, please visit:
|
For any issues, feature requests, or questions, please visit:
|
||||||
.sp
|
.sp
|
||||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||||
.SH AUTHOR
|
.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
|
.SH REPORTING BUGS
|
||||||
For any issues, feature requests, or questions, please visit:
|
For any issues, feature requests, or questions, please visit:
|
||||||
.sp
|
.sp
|
||||||
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR.
|
\fBhttps://github.com/ajeetdsouza/zoxide/issues\fR
|
||||||
.SH AUTHOR
|
.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
|
group_imports = "StdExternalCrate"
|
||||||
# error_on_line_overflow = true
|
imports_granularity = "Module"
|
||||||
# error_on_unformatted = true
|
|
||||||
# group_imports = "StdExternalCrate"
|
|
||||||
# imports_granularity = "Module"
|
|
||||||
max_width = 120
|
|
||||||
newline_style = "Native"
|
newline_style = "Native"
|
||||||
use_field_init_shorthand = true
|
use_field_init_shorthand = true
|
||||||
use_small_heuristics = "Max"
|
use_small_heuristics = "Max"
|
||||||
use_try_shorthand = true
|
use_try_shorthand = true
|
||||||
# wrap_comments = true
|
wrap_comments = true
|
||||||
# version = "Two"
|
version = "Two"
|
||||||
|
|
24
shell.nix
24
shell.nix
|
@ -1,13 +1,22 @@
|
||||||
let
|
let
|
||||||
rust = import (builtins.fetchTarball
|
|
||||||
"https://github.com/oxalica/rust-overlay/archive/46d8d20fce510c6a25fa66f36e31f207f6ea49e4.tar.gz");
|
|
||||||
pkgs = import (builtins.fetchTarball
|
pkgs = import (builtins.fetchTarball
|
||||||
"https://github.com/NixOS/nixpkgs/archive/fae46e66a5df220327b45e0d7c27c6961cf922ce.tar.gz") {
|
"https://github.com/NixOS/nixpkgs/archive/22a6958f46fd8e14830d02856ff63b1d0e5cc3e4.tar.gz") {
|
||||||
overlays = [ rust ];
|
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 {
|
in pkgs.mkShell {
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
# Rust
|
# Rust
|
||||||
|
(pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.rustfmt))
|
||||||
pkgs.rust-bin.stable.latest.default
|
pkgs.rust-bin.stable.latest.default
|
||||||
|
|
||||||
# Shells
|
# Shells
|
||||||
|
@ -21,7 +30,11 @@ in pkgs.mkShell {
|
||||||
pkgs.zsh
|
pkgs.zsh
|
||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
pkgs.cargo-audit
|
cargo-udeps
|
||||||
|
pkgs.cargo-msrv
|
||||||
|
pkgs.cargo-nextest
|
||||||
|
pkgs.cargo-udeps
|
||||||
|
pkgs.just
|
||||||
pkgs.mandoc
|
pkgs.mandoc
|
||||||
pkgs.nixfmt
|
pkgs.nixfmt
|
||||||
pkgs.nodePackages.markdownlint-cli
|
pkgs.nodePackages.markdownlint-cli
|
||||||
|
@ -30,6 +43,7 @@ in pkgs.mkShell {
|
||||||
pkgs.python3Packages.pylint
|
pkgs.python3Packages.pylint
|
||||||
pkgs.shellcheck
|
pkgs.shellcheck
|
||||||
pkgs.shfmt
|
pkgs.shfmt
|
||||||
|
pkgs.yamlfmt
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
pkgs.cacert
|
pkgs.cacert
|
||||||
|
@ -38,5 +52,5 @@ in pkgs.mkShell {
|
||||||
pkgs.libiconv
|
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 anyhow::{bail, Result};
|
||||||
|
|
||||||
use crate::cmd::{Add, Run};
|
use crate::cmd::{Add, Run};
|
||||||
use crate::db::DatabaseFile;
|
use crate::db::Database;
|
||||||
use crate::{config, util};
|
use crate::{config, util};
|
||||||
|
|
||||||
impl Run for Add {
|
impl Run for Add {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
// These characters can't be printed cleanly to a single line, so they can cause confusion
|
// These characters can't be printed cleanly to a single line, so they can cause
|
||||||
// when writing to fzf / stdout.
|
// confusion when writing to stdout.
|
||||||
const EXCLUDE_CHARS: &[char] = &['\n', '\r'];
|
const EXCLUDE_CHARS: &[char] = &['\n', '\r'];
|
||||||
|
|
||||||
let data_dir = config::data_dir()?;
|
|
||||||
let exclude_dirs = config::exclude_dirs()?;
|
let exclude_dirs = config::exclude_dirs()?;
|
||||||
let max_age = config::maxage()?;
|
let max_age = config::maxage()?;
|
||||||
let now = util::current_time()?;
|
let now = util::current_time()?;
|
||||||
|
|
||||||
let mut db = DatabaseFile::new(data_dir);
|
let mut db = Database::open()?;
|
||||||
let mut db = db.open()?;
|
|
||||||
|
|
||||||
for path in &self.paths {
|
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)?;
|
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)) {
|
if path.contains(EXCLUDE_CHARS) || exclude_dirs.iter().any(|glob| glob.matches(path)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if !Path::new(path).is_dir() {
|
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.age(max_age);
|
||||||
db.save()?;
|
|
||||||
}
|
}
|
||||||
|
db.save()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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 anyhow::{bail, Context, Result};
|
||||||
|
|
||||||
use crate::cmd::{Import, ImportFrom, Run};
|
use crate::cmd::{Import, ImportFrom, Run};
|
||||||
use crate::config;
|
use crate::db::Database;
|
||||||
use crate::db::{Database, DatabaseFile, Dir};
|
|
||||||
|
|
||||||
impl Run for Import {
|
impl Run for Import {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
let buffer = fs::read_to_string(&self.path)
|
let buffer = fs::read_to_string(&self.path).with_context(|| {
|
||||||
.with_context(|| format!("could not open database for importing: {}", &self.path.display()))?;
|
format!("could not open database for importing: {}", &self.path.display())
|
||||||
|
})?;
|
||||||
|
|
||||||
let data_dir = config::data_dir()?;
|
let mut db = Database::open()?;
|
||||||
let mut db = DatabaseFile::new(data_dir);
|
if !self.merge && !db.dirs().is_empty() {
|
||||||
let db = &mut db.open()?;
|
|
||||||
if !self.merge && !db.dirs.is_empty() {
|
|
||||||
bail!("current database is not empty, specify --merge to continue anyway");
|
bail!("current database is not empty, specify --merge to continue anyway");
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.from {
|
match self.from {
|
||||||
ImportFrom::Autojump => from_autojump(db, &buffer),
|
ImportFrom::Autojump => import_autojump(&mut db, &buffer),
|
||||||
ImportFrom::Z => from_z(db, &buffer),
|
ImportFrom::Z => import_z(&mut db, &buffer),
|
||||||
}
|
}
|
||||||
.context("import error")?;
|
.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() {
|
for line in buffer.lines() {
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
continue;
|
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}"))?;
|
||||||
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
|
||||||
// Normalize the rank using a sigmoid function. Don't import actual ranks from autojump,
|
// autojump, since its scoring algorithm is very different and might
|
||||||
// since its scoring algorithm is very different and might take a while to get normalized.
|
// take a while to get normalized.
|
||||||
rank = sigmoid(rank);
|
rank = sigmoid(rank);
|
||||||
|
|
||||||
let path = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
db.add_unchecked(path, rank, 0);
|
||||||
|
|
||||||
db.dirs.push(Dir { path: path.into(), rank, last_accessed: 0 });
|
|
||||||
db.modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if db.modified {
|
if db.dirty() {
|
||||||
db.dedup();
|
db.dedup();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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() {
|
for line in buffer.lines() {
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut split = line.rsplitn(3, '|');
|
let mut split = line.rsplitn(3, '|');
|
||||||
|
|
||||||
let last_accessed = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
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 =
|
||||||
|
last_accessed.parse().with_context(|| format!("invalid epoch: {last_accessed}"))?;
|
||||||
|
|
||||||
let rank = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
let rank = split.next().with_context(|| format!("invalid entry: {line}"))?;
|
||||||
let rank = rank.parse().with_context(|| format!("invalid rank: {}", rank))?;
|
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.add_unchecked(path, rank, last_accessed);
|
||||||
db.modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if db.modified {
|
if db.dirty() {
|
||||||
db.dedup();
|
db.dedup();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,33 +80,33 @@ fn sigmoid(x: f64) -> f64 {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::sigmoid;
|
use super::*;
|
||||||
use crate::db::{Database, Dir};
|
use crate::db::Dir;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_autojump() {
|
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
|
7.0 /baz
|
||||||
2.0 /foo/bar
|
2.0 /foo/bar
|
||||||
5.0 /quux/quuz
|
5.0 /quux/quuz";
|
||||||
"#;
|
import_autojump(&mut db, buffer).unwrap();
|
||||||
|
|
||||||
let dirs = vec![
|
db.sort_by_path();
|
||||||
Dir { path: "/quux/quuz".into(), rank: 1.0, last_accessed: 100 },
|
println!("got: {:?}", &db.dirs());
|
||||||
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 };
|
|
||||||
|
|
||||||
super::from_autojump(&mut db, buffer).unwrap();
|
let exp = [
|
||||||
db.dirs.sort_by(|dir1, dir2| dir1.path.cmp(&dir2.path));
|
|
||||||
println!("got: {:?}", &db.dirs.as_slice());
|
|
||||||
|
|
||||||
let exp = &[
|
|
||||||
Dir { path: "/baz".into(), rank: sigmoid(7.0), last_accessed: 0 },
|
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: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||||
Dir { path: "/foo/bar".into(), rank: 9.0 + sigmoid(2.0), last_accessed: 900 },
|
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: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||||
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
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_eq!(dir1.path, dir2.path);
|
||||||
assert!((dir1.rank - dir2.rank).abs() < 0.01);
|
assert!((dir1.rank - dir2.rank).abs() < 0.01);
|
||||||
assert_eq!(dir1.last_accessed, dir2.last_accessed);
|
assert_eq!(dir1.last_accessed, dir2.last_accessed);
|
||||||
|
@ -131,29 +125,29 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_z() {
|
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
|
/baz|7|700
|
||||||
/quux/quuz|4|400
|
/quux/quuz|4|400
|
||||||
/foo/bar|2|200
|
/foo/bar|2|200
|
||||||
/quux/quuz|5|500
|
/quux/quuz|5|500";
|
||||||
"#;
|
import_z(&mut db, buffer).unwrap();
|
||||||
|
|
||||||
let dirs = vec![
|
db.sort_by_path();
|
||||||
Dir { path: "/quux/quuz".into(), rank: 1.0, last_accessed: 100 },
|
println!("got: {:?}", &db.dirs());
|
||||||
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 };
|
|
||||||
|
|
||||||
super::from_z(&mut db, buffer).unwrap();
|
let exp = [
|
||||||
db.dirs.sort_by(|dir1, dir2| dir1.path.cmp(&dir2.path));
|
|
||||||
println!("got: {:?}", &db.dirs.as_slice());
|
|
||||||
|
|
||||||
let exp = &[
|
|
||||||
Dir { path: "/baz".into(), rank: 7.0, last_accessed: 700 },
|
Dir { path: "/baz".into(), rank: 7.0, last_accessed: 700 },
|
||||||
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||||
Dir { path: "/foo/bar".into(), rank: 11.0, last_accessed: 900 },
|
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: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||||
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
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_eq!(dir1.path, dir2.path);
|
||||||
assert!((dir1.rank - dir2.rank).abs() < 0.01);
|
assert!((dir1.rank - dir2.rank).abs() < 0.01);
|
||||||
assert_eq!(dir1.last_accessed, dir2.last_accessed);
|
assert_eq!(dir1.last_accessed, dir2.last_accessed);
|
||||||
|
|
|
@ -6,28 +6,26 @@ use askama::Template;
|
||||||
use crate::cmd::{Init, InitShell, Run};
|
use crate::cmd::{Init, InitShell, Run};
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::error::BrokenPipeHandler;
|
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 {
|
impl Run for Init {
|
||||||
fn run(&self) -> Result<()> {
|
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 echo = config::echo();
|
||||||
let resolve_symlinks = config::resolve_symlinks();
|
let resolve_symlinks = config::resolve_symlinks();
|
||||||
|
|
||||||
let opts = &Opts { cmd, hook: self.hook, echo, resolve_symlinks };
|
let opts = &Opts { cmd, hook: self.hook, echo, resolve_symlinks };
|
||||||
|
|
||||||
let source = match self.shell {
|
let source = match self.shell {
|
||||||
InitShell::Bash => shell::Bash(opts).render(),
|
InitShell::Bash => Bash(opts).render(),
|
||||||
InitShell::Elvish => shell::Elvish(opts).render(),
|
InitShell::Elvish => Elvish(opts).render(),
|
||||||
InitShell::Fish => shell::Fish(opts).render(),
|
InitShell::Fish => Fish(opts).render(),
|
||||||
InitShell::Nushell => shell::Nushell(opts).render(),
|
InitShell::Nushell => Nushell(opts).render(),
|
||||||
InitShell::Posix => shell::Posix(opts).render(),
|
InitShell::Posix => Posix(opts).render(),
|
||||||
InitShell::Powershell => shell::Powershell(opts).render(),
|
InitShell::Powershell => Powershell(opts).render(),
|
||||||
InitShell::Xonsh => shell::Xonsh(opts).render(),
|
InitShell::Xonsh => Xonsh(opts).render(),
|
||||||
InitShell::Zsh => shell::Zsh(opts).render(),
|
InitShell::Zsh => Zsh(opts).render(),
|
||||||
}
|
}
|
||||||
.context("could not render template")?;
|
.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 add;
|
||||||
|
mod cmd;
|
||||||
|
mod edit;
|
||||||
mod import;
|
mod import;
|
||||||
mod init;
|
mod init;
|
||||||
mod query;
|
mod query;
|
||||||
|
@ -7,7 +8,7 @@ mod remove;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
pub use crate::cmd::_cmd::*;
|
pub use crate::cmd::cmd::*;
|
||||||
|
|
||||||
pub trait Run {
|
pub trait Run {
|
||||||
fn run(&self) -> Result<()>;
|
fn run(&self) -> Result<()>;
|
||||||
|
@ -17,6 +18,7 @@ impl Run for Cmd {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Cmd::Add(cmd) => cmd.run(),
|
Cmd::Add(cmd) => cmd.run(),
|
||||||
|
Cmd::Edit(cmd) => cmd.run(),
|
||||||
Cmd::Import(cmd) => cmd.run(),
|
Cmd::Import(cmd) => cmd.run(),
|
||||||
Cmd::Init(cmd) => cmd.run(),
|
Cmd::Init(cmd) => cmd.run(),
|
||||||
Cmd::Query(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 std::io::{self, Write};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
|
||||||
use crate::cmd::{Query, Run};
|
use crate::cmd::{Query, Run};
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::db::{Database, DatabaseFile};
|
use crate::db::{Database, Epoch, Stream};
|
||||||
use crate::error::BrokenPipeHandler;
|
use crate::error::BrokenPipeHandler;
|
||||||
use crate::util::{self, Fzf};
|
use crate::util::{self, Fzf, FzfChild};
|
||||||
|
|
||||||
impl Run for Query {
|
impl Run for Query {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
let data_dir = config::data_dir()?;
|
let mut db = crate::db::Database::open()?;
|
||||||
let mut db = DatabaseFile::new(data_dir);
|
|
||||||
let mut db = db.open()?;
|
|
||||||
self.query(&mut db).and(db.save())
|
self.query(&mut db).and(db.save())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +18,50 @@ impl Run for Query {
|
||||||
impl Query {
|
impl Query {
|
||||||
fn query(&self, db: &mut Database) -> Result<()> {
|
fn query(&self, db: &mut Database) -> Result<()> {
|
||||||
let now = util::current_time()?;
|
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);
|
let mut stream = db.stream(now).with_keywords(&self.keywords);
|
||||||
if !self.all {
|
if !self.all {
|
||||||
let resolve_symlinks = config::resolve_symlinks();
|
let resolve_symlinks = config::resolve_symlinks();
|
||||||
|
@ -29,51 +70,36 @@ impl Query {
|
||||||
if let Some(path) = &self.exclude {
|
if let Some(path) = &self.exclude {
|
||||||
stream = stream.with_exclude(path);
|
stream = stream.with_exclude(path);
|
||||||
}
|
}
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
|
||||||
if self.interactive {
|
fn get_fzf() -> Result<FzfChild> {
|
||||||
let mut fzf = Fzf::new(false)?;
|
let mut fzf = Fzf::new()?;
|
||||||
let stdin = fzf.stdin();
|
if let Some(fzf_opts) = config::fzf_opts() {
|
||||||
|
fzf.env("FZF_DEFAULT_OPTS", fzf_opts)
|
||||||
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")?;
|
|
||||||
} else {
|
} else {
|
||||||
let dir = stream.next().context("no match found")?;
|
fzf.args([
|
||||||
if self.score {
|
// Search mode
|
||||||
writeln!(io::stdout(), "{}", dir.display_score(now))
|
"--exact",
|
||||||
} else {
|
// Search result
|
||||||
writeln!(io::stdout(), "{}", dir.display())
|
"--no-sort",
|
||||||
}
|
// Interface
|
||||||
.pipe_exit("stdout")?;
|
"--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()
|
||||||
}
|
}
|
||||||
|
.spawn()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,19 @@
|
||||||
use std::io::{self, Write};
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
|
||||||
|
|
||||||
use crate::cmd::{Remove, Run};
|
use crate::cmd::{Remove, Run};
|
||||||
use crate::config;
|
use crate::db::Database;
|
||||||
use crate::db::DatabaseFile;
|
use crate::util;
|
||||||
use crate::util::{self, Fzf};
|
|
||||||
|
|
||||||
impl Run for Remove {
|
impl Run for Remove {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
let data_dir = config::data_dir()?;
|
let mut db = Database::open()?;
|
||||||
let mut db = DatabaseFile::new(data_dir);
|
|
||||||
let mut db = db.open()?;
|
|
||||||
|
|
||||||
if self.interactive {
|
for path in &self.paths {
|
||||||
let keywords = &self.paths;
|
if !db.remove(path) {
|
||||||
let now = util::current_time()?;
|
let path_abs = util::resolve_path(path)?;
|
||||||
let mut stream = db.stream(now).with_keywords(keywords);
|
let path_abs = util::path_to_str(&path_abs)?;
|
||||||
|
if path_abs == path || !db.remove(path_abs) {
|
||||||
let mut fzf = Fzf::new(true)?;
|
bail!("path not found in database: {path}")
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::env;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use glob::Pattern;
|
use glob::Pattern;
|
||||||
|
|
||||||
use crate::db::Rank;
|
use crate::db::Rank;
|
||||||
|
@ -10,45 +10,35 @@ use crate::db::Rank;
|
||||||
pub fn data_dir() -> Result<PathBuf> {
|
pub fn data_dir() -> Result<PathBuf> {
|
||||||
let path = match env::var_os("_ZO_DATA_DIR") {
|
let path = match env::var_os("_ZO_DATA_DIR") {
|
||||||
Some(path) => PathBuf::from(path),
|
Some(path) => PathBuf::from(path),
|
||||||
None => match dirs::data_local_dir() {
|
None => dirs::data_local_dir()
|
||||||
Some(mut path) => {
|
.context("could not find data directory, please set _ZO_DATA_DIR manually")?
|
||||||
path.push("zoxide");
|
.join("zoxide"),
|
||||||
path
|
|
||||||
}
|
|
||||||
None => bail!("could not find data directory, please set _ZO_DATA_DIR manually"),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn echo() -> bool {
|
pub fn echo() -> bool {
|
||||||
match env::var_os("_ZO_ECHO") {
|
env::var_os("_ZO_ECHO").map_or(false, |var| var == "1")
|
||||||
Some(var) => var == "1",
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exclude_dirs() -> Result<Vec<Pattern>> {
|
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 pattern = (|| {
|
||||||
let home = dirs::home_dir()?;
|
let home = dirs::home_dir()?;
|
||||||
let home = home.to_str()?;
|
let home = Pattern::escape(home.to_str()?);
|
||||||
let home = Pattern::escape(home);
|
|
||||||
Pattern::new(&home).ok()
|
Pattern::new(&home).ok()
|
||||||
})();
|
})();
|
||||||
Ok(pattern.into_iter().collect())
|
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> {
|
pub fn fzf_opts() -> Option<OsString> {
|
||||||
|
@ -56,20 +46,15 @@ pub fn fzf_opts() -> Option<OsString> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maxage() -> Result<Rank> {
|
pub fn maxage() -> Result<Rank> {
|
||||||
match env::var_os("_ZO_MAXAGE") {
|
env::var_os("_ZO_MAXAGE").map_or(Ok(10_000.0), |maxage| {
|
||||||
Some(maxage) => {
|
let maxage = maxage.to_str().context("invalid unicode in _ZO_MAXAGE")?;
|
||||||
let maxage = maxage.to_str().context("invalid unicode in _ZO_MAXAGE")?;
|
let maxage = maxage
|
||||||
let maxage =
|
.parse::<u32>()
|
||||||
maxage.parse::<u64>().with_context(|| format!("unable to parse _ZO_MAXAGE as integer: {}", maxage))?;
|
.with_context(|| format!("unable to parse _ZO_MAXAGE as integer: {maxage}"))?;
|
||||||
Ok(maxage as Rank)
|
Ok(maxage as Rank)
|
||||||
}
|
})
|
||||||
None => Ok(10000.0),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_symlinks() -> bool {
|
pub fn resolve_symlinks() -> bool {
|
||||||
match env::var_os("_ZO_RESOLVE_SYMLINKS") {
|
env::var_os("_ZO_RESOLVE_SYMLINKS").map_or(false, |var| var == "1")
|
||||||
Some(var) => var == "1",
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
152
src/db/dir.rs
152
src/db/dir.rs
|
@ -1,83 +1,9 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
|
||||||
use bincode::Options as _;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
use crate::util::{DAY, HOUR, WEEK};
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Dir<'a> {
|
pub struct Dir<'a> {
|
||||||
|
@ -88,11 +14,11 @@ pub struct Dir<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dir<'_> {
|
impl Dir<'_> {
|
||||||
pub fn score(&self, now: Epoch) -> Rank {
|
pub fn display(&self) -> DirDisplay<'_> {
|
||||||
const HOUR: Epoch = 60 * 60;
|
DirDisplay::new(self)
|
||||||
const DAY: Epoch = 24 * HOUR;
|
}
|
||||||
const WEEK: Epoch = 7 * DAY;
|
|
||||||
|
|
||||||
|
pub fn score(&self, now: Epoch) -> Rank {
|
||||||
// The older the entry, the lesser its importance.
|
// The older the entry, the lesser its importance.
|
||||||
let duration = now.saturating_sub(self.last_accessed);
|
let duration = now.saturating_sub(self.last_accessed);
|
||||||
if duration < HOUR {
|
if duration < HOUR {
|
||||||
|
@ -105,63 +31,39 @@ impl Dir<'_> {
|
||||||
self.rank * 0.25
|
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> {
|
pub struct DirDisplay<'a> {
|
||||||
dir: &'a Dir<'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<'_> {
|
impl Display for DirDisplay<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
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)
|
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 Rank = f64;
|
||||||
pub type Epoch = u64;
|
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 dir;
|
||||||
mod stream;
|
mod stream;
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
use std::io;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::{fs, io};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
pub use dir::{Dir, DirList, Epoch, Rank};
|
use bincode::Options;
|
||||||
pub use stream::Stream;
|
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)]
|
#[self_referencing]
|
||||||
pub struct Database<'file> {
|
pub struct Database {
|
||||||
pub dirs: DirList<'file>,
|
path: PathBuf,
|
||||||
pub modified: bool,
|
bytes: Vec<u8>,
|
||||||
pub data_dir: &'file Path,
|
#[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<()> {
|
pub fn save(&mut self) -> Result<()> {
|
||||||
if !self.modified {
|
// Only write to disk if the database is modified.
|
||||||
|
if !self.dirty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffer = self.dirs.to_bytes()?;
|
let bytes = Self::serialize(self.dirs())?;
|
||||||
let path = db_path(&self.data_dir);
|
util::write(self.borrow_path(), bytes).context("could not write to database")?;
|
||||||
util::write(&path, &buffer).context("could not write to database")?;
|
self.with_dirty_mut(|dirty| *dirty = false);
|
||||||
self.modified = false;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a new directory or increments its rank. Also updates its last accessed time.
|
/// Increments the rank of a directory, or creates it if it does not exist.
|
||||||
pub fn add<S: AsRef<str>>(&mut self, path: S, now: Epoch) {
|
pub fn add(&mut self, path: impl AsRef<str> + Into<String>, by: Rank, now: Epoch) {
|
||||||
let path = path.as_ref();
|
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),
|
||||||
match self.dirs.iter_mut().find(|dir| dir.path == path) {
|
|
||||||
None => {
|
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;
|
self.with_dirty_mut(|dirty| *dirty = true);
|
||||||
dir.rank += 1.0;
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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) {
|
pub fn dedup(&mut self) {
|
||||||
// Sort by path, so that equal paths are next to each other.
|
// 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() {
|
let mut dirty = false;
|
||||||
// Check if curr_dir and next_dir have equal paths.
|
self.with_dirs_mut(|dirs| {
|
||||||
let curr_dir = &self.dirs[idx];
|
for idx in (1..dirs.len()).rev() {
|
||||||
let next_dir = &self.dirs[idx - 1];
|
// Check if curr_dir and next_dir have equal paths.
|
||||||
if next_dir.path != curr_dir.path {
|
let curr_dir = &dirs[idx];
|
||||||
continue;
|
let next_dir = &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);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
// Deserialize sections.
|
||||||
buffer: Vec<u8>,
|
let version = deserializer.deserialize(bytes_version)?;
|
||||||
data_dir: PathBuf,
|
let dirs = match version {
|
||||||
}
|
Self::VERSION => {
|
||||||
|
deserializer.deserialize(bytes_dirs).context("could not deserialize database")?
|
||||||
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 })
|
|
||||||
}
|
}
|
||||||
Err(e) if e.kind() == io::ErrorKind::NotFound => {
|
version => {
|
||||||
// Create data directory, but don't create any file yet. The file will be created
|
bail!("unsupported version (got {version}, supports {})", Self::VERSION)
|
||||||
// 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 })
|
|
||||||
}
|
}
|
||||||
Err(e) => Err(e).with_context(|| format!("could not read from database: {}", path.display())),
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn db_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
|
Ok(dirs)
|
||||||
const DB_FILENAME: &str = "db.zo";
|
}
|
||||||
data_dir.as_ref().join(DB_FILENAME)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -155,50 +240,49 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
|
let data_dir = tempfile::tempdir().unwrap();
|
||||||
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
||||||
let now = 946684800;
|
let now = 946684800;
|
||||||
|
|
||||||
let data_dir = tempfile::tempdir().unwrap();
|
|
||||||
{
|
{
|
||||||
let mut db = DatabaseFile::new(data_dir.path());
|
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||||
let mut db = db.open().unwrap();
|
db.add(path, 1.0, now);
|
||||||
db.add(path, now);
|
db.add(path, 1.0, now);
|
||||||
db.add(path, now);
|
|
||||||
db.save().unwrap();
|
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_eq!(dir.path, path);
|
||||||
|
assert!((dir.rank - 2.0).abs() < 0.01);
|
||||||
assert_eq!(dir.last_accessed, now);
|
assert_eq!(dir.last_accessed, now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove() {
|
fn remove() {
|
||||||
|
let data_dir = tempfile::tempdir().unwrap();
|
||||||
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
||||||
let now = 946684800;
|
let now = 946684800;
|
||||||
|
|
||||||
let data_dir = tempfile::tempdir().unwrap();
|
|
||||||
{
|
{
|
||||||
let mut db = DatabaseFile::new(data_dir.path());
|
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||||
let mut db = db.open().unwrap();
|
db.add(path, 1.0, now);
|
||||||
db.add(path, now);
|
|
||||||
db.save().unwrap();
|
db.save().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut db = DatabaseFile::new(data_dir.path());
|
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||||
let mut db = db.open().unwrap();
|
|
||||||
assert!(db.remove(path));
|
assert!(db.remove(path));
|
||||||
db.save().unwrap();
|
db.save().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut db = DatabaseFile::new(data_dir.path());
|
let mut db = Database::open_dir(data_dir.path()).unwrap();
|
||||||
let mut db = db.open().unwrap();
|
assert!(db.dirs().is_empty());
|
||||||
assert!(db.dirs.is_empty());
|
|
||||||
assert!(!db.remove(path));
|
assert!(!db.remove(path));
|
||||||
db.save().unwrap();
|
db.save().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,36 +2,36 @@ use std::iter::Rev;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::{fs, path};
|
use std::{fs, path};
|
||||||
|
|
||||||
use ordered_float::OrderedFloat;
|
use crate::db::{Database, Dir, Epoch};
|
||||||
|
use crate::util::{self, MONTH};
|
||||||
|
|
||||||
use super::{Database, Dir, Epoch};
|
pub struct Stream<'a> {
|
||||||
use crate::util;
|
// State
|
||||||
|
db: &'a mut Database,
|
||||||
pub struct Stream<'db, 'file> {
|
|
||||||
db: &'db mut Database<'file>,
|
|
||||||
idxs: Rev<Range<usize>>,
|
idxs: Rev<Range<usize>>,
|
||||||
|
did_exclude: bool,
|
||||||
|
|
||||||
|
// Configuration
|
||||||
keywords: Vec<String>,
|
keywords: Vec<String>,
|
||||||
|
|
||||||
check_exists: bool,
|
check_exists: bool,
|
||||||
expire_below: Epoch,
|
expire_below: Epoch,
|
||||||
resolve_symlinks: bool,
|
resolve_symlinks: bool,
|
||||||
|
|
||||||
exclude_path: Option<String>,
|
exclude_path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db, 'file> Stream<'db, 'file> {
|
impl<'a> Stream<'a> {
|
||||||
pub fn new(db: &'db mut Database<'file>, now: Epoch) -> Self {
|
pub fn new(db: &'a mut Database, now: Epoch) -> Self {
|
||||||
// Iterate in descending order of score.
|
db.sort_by_score(now);
|
||||||
db.dirs.sort_unstable_by_key(|dir| OrderedFloat(dir.score(now)));
|
let idxs = (0..db.dirs().len()).rev();
|
||||||
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.
|
// If a directory is deleted and hasn't been used for 3 months, delete
|
||||||
let expire_below = now.saturating_sub(90 * 24 * 60 * 60);
|
// it from the database.
|
||||||
|
let expire_below = now.saturating_sub(3 * MONTH);
|
||||||
|
|
||||||
Stream {
|
Stream {
|
||||||
db,
|
db,
|
||||||
idxs,
|
idxs,
|
||||||
|
did_exclude: false,
|
||||||
keywords: Vec::new(),
|
keywords: Vec::new(),
|
||||||
check_exists: false,
|
check_exists: false,
|
||||||
expire_below,
|
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.exclude_path = Some(path.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -51,14 +51,14 @@ impl<'db, 'file> Stream<'db, 'file> {
|
||||||
self
|
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.keywords = keywords.iter().map(util::to_lowercase).collect();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(&mut self) -> Option<&Dir<'file>> {
|
pub fn next(&mut self) -> Option<&Dir> {
|
||||||
while let Some(idx) = self.idxs.next() {
|
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) {
|
if !self.matches_keywords(&dir.path) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -66,32 +66,36 @@ impl<'db, 'file> Stream<'db, 'file> {
|
||||||
|
|
||||||
if !self.matches_exists(&dir.path) {
|
if !self.matches_exists(&dir.path) {
|
||||||
if dir.last_accessed < self.expire_below {
|
if dir.last_accessed < self.expire_below {
|
||||||
self.db.dirs.swap_remove(idx);
|
self.db.swap_remove(idx);
|
||||||
self.db.modified = true;
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if Some(dir.path.as_ref()) == self.exclude_path.as_deref() {
|
if Some(dir.path.as_ref()) == self.exclude_path.as_deref() {
|
||||||
|
self.did_exclude = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dir = &self.db.dirs[idx];
|
let dir = &self.db.dirs()[idx];
|
||||||
return Some(dir);
|
return Some(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
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 {
|
if !self.check_exists {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let resolver = if self.resolve_symlinks { fs::symlink_metadata } else { fs::metadata };
|
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() {
|
let (keywords_last, keywords) = match self.keywords.split_last() {
|
||||||
Some(split) => split,
|
Some(split) => split,
|
||||||
None => return true,
|
None => return true,
|
||||||
|
@ -126,7 +130,7 @@ mod tests {
|
||||||
|
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
use super::Database;
|
use super::*;
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
// Case normalization
|
// Case normalization
|
||||||
|
@ -149,8 +153,8 @@ mod tests {
|
||||||
#[case(&["/foo/", "/bar"], "/foo/bar", false)]
|
#[case(&["/foo/", "/bar"], "/foo/bar", false)]
|
||||||
#[case(&["/foo/", "/bar"], "/foo/baz/bar", true)]
|
#[case(&["/foo/", "/bar"], "/foo/baz/bar", true)]
|
||||||
fn query(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) {
|
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 db = &mut Database::new(PathBuf::new(), Vec::new(), |_| Vec::new(), false);
|
||||||
let stream = db.stream(0).with_keywords(keywords);
|
let stream = Stream::new(db, 0).with_keywords(keywords);
|
||||||
assert_eq!(is_match, stream.matches_keywords(path));
|
assert_eq!(is_match, stream.matches_keywords(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use anyhow::{bail, Context, Result};
|
||||||
/// Custom error type for early exit.
|
/// Custom error type for early exit.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SilentExit {
|
pub struct SilentExit {
|
||||||
pub code: i32,
|
pub code: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SilentExit {
|
impl Display for SilentExit {
|
||||||
|
@ -23,7 +23,7 @@ impl BrokenPipeHandler for io::Result<()> {
|
||||||
fn pipe_exit(self, device: &str) -> Result<()> {
|
fn pipe_exit(self, device: &str) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => bail!(SilentExit { code: 0 }),
|
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)]
|
#![allow(clippy::single_component_path_imports)]
|
||||||
|
|
||||||
// rstest_reuse must be imported at the top of the crate.
|
// rstest_reuse must be imported at the top of the crate.
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "nix-dev"))]
|
||||||
use rstest_reuse;
|
use rstest_reuse;
|
||||||
|
|
||||||
mod cmd;
|
mod cmd;
|
||||||
|
@ -11,26 +11,28 @@ mod error;
|
||||||
mod shell;
|
mod shell;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::{env, process};
|
use std::process::ExitCode;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::cmd::{Cmd, Run};
|
use crate::cmd::{Cmd, Run};
|
||||||
use crate::error::SilentExit;
|
use crate::error::SilentExit;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() -> ExitCode {
|
||||||
// Forcibly disable backtraces.
|
// Forcibly disable backtraces.
|
||||||
env::remove_var("RUST_LIB_BACKTRACE");
|
env::remove_var("RUST_LIB_BACKTRACE");
|
||||||
env::remove_var("RUST_BACKTRACE");
|
env::remove_var("RUST_BACKTRACE");
|
||||||
|
|
||||||
if let Err(e) = Cmd::parse().run() {
|
match Cmd::parse().run() {
|
||||||
match e.downcast::<SilentExit>() {
|
Ok(()) => ExitCode::SUCCESS,
|
||||||
Ok(SilentExit { code }) => process::exit(code),
|
Err(e) => match e.downcast::<SilentExit>() {
|
||||||
|
Ok(SilentExit { code }) => code.into(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = writeln!(io::stderr(), "zoxide: {:?}", e);
|
_ = writeln!(io::stderr(), "zoxide: {e:?}");
|
||||||
process::exit(1);
|
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 opts = Opts { cmd, hook, echo, resolve_symlinks };
|
||||||
let source = Bash(&opts).render().unwrap();
|
let source = Bash(&opts).render().unwrap();
|
||||||
Command::new("bash")
|
Command::new("bash")
|
||||||
.args(&["--noprofile", "--norc", "-e", "-u", "-o", "pipefail", "-c", &source])
|
.args(["--noprofile", "--norc", "-e", "-u", "-o", "pipefail", "-c", &source])
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout("")
|
.stdout("")
|
||||||
|
@ -70,7 +70,7 @@ mod tests {
|
||||||
let source = Bash(&opts).render().unwrap();
|
let source = Bash(&opts).render().unwrap();
|
||||||
|
|
||||||
Command::new("shellcheck")
|
Command::new("shellcheck")
|
||||||
.args(&["--enable", "all", "--shell", "bash", "-"])
|
.args(["--enable", "all", "--shell", "bash", "-"])
|
||||||
.write_stdin(source)
|
.write_stdin(source)
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
|
@ -85,7 +85,7 @@ mod tests {
|
||||||
source.push('\n');
|
source.push('\n');
|
||||||
|
|
||||||
Command::new("shfmt")
|
Command::new("shfmt")
|
||||||
.args(&["-d", "-s", "-ln", "bash", "-i", "4", "-ci", "-"])
|
.args(["--diff", "--indent=4", "--language-dialect=bash", "--simplify", "-"])
|
||||||
.write_stdin(source)
|
.write_stdin(source)
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
|
@ -96,16 +96,21 @@ mod tests {
|
||||||
#[apply(opts)]
|
#[apply(opts)]
|
||||||
fn elvish_elvish(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {
|
fn elvish_elvish(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) {
|
||||||
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
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
|
// Filter out lines using edit:*, since those functions are only available in
|
||||||
// interactive editor.
|
// the interactive editor.
|
||||||
for line in Elvish(&opts).render().unwrap().split('\n').filter(|line| !line.contains("edit:")) {
|
for line in Elvish(&opts).render().unwrap().lines().filter(|line| !line.contains("edit:")) {
|
||||||
source.push_str(line);
|
source.push_str(line);
|
||||||
source.push('\n');
|
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)]
|
#[apply(opts)]
|
||||||
|
@ -118,7 +123,7 @@ mod tests {
|
||||||
|
|
||||||
Command::new("fish")
|
Command::new("fish")
|
||||||
.env("HOME", tempdir)
|
.env("HOME", tempdir)
|
||||||
.args(&["--command", &source, "--private"])
|
.args(["--command", &source, "--no-config", "--private"])
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout("")
|
.stdout("")
|
||||||
|
@ -149,10 +154,14 @@ mod tests {
|
||||||
let source = Nushell(&opts).render().unwrap();
|
let source = Nushell(&opts).render().unwrap();
|
||||||
|
|
||||||
let tempdir = tempfile::tempdir().unwrap();
|
let tempdir = tempfile::tempdir().unwrap();
|
||||||
let tempdir = tempdir.path().to_str().unwrap();
|
let tempdir = tempdir.path();
|
||||||
|
|
||||||
let assert =
|
let assert = Command::new("nu")
|
||||||
Command::new("nu").env("HOME", tempdir).args(&["--commands", &source]).assert().success().stderr("");
|
.env("HOME", tempdir)
|
||||||
|
.args(["--commands", &source])
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stderr("");
|
||||||
|
|
||||||
if opts.hook != InitHook::Pwd {
|
if opts.hook != InitHook::Pwd {
|
||||||
assert.stdout("");
|
assert.stdout("");
|
||||||
|
@ -165,7 +174,7 @@ mod tests {
|
||||||
let source = Posix(&opts).render().unwrap();
|
let source = Posix(&opts).render().unwrap();
|
||||||
|
|
||||||
let assert = Command::new("bash")
|
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()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stderr("");
|
.stderr("");
|
||||||
|
@ -179,7 +188,8 @@ mod tests {
|
||||||
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
||||||
let source = Posix(&opts).render().unwrap();
|
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 {
|
if opts.hook != InitHook::Pwd {
|
||||||
assert.stdout("");
|
assert.stdout("");
|
||||||
}
|
}
|
||||||
|
@ -191,7 +201,7 @@ mod tests {
|
||||||
let source = Posix(&opts).render().unwrap();
|
let source = Posix(&opts).render().unwrap();
|
||||||
|
|
||||||
Command::new("shellcheck")
|
Command::new("shellcheck")
|
||||||
.args(&["--enable", "all", "--shell", "sh", "-"])
|
.args(["--enable", "all", "--shell", "sh", "-"])
|
||||||
.write_stdin(source)
|
.write_stdin(source)
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
|
@ -206,7 +216,7 @@ mod tests {
|
||||||
source.push('\n');
|
source.push('\n');
|
||||||
|
|
||||||
Command::new("shfmt")
|
Command::new("shfmt")
|
||||||
.args(&["-d", "-s", "-ln", "posix", "-i", "4", "-ci", "-"])
|
.args(["--diff", "--indent=4", "--language-dialect=posix", "--simplify", "-"])
|
||||||
.write_stdin(source)
|
.write_stdin(source)
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
|
@ -221,7 +231,7 @@ mod tests {
|
||||||
Powershell(&opts).render_into(&mut source).unwrap();
|
Powershell(&opts).render_into(&mut source).unwrap();
|
||||||
|
|
||||||
Command::new("pwsh")
|
Command::new("pwsh")
|
||||||
.args(&["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", &source])
|
.args(["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", &source])
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout("")
|
.stdout("")
|
||||||
|
@ -234,7 +244,12 @@ mod tests {
|
||||||
let mut source = Xonsh(&opts).render().unwrap();
|
let mut source = Xonsh(&opts).render().unwrap();
|
||||||
source.push('\n');
|
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)]
|
#[apply(opts)]
|
||||||
|
@ -242,7 +257,7 @@ mod tests {
|
||||||
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
let opts = Opts { cmd, hook, echo, resolve_symlinks };
|
||||||
let source = Xonsh(&opts).render().unwrap();
|
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)]
|
#[apply(opts)]
|
||||||
|
@ -252,7 +267,7 @@ mod tests {
|
||||||
source.push('\n');
|
source.push('\n');
|
||||||
|
|
||||||
Command::new("pylint")
|
Command::new("pylint")
|
||||||
.args(&["--from-stdin", "--persistent=n", "zoxide"])
|
.args(["--from-stdin", "--persistent=n", "zoxide"])
|
||||||
.write_stdin(source)
|
.write_stdin(source)
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
|
@ -268,7 +283,7 @@ mod tests {
|
||||||
let tempdir = tempdir.path().to_str().unwrap();
|
let tempdir = tempdir.path().to_str().unwrap();
|
||||||
|
|
||||||
Command::new("xonsh")
|
Command::new("xonsh")
|
||||||
.args(&["-c", &source, "--no-rc"])
|
.args(["-c", &source, "--no-rc"])
|
||||||
.env("HOME", tempdir)
|
.env("HOME", tempdir)
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
|
@ -283,7 +298,7 @@ mod tests {
|
||||||
|
|
||||||
// ShellCheck doesn't support zsh yet: https://github.com/koalaman/shellcheck/issues/809
|
// ShellCheck doesn't support zsh yet: https://github.com/koalaman/shellcheck/issues/809
|
||||||
Command::new("shellcheck")
|
Command::new("shellcheck")
|
||||||
.args(&["--enable", "all", "--shell", "bash", "-"])
|
.args(["--enable", "all", "--shell", "bash", "-"])
|
||||||
.write_stdin(source)
|
.write_stdin(source)
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
|
@ -297,7 +312,7 @@ mod tests {
|
||||||
let source = Zsh(&opts).render().unwrap();
|
let source = Zsh(&opts).render().unwrap();
|
||||||
|
|
||||||
Command::new("zsh")
|
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()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout("")
|
.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::fs::{self, File, OpenOptions};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::mem;
|
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::{Child, Command, Stdio};
|
||||||
use std::process::{Child, ChildStdin, Stdio};
|
|
||||||
use std::time::SystemTime;
|
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::{Dir, Epoch};
|
||||||
use crate::db::Epoch;
|
|
||||||
use crate::error::SilentExit;
|
use crate::error::SilentExit;
|
||||||
|
|
||||||
pub struct Fzf {
|
pub const SECOND: Epoch = 1;
|
||||||
child: Child,
|
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 {
|
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> {
|
pub fn new() -> Result<Self> {
|
||||||
let bin = if cfg!(windows) { "fzf.exe" } else { "fzf" };
|
// On Windows, CreateProcess implicitly searches the current working
|
||||||
let mut command = get_command(bin).map_err(|_| anyhow!(Self::ERR_NOT_FOUND))?;
|
// directory for the executable, which is a potential security issue.
|
||||||
if multiple {
|
// Instead, we resolve the path to the executable and then pass it to
|
||||||
command.arg("-m");
|
// CreateProcess.
|
||||||
}
|
#[cfg(windows)]
|
||||||
command.arg("-n2..").stdin(Stdio::piped()).stdout(Stdio::piped());
|
let program = which::which("fzf.exe").map_err(|_| anyhow!(Self::ERR_FZF_NOT_FOUND))?;
|
||||||
if let Some(fzf_opts) = config::fzf_opts() {
|
#[cfg(not(windows))]
|
||||||
command.env("FZF_DEFAULT_OPTS", fzf_opts);
|
let program = "fzf";
|
||||||
} 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..}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = match command.spawn() {
|
// TODO: check version of fzf here.
|
||||||
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")?,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 {
|
pub fn enable_preview(&mut self) -> &mut Self {
|
||||||
self.child.stdin.as_mut().unwrap()
|
// 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.
|
// 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 stdout = self.0.stdout.take().unwrap();
|
||||||
let mut output = String::new();
|
let mut output = String::default();
|
||||||
stdout.read_to_string(&mut output).context("failed to read from fzf")?;
|
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() {
|
match status.code() {
|
||||||
Some(0) => Ok(output),
|
Some(0) => Ok(output),
|
||||||
Some(1) => bail!("no match found"),
|
Some(1) => bail!("no match found"),
|
||||||
Some(2) => bail!("fzf returned an error"),
|
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"),
|
Some(128..=254) | None => bail!("fzf was terminated"),
|
||||||
_ => bail!("fzf returned an unknown error"),
|
_ => bail!("fzf returned an unknown error"),
|
||||||
}
|
}
|
||||||
|
@ -85,7 +151,7 @@ impl Fzf {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to [`fs::write`], but atomic (best effort on Windows).
|
/// 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 path = path.as_ref();
|
||||||
let contents = contents.as_ref();
|
let contents = contents.as_ref();
|
||||||
let dir = path.parent().unwrap();
|
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 (mut tmp_file, tmp_path) = tmpfile(dir)?;
|
||||||
let result = (|| {
|
let result = (|| {
|
||||||
// Write to the tmpfile.
|
// Write to the tmpfile.
|
||||||
let _ = tmp_file.set_len(contents.len() as u64);
|
_ = 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
|
||||||
|
.write_all(contents)
|
||||||
|
.with_context(|| format!("could not write to file: {}", tmp_path.display()))?;
|
||||||
|
|
||||||
// Set the owner of the tmpfile (UNIX only).
|
// Set the owner of the tmpfile (UNIX only).
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
if let Ok(metadata) = path.metadata() {
|
if let Ok(metadata) = path.metadata() {
|
||||||
use nix::unistd::{self, Gid, Uid};
|
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
use nix::unistd::{self, Gid, Uid};
|
||||||
|
|
||||||
let uid = Uid::from_raw(metadata.uid());
|
let uid = Uid::from_raw(metadata.uid());
|
||||||
let gid = Gid::from_raw(metadata.gid());
|
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.
|
// 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.
|
// In case of an error, delete the tmpfile.
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
let _ = fs::remove_file(&tmp_path);
|
_ = fs::remove_file(&tmp_path);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Atomically create a tmpfile in the given directory.
|
/// 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 MAX_ATTEMPTS: usize = 5;
|
||||||
const TMP_NAME_LEN: usize = 16;
|
const TMP_NAME_LEN: usize = 16;
|
||||||
let dir = dir.as_ref();
|
let dir = dir.as_ref();
|
||||||
|
@ -141,35 +210,39 @@ fn tmpfile<P: AsRef<Path>>(dir: P) -> Result<(File, PathBuf)> {
|
||||||
// Atomically create the tmpfile.
|
// Atomically create the tmpfile.
|
||||||
match OpenOptions::new().write(true).create_new(true).open(&path) {
|
match OpenOptions::new().write(true).create_new(true).open(&path) {
|
||||||
Ok(file) => break Ok((file, path)),
|
Ok(file) => break Ok((file, path)),
|
||||||
Err(e) if e.kind() == io::ErrorKind::AlreadyExists && attempts < MAX_ATTEMPTS => (),
|
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) => {
|
||||||
|
break Err(e).with_context(|| format!("could not create file: {}", path.display()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to [`fs::rename`], but retries on Windows.
|
/// Similar to [`fs::rename`], but with retries on Windows.
|
||||||
fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
|
fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
||||||
const MAX_ATTEMPTS: usize = 5;
|
|
||||||
let from = from.as_ref();
|
let from = from.as_ref();
|
||||||
let to = to.as_ref();
|
let to = to.as_ref();
|
||||||
|
|
||||||
if cfg!(windows) {
|
const MAX_ATTEMPTS: usize = if cfg!(windows) { 5 } else { 1 };
|
||||||
let mut attempts = 0;
|
let mut attempts = 0;
|
||||||
loop {
|
|
||||||
attempts += 1;
|
loop {
|
||||||
match fs::rename(from, to) {
|
match fs::rename(from, to) {
|
||||||
Err(e) if e.kind() == io::ErrorKind::PermissionDenied && attempts < MAX_ATTEMPTS => (),
|
Err(e) if e.kind() == io::ErrorKind::PermissionDenied && attempts < MAX_ATTEMPTS => {
|
||||||
result => break result,
|
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> {
|
pub fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {
|
||||||
dunce::canonicalize(path).with_context(|| format!("could not resolve path: {}", path.as_ref().display()))
|
dunce::canonicalize(&path)
|
||||||
|
.with_context(|| format!("could not resolve path: {}", path.as_ref().display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_dir() -> Result<PathBuf> {
|
pub fn current_dir() -> Result<PathBuf> {
|
||||||
|
@ -177,49 +250,22 @@ pub fn current_dir() -> Result<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_time() -> Result<Epoch> {
|
pub fn current_time() -> Result<Epoch> {
|
||||||
let current_time =
|
let current_time = SystemTime::now()
|
||||||
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).context("system clock set to invalid time")?.as_secs();
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.context("system clock set to invalid time")?
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
Ok(current_time)
|
Ok(current_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new [`Command`] for launching the program with the name
|
pub fn path_to_str(path: &impl AsRef<Path>) -> Result<&str> {
|
||||||
/// `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> {
|
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
path.to_str().with_context(|| format!("invalid unicode in path: {}", path.display()))
|
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
|
/// Returns the absolute version of a path. Like
|
||||||
/// resolve symlinks.
|
/// [`std::path::Path::canonicalize`], but doesn't resolve symlinks.
|
||||||
pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
pub fn resolve_path(path: impl AsRef<Path>) -> Result<PathBuf> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let base_path;
|
let base_path;
|
||||||
|
|
||||||
|
@ -230,13 +276,15 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
use std::path::Prefix;
|
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 path = path.as_ref();
|
||||||
let mut components = path.components();
|
let mut components = path.components();
|
||||||
|
|
||||||
match components.next() {
|
match components.next() {
|
||||||
Some(Component::Prefix(prefix)) => match prefix.kind() {
|
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,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -289,8 +337,9 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||||
components.next();
|
components.next();
|
||||||
|
|
||||||
let current_dir = env::current_dir()?;
|
let current_dir = env::current_dir()?;
|
||||||
let drive_letter = get_drive_letter(¤t_dir)
|
let drive_letter = get_drive_letter(¤t_dir).with_context(|| {
|
||||||
.with_context(|| format!("could not get drive letter: {}", current_dir.display()))?;
|
format!("could not get drive letter: {}", current_dir.display())
|
||||||
|
})?;
|
||||||
base_path = get_drive_path(drive_letter);
|
base_path = get_drive_path(drive_letter);
|
||||||
stack.extend(base_path.components());
|
stack.extend(base_path.components());
|
||||||
}
|
}
|
||||||
|
@ -310,7 +359,7 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
|
||||||
for component in components {
|
for component in components {
|
||||||
match component {
|
match component {
|
||||||
Component::Normal(_) => stack.push(component),
|
Component::Normal(_) => stack.push(component),
|
||||||
Component::CurDir => (),
|
Component::CurDir => {}
|
||||||
Component::ParentDir => {
|
Component::ParentDir => {
|
||||||
if stack.last() != Some(&Component::RootDir) {
|
if stack.last() != Some(&Component::RootDir) {
|
||||||
stack.pop();
|
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.
|
/// 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();
|
let s = s.as_ref();
|
||||||
if s.is_ascii() {
|
if s.is_ascii() { s.to_ascii_lowercase() } else { s.to_lowercase() }
|
||||||
s.to_ascii_lowercase()
|
|
||||||
} else {
|
|
||||||
s.to_lowercase()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
||||||
function __zoxide_pwd() {
|
function __zoxide_pwd() {
|
||||||
{%- if resolve_symlinks %}
|
{%- if cfg!(windows) %}
|
||||||
|
\command cygpath -w "$(\builtin pwd -P)"
|
||||||
|
{%- else if resolve_symlinks %}
|
||||||
\builtin pwd -P
|
\builtin pwd -P
|
||||||
{%- else %}
|
{%- else %}
|
||||||
\builtin pwd -L
|
\builtin pwd -L
|
||||||
|
@ -32,7 +34,8 @@ function __zoxide_cd() {
|
||||||
{%- if hook == InitHook::Prompt %}
|
{%- if hook == InitHook::Prompt %}
|
||||||
function __zoxide_hook() {
|
function __zoxide_hook() {
|
||||||
\builtin local -r retval="$?"
|
\builtin local -r retval="$?"
|
||||||
\command zoxide add -- "$(__zoxide_pwd || \builtin true)"
|
# shellcheck disable=SC2312
|
||||||
|
\command zoxide add -- "$(__zoxide_pwd)"
|
||||||
return "${retval}"
|
return "${retval}"
|
||||||
}
|
}
|
||||||
{%- else if hook == InitHook::Pwd %}
|
{%- else if hook == InitHook::Pwd %}
|
||||||
|
@ -58,8 +61,7 @@ fi
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# When using zoxide with --no-aliases, alias these internal functions as
|
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||||
# desired.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
__zoxide_z_prefix='z#'
|
__zoxide_z_prefix='z#'
|
||||||
|
@ -73,13 +75,14 @@ function __zoxide_z() {
|
||||||
__zoxide_cd "${OLDPWD}"
|
__zoxide_cd "${OLDPWD}"
|
||||||
elif [[ $# -eq 1 && -d $1 ]]; then
|
elif [[ $# -eq 1 && -d $1 ]]; then
|
||||||
__zoxide_cd "$1"
|
__zoxide_cd "$1"
|
||||||
elif [[ ${@: -1} == "${__zoxide_z_prefix}"* ]]; then
|
elif [[ ${@: -1} == "${__zoxide_z_prefix}"?* ]]; then
|
||||||
# shellcheck disable=SC2124
|
# shellcheck disable=SC2124
|
||||||
\builtin local result="${@: -1}"
|
\builtin local result="${@: -1}"
|
||||||
__zoxide_cd "{{ "${result:${#__zoxide_z_prefix}}" }}"
|
__zoxide_cd "{{ "${result:${#__zoxide_z_prefix}}" }}"
|
||||||
else
|
else
|
||||||
\builtin local result
|
\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}"
|
__zoxide_cd "${result}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
@ -87,29 +90,22 @@ function __zoxide_z() {
|
||||||
# Jump to a directory using interactive search.
|
# Jump to a directory using interactive search.
|
||||||
function __zoxide_zi() {
|
function __zoxide_zi() {
|
||||||
\builtin local result
|
\builtin local result
|
||||||
result="$(\command zoxide query -i -- "$@")" && __zoxide_cd "${result}"
|
result="$(\command zoxide query --interactive -- "$@")" && __zoxide_cd "${result}"
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
# Commands for zoxide. Disable these using --no-cmd.
|
||||||
#
|
#
|
||||||
|
|
||||||
{%- match cmd %}
|
{%- match cmd %}
|
||||||
{%- when Some with (cmd) %}
|
{%- when Some with (cmd) %}
|
||||||
|
|
||||||
# Remove definitions.
|
\builtin unalias {{cmd}} &>/dev/null || \builtin true
|
||||||
function __zoxide_unset() {
|
|
||||||
\builtin unset -f "$@" &>/dev/null
|
|
||||||
\builtin unset -v "$@" &>/dev/null
|
|
||||||
\builtin unalias "$@" &>/dev/null || \builtin :
|
|
||||||
}
|
|
||||||
|
|
||||||
__zoxide_unset {{cmd}}
|
|
||||||
function {{cmd}}() {
|
function {{cmd}}() {
|
||||||
__zoxide_z "$@"
|
__zoxide_z "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
__zoxide_unset {{cmd}}i
|
\builtin unalias {{cmd}}i &>/dev/null || \builtin true
|
||||||
function {{cmd}}i() {
|
function {{cmd}}i() {
|
||||||
__zoxide_zi "$@"
|
__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 there is only one argument, use `cd` completions.
|
||||||
if [[ {{ "${#COMP_WORDS[@]}" }} -eq 2 ]]; then
|
if [[ {{ "${#COMP_WORDS[@]}" }} -eq 2 ]]; then
|
||||||
\builtin mapfile -t COMPREPLY < \
|
\builtin mapfile -t COMPREPLY < <(
|
||||||
<(\builtin compgen -A directory -S / -- "${COMP_WORDS[-1]}" || \builtin true)
|
\builtin compgen -A directory -- "${COMP_WORDS[-1]}" || \builtin true
|
||||||
|
)
|
||||||
# If there is a space after the last word, use interactive selection.
|
# 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
|
\builtin local result
|
||||||
result="$(\command zoxide query -i -- "{{ "${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}" }}")" &&
|
# shellcheck disable=SC2312
|
||||||
COMPREPLY=("${__zoxide_z_prefix}${result@Q}")
|
result="$(\command zoxide query --exclude "$(__zoxide_pwd)" --interactive -- "{{ "${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}" }}")" &&
|
||||||
|
COMPREPLY=("${__zoxide_z_prefix}${result}/")
|
||||||
\builtin printf '\e[5n'
|
\builtin printf '\e[5n'
|
||||||
fi
|
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
|
fi
|
||||||
|
|
||||||
{%- when None %}
|
{%- when None %}
|
||||||
|
|
|
@ -41,8 +41,7 @@ if (builtin:not (builtin:eq $E:__zoxide_shlvl $E:SHLVL)) {
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# When using zoxide with --no-aliases, alias these internal functions as
|
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||||
# desired.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
# Jump to a directory using only keywords.
|
# Jump to a directory using only keywords.
|
||||||
|
@ -57,7 +56,7 @@ fn __zoxide_z {|@rest|
|
||||||
var path
|
var path
|
||||||
try {
|
try {
|
||||||
set path = (zoxide query --exclude $pwd -- $@rest)
|
set path = (zoxide query --exclude $pwd -- $@rest)
|
||||||
} except {
|
} catch {
|
||||||
} else {
|
} else {
|
||||||
__zoxide_cd $path
|
__zoxide_cd $path
|
||||||
}
|
}
|
||||||
|
@ -69,8 +68,8 @@ edit:add-var __zoxide_z~ $__zoxide_z~
|
||||||
fn __zoxide_zi {|@rest|
|
fn __zoxide_zi {|@rest|
|
||||||
var path
|
var path
|
||||||
try {
|
try {
|
||||||
set path = (zoxide query -i -- $@rest)
|
set path = (zoxide query --interactive -- $@rest)
|
||||||
} except {
|
} catch {
|
||||||
} else {
|
} else {
|
||||||
__zoxide_cd $path
|
__zoxide_cd $path
|
||||||
}
|
}
|
||||||
|
@ -78,7 +77,7 @@ fn __zoxide_zi {|@rest|
|
||||||
edit:add-var __zoxide_zi~ $__zoxide_zi~
|
edit:add-var __zoxide_zi~ $__zoxide_zi~
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
# Commands for zoxide. Disable these using --no-cmd.
|
||||||
#
|
#
|
||||||
|
|
||||||
{%- match cmd %}
|
{%- match cmd %}
|
||||||
|
@ -88,8 +87,10 @@ edit:add-var {{cmd}}~ $__zoxide_z~
|
||||||
edit:add-var {{cmd}}i~ $__zoxide_zi~
|
edit:add-var {{cmd}}i~ $__zoxide_zi~
|
||||||
|
|
||||||
# Load completions.
|
# 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|
|
fn __zoxide_z_complete {|@rest|
|
||||||
if (!= (builtin:count $rest) 2) {
|
if (!= (builtin:count $rest) 2) {
|
||||||
builtin:return
|
builtin:return
|
||||||
|
@ -116,4 +117,4 @@ set edit:completion:arg-completer[{{cmd}}] = $__zoxide_z_complete~
|
||||||
#
|
#
|
||||||
# eval (zoxide init elvish | slurp)
|
# 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.
|
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
||||||
function __zoxide_pwd
|
function __zoxide_pwd
|
||||||
{%- if resolve_symlinks %}
|
{%- if cfg!(windows) %}
|
||||||
|
command cygpath -w (builtin pwd -P)
|
||||||
|
{%- else if resolve_symlinks %}
|
||||||
builtin pwd -P
|
builtin pwd -P
|
||||||
{%- else %}
|
{%- else %}
|
||||||
builtin pwd -L
|
builtin pwd -L
|
||||||
|
@ -16,9 +18,9 @@ end
|
||||||
|
|
||||||
# A copy of fish's internal cd function. This makes it possible to use
|
# A copy of fish's internal cd function. This makes it possible to use
|
||||||
# `alias cd=z` without causing an infinite loop.
|
# `alias cd=z` without causing an infinite loop.
|
||||||
if ! builtin functions -q __zoxide_cd_internal
|
if ! builtin functions --query __zoxide_cd_internal
|
||||||
if builtin functions -q cd
|
if builtin functions --query cd
|
||||||
builtin functions -c cd __zoxide_cd_internal
|
builtin functions --copy cd __zoxide_cd_internal
|
||||||
else
|
else
|
||||||
alias __zoxide_cd_internal='builtin cd'
|
alias __zoxide_cd_internal='builtin cd'
|
||||||
end
|
end
|
||||||
|
@ -26,7 +28,11 @@ end
|
||||||
|
|
||||||
# cd + custom logic based on the value of _ZO_ECHO.
|
# cd + custom logic based on the value of _ZO_ECHO.
|
||||||
function __zoxide_cd
|
function __zoxide_cd
|
||||||
|
{%- if cfg!(windows) %}
|
||||||
|
__zoxide_cd_internal (cygpath -u $argv)
|
||||||
|
{%- else %}
|
||||||
__zoxide_cd_internal $argv
|
__zoxide_cd_internal $argv
|
||||||
|
{%- endif %}
|
||||||
{%- if echo %}
|
{%- if echo %}
|
||||||
and __zoxide_pwd
|
and __zoxide_pwd
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
@ -53,68 +59,67 @@ end
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# When using zoxide with --no-aliases, alias these internal functions as
|
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||||
# 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.
|
# Jump to a directory using only keywords.
|
||||||
function __zoxide_z
|
function __zoxide_z
|
||||||
set argc (count $argv)
|
set -l argc (count $argv)
|
||||||
if test $argc -eq 0
|
if test $argc -eq 0
|
||||||
__zoxide_cd $HOME
|
__zoxide_cd $HOME
|
||||||
else if test "$argv" = -
|
else if test "$argv" = -
|
||||||
__zoxide_cd -
|
__zoxide_cd -
|
||||||
else if test $argc -eq 1 -a -d $argv[1]
|
else if test $argc -eq 1 -a -d $argv[1]
|
||||||
__zoxide_cd $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
|
else
|
||||||
set -l result (command zoxide query --exclude (__zoxide_pwd) -- $argv)
|
set -l result (command zoxide query --exclude (__zoxide_pwd) -- $argv)
|
||||||
and __zoxide_cd $result
|
and __zoxide_cd $result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Completions for `z`.
|
# Completions.
|
||||||
function __zoxide_z_complete
|
function __zoxide_z_complete
|
||||||
set -l tokens (commandline --current-process --tokenize)
|
set -l tokens (commandline --current-process --tokenize)
|
||||||
set -l curr_tokens (commandline --cut-at-cursor --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 test (count $tokens) -le 2 -a (count $curr_tokens) -eq 1
|
||||||
# If there are < 2 arguments, use `cd` completions.
|
# If there are < 2 arguments, use `cd` completions.
|
||||||
__fish_complete_directories "$tokens[2]" ''
|
complete --do-complete "'' "(commandline --cut-at-cursor --current-token) | string match --regex '.*/$'
|
||||||
else if test (count $tokens) -eq (count $curr_tokens)
|
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, use interactive selection.
|
# 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 query $tokens[2..-1]
|
||||||
set -l result (zoxide query -i -- $query)
|
set -l result (zoxide query --exclude (__zoxide_pwd) --interactive -- $query)
|
||||||
commandline --current-process "$tokens[1] "(string escape $result)
|
and echo $__zoxide_z_prefix$result
|
||||||
commandline --function repaint
|
commandline --function repaint
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
complete --command __zoxide_z --no-files --arguments '(__zoxide_z_complete)'
|
||||||
|
|
||||||
# Jump to a directory using interactive search.
|
# Jump to a directory using interactive search.
|
||||||
function __zoxide_zi
|
function __zoxide_zi
|
||||||
set -l result (command zoxide query -i -- $argv)
|
set -l result (command zoxide query --interactive -- $argv)
|
||||||
and __zoxide_cd $result
|
and __zoxide_cd $result
|
||||||
end
|
end
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
# Commands for zoxide. Disable these using --no-cmd.
|
||||||
#
|
#
|
||||||
|
|
||||||
{%- match cmd %}
|
{%- match cmd %}
|
||||||
{%- when Some with (cmd) %}
|
{%- when Some with (cmd) %}
|
||||||
|
|
||||||
# Remove definitions.
|
abbr --erase {{cmd}} &>/dev/null
|
||||||
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}}
|
|
||||||
alias {{cmd}}=__zoxide_z
|
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
|
alias {{cmd}}i=__zoxide_zi
|
||||||
|
|
||||||
{%- when None %}
|
{%- when None %}
|
||||||
|
@ -127,4 +132,4 @@ alias {{cmd}}i=__zoxide_zi
|
||||||
# To initialize zoxide, add this to your configuration (usually
|
# To initialize zoxide, add this to your configuration (usually
|
||||||
# ~/.config/fish/config.fish):
|
# ~/.config/fish/config.fish):
|
||||||
#
|
#
|
||||||
# zoxide init fish | source
|
# zoxide init fish | source
|
||||||
|
|
|
@ -3,90 +3,65 @@
|
||||||
|
|
||||||
# Code generated by zoxide. DO NOT EDIT.
|
# 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 }}
|
{{ section }}
|
||||||
# Hook configuration for zoxide.
|
# Hook configuration for zoxide.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Hook to add new entries to the database.
|
{% if hook == InitHook::None -%}
|
||||||
{%- match hook %}
|
|
||||||
{%- when InitHook::None %}
|
|
||||||
{{ not_configured }}
|
{{ not_configured }}
|
||||||
|
|
||||||
{%- when InitHook::Prompt %}
|
{%- else -%}
|
||||||
def __zoxide_hook [] {
|
# Initialize hook to add new entries to the database.
|
||||||
shells | where active == $true && name == filesystem | get path | each {
|
if (not ($env | default false __zoxide_hooked | get __zoxide_hooked)) {
|
||||||
zoxide add -- $it
|
$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.
|
{%- endif %}
|
||||||
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 %}
|
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# When using zoxide with --no-aliases, alias these internal functions as
|
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||||
# desired.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
# Jump to a directory using only keywords.
|
# Jump to a directory using only keywords.
|
||||||
def __zoxide_z [...rest:string] {
|
def-env __zoxide_z [...rest:string] {
|
||||||
if (shells | where active == $true | get name) != filesystem {
|
let arg0 = ($rest | append '~').0
|
||||||
if ($rest | length) > 1 {
|
let path = if (($rest | length) <= 1) and ($arg0 == '-' or ($arg0 | path expand | path type) == dir) {
|
||||||
$'zoxide: can only jump directories on filesystem(char nl)'
|
$arg0
|
||||||
} {
|
} else {
|
||||||
cd $rest
|
(zoxide query --exclude $env.PWD -- $rest | str trim -r -c "\n")
|
||||||
|
}
|
||||||
|
cd $path
|
||||||
{%- if echo %}
|
{%- if echo %}
|
||||||
pwd
|
echo $env.PWD
|
||||||
{%- endif %}
|
{%- 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.
|
# Jump to a directory using interactive search.
|
||||||
def __zoxide_zi [...rest:string] {
|
def-env __zoxide_zi [...rest:string] {
|
||||||
if (shells | where active == $true | get name) != filesystem {
|
cd $'(zoxide query --interactive -- $rest | str trim -r -c "\n")'
|
||||||
$'zoxide: can only jump directories on filesystem(char nl)'
|
|
||||||
} {
|
|
||||||
cd $'(zoxide query -i -- $rest | str trim -rc (char newline))'
|
|
||||||
{%- if echo %}
|
{%- if echo %}
|
||||||
pwd
|
echo $env.PWD
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
# Commands for zoxide. Disable these using --no-cmd.
|
||||||
#
|
#
|
||||||
|
|
||||||
{%- match cmd %}
|
{%- match cmd %}
|
||||||
|
@ -102,9 +77,13 @@ alias {{cmd}}i = __zoxide_zi
|
||||||
{%- endmatch %}
|
{%- endmatch %}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# To initialize zoxide, add this to your configuration (find it by running
|
# Add this to your env file (find it by running `$nu.env-path` in Nushell):
|
||||||
# `config 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.
|
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
||||||
__zoxide_pwd() {
|
__zoxide_pwd() {
|
||||||
{%- if resolve_symlinks %}
|
{%- if cfg!(windows) %}
|
||||||
|
\command cygpath -w "$(\builtin pwd -P)"
|
||||||
|
{%- else if resolve_symlinks %}
|
||||||
\command pwd -P
|
\command pwd -P
|
||||||
{%- else %}
|
{%- else %}
|
||||||
\command pwd -L
|
\command pwd -L
|
||||||
|
@ -47,8 +49,7 @@ fi
|
||||||
{%- endmatch %}
|
{%- endmatch %}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# When using zoxide with --no-aliases, alias these internal functions as
|
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||||
# desired.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
# Jump to a directory using only keywords.
|
# Jump to a directory using only keywords.
|
||||||
|
@ -73,30 +74,22 @@ __zoxide_z() {
|
||||||
|
|
||||||
# Jump to a directory using interactive search.
|
# Jump to a directory using interactive search.
|
||||||
__zoxide_zi() {
|
__zoxide_zi() {
|
||||||
__zoxide_result="$(\command zoxide query -i -- "$@")" && __zoxide_cd "${__zoxide_result}"
|
__zoxide_result="$(\command zoxide query --interactive -- "$@")" && __zoxide_cd "${__zoxide_result}"
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
# Commands for zoxide. Disable these using --no-cmd.
|
||||||
#
|
#
|
||||||
|
|
||||||
{%- match cmd %}
|
{%- match cmd %}
|
||||||
{%- when Some with (cmd) %}
|
{%- when Some with (cmd) %}
|
||||||
|
|
||||||
# Remove definitions.
|
\command unalias {{cmd}} >/dev/null 2>&1 || \true
|
||||||
__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}}'
|
|
||||||
{{cmd}}() {
|
{{cmd}}() {
|
||||||
__zoxide_z "$@"
|
__zoxide_z "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
__zoxide_unset '{{cmd}}i'
|
\command unalias {{cmd}}i >/dev/null 2>&1 || \true
|
||||||
{{cmd}}i() {
|
{{cmd}}i() {
|
||||||
__zoxide_zi "$@"
|
__zoxide_zi "$@"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,20 @@
|
||||||
# Utility functions for zoxide.
|
# 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.
|
# pwd based on zoxide's format.
|
||||||
function __zoxide_pwd {
|
function global:__zoxide_pwd {
|
||||||
$cwd = Get-Location
|
$cwd = Get-Location
|
||||||
if ($cwd.Provider.Name -eq "FileSystem") {
|
if ($cwd.Provider.Name -eq "FileSystem") {
|
||||||
$cwd.ProviderPath
|
$cwd.ProviderPath
|
||||||
|
@ -14,11 +26,19 @@ function __zoxide_pwd {
|
||||||
}
|
}
|
||||||
|
|
||||||
# cd + custom logic based on the value of _ZO_ECHO.
|
# cd + custom logic based on the value of _ZO_ECHO.
|
||||||
function __zoxide_cd($dir, $literal) {
|
function global:__zoxide_cd($dir, $literal) {
|
||||||
$dir = if ($literal) {
|
$dir = if ($literal) {
|
||||||
Set-Location -LiteralPath $dir -Passthru -ErrorAction Stop
|
Set-Location -LiteralPath $dir -Passthru -ErrorAction Stop
|
||||||
} else {
|
} 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 %}
|
{%- if echo %}
|
||||||
Write-Output $dir.Path
|
Write-Output $dir.Path
|
||||||
|
@ -29,66 +49,73 @@ function __zoxide_cd($dir, $literal) {
|
||||||
# Hook configuration for zoxide.
|
# 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.
|
# Hook to add new entries to the database.
|
||||||
function __zoxide_hook {
|
function global:__zoxide_hook {
|
||||||
$result = __zoxide_pwd
|
$result = __zoxide_pwd
|
||||||
if ($null -ne $result) {
|
if ($null -ne $result) {
|
||||||
zoxide add -- $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 hook.
|
||||||
{# Initialize $__zoxide_hooked if it does not exist. Removing this will cause
|
$global:__zoxide_hooked = (Get-Variable __zoxide_hooked -ErrorAction SilentlyContinue -ValueOnly)
|
||||||
# an unset variable error in StrictMode. #}
|
if ($global:__zoxide_hooked -ne 1) {
|
||||||
$__zoxide_hooked = (Get-Variable __zoxide_hooked -ValueOnly -ErrorAction SilentlyContinue)
|
$global:__zoxide_hooked = 1
|
||||||
if ($__zoxide_hooked -ne 1) {
|
$global:__zoxide_prompt_old = $function:prompt
|
||||||
$__zoxide_hooked = 1
|
|
||||||
{%- match hook %}
|
function global:prompt {
|
||||||
{%- when InitHook::None %}
|
if ($null -ne $__zoxide_prompt_old) {
|
||||||
{{ not_configured }}
|
& $__zoxide_prompt_old
|
||||||
{%- 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
|
|
||||||
}
|
}
|
||||||
|
$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 }}
|
{{ section }}
|
||||||
# When using zoxide with --no-aliases, alias these internal functions as
|
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||||
# desired.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
# Jump to a directory using only keywords.
|
# Jump to a directory using only keywords.
|
||||||
function __zoxide_z {
|
function global:__zoxide_z {
|
||||||
if ($args.Length -eq 0) {
|
if ($args.Length -eq 0) {
|
||||||
__zoxide_cd ~ $true
|
__zoxide_cd ~ $true
|
||||||
}
|
}
|
||||||
elseif (
|
elseif ($args.Length -eq 1 -and ($args[0] -eq '-' -or $args[0] -eq '+')) {
|
||||||
$args.Length -eq 1 -and
|
|
||||||
(($args[0] -eq '-' -or $args[0] -eq '+') -or (Test-Path $args[0] -PathType Container))
|
|
||||||
) {
|
|
||||||
__zoxide_cd $args[0] $false
|
__zoxide_cd $args[0] $false
|
||||||
}
|
}
|
||||||
|
elseif ($args.Length -eq 1 -and (Test-Path $args[0] -PathType Container)) {
|
||||||
|
__zoxide_cd $args[0] $true
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
$result = __zoxide_pwd
|
$result = __zoxide_pwd
|
||||||
if ($null -ne $result) {
|
if ($null -ne $result) {
|
||||||
$result = zoxide query --exclude $result -- @args
|
$result = __zoxide_bin query --exclude $result -- @args
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$result = zoxide query -- @args
|
$result = __zoxide_bin query -- @args
|
||||||
}
|
}
|
||||||
if ($LASTEXITCODE -eq 0) {
|
if ($LASTEXITCODE -eq 0) {
|
||||||
__zoxide_cd $result $true
|
__zoxide_cd $result $true
|
||||||
|
@ -97,15 +124,15 @@ function __zoxide_z {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Jump to a directory using interactive search.
|
# Jump to a directory using interactive search.
|
||||||
function __zoxide_zi {
|
function global:__zoxide_zi {
|
||||||
$result = zoxide query -i -- @args
|
$result = __zoxide_bin query -i -- @args
|
||||||
if ($LASTEXITCODE -eq 0) {
|
if ($LASTEXITCODE -eq 0) {
|
||||||
__zoxide_cd $result $true
|
__zoxide_cd $result $true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
# Commands for zoxide. Disable these using --no-cmd.
|
||||||
#
|
#
|
||||||
|
|
||||||
{%- match 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
|
# To initialize zoxide, add this to your configuration (find it by running
|
||||||
# `echo $profile` in PowerShell):
|
# `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 %}
|
{% endif %}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# When using zoxide with --no-aliases, alias these internal functions as
|
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||||
# desired.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,7 +156,7 @@ def __zoxide_zi(args: typing.List[str]) -> None:
|
||||||
|
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
# Commands for zoxide. Disable these using --no-cmd.
|
||||||
#
|
#
|
||||||
|
|
||||||
{%- match cmd %}
|
{%- match cmd %}
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
|
||||||
function __zoxide_pwd() {
|
function __zoxide_pwd() {
|
||||||
{%- if resolve_symlinks %}
|
{%- if cfg!(windows) %}
|
||||||
|
\command cygpath -w "$(\builtin pwd -P)"
|
||||||
|
{%- else if resolve_symlinks %}
|
||||||
\builtin pwd -P
|
\builtin pwd -P
|
||||||
{%- else %}
|
{%- else %}
|
||||||
\builtin pwd -L
|
\builtin pwd -L
|
||||||
|
@ -30,7 +32,8 @@ function __zoxide_cd() {
|
||||||
{% else -%}
|
{% else -%}
|
||||||
# Hook to add new entries to the database.
|
# Hook to add new entries to the database.
|
||||||
function __zoxide_hook() {
|
function __zoxide_hook() {
|
||||||
\command zoxide add -- "$(__zoxide_pwd || \builtin true)"
|
# shellcheck disable=SC2312
|
||||||
|
\command zoxide add -- "$(__zoxide_pwd)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize hook.
|
# Initialize hook.
|
||||||
|
@ -46,8 +49,7 @@ fi
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{{ section }}
|
{{ section }}
|
||||||
# When using zoxide with --no-aliases, alias these internal functions as
|
# When using zoxide with --no-cmd, alias these internal functions as desired.
|
||||||
# desired.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
__zoxide_z_prefix='z#'
|
__zoxide_z_prefix='z#'
|
||||||
|
@ -57,23 +59,16 @@ function __zoxide_z() {
|
||||||
# shellcheck disable=SC2199
|
# shellcheck disable=SC2199
|
||||||
if [[ "$#" -eq 0 ]]; then
|
if [[ "$#" -eq 0 ]]; then
|
||||||
__zoxide_cd ~
|
__zoxide_cd ~
|
||||||
elif [[ "$#" -eq 1 ]] && [[ "$1" = '-' ]]; then
|
elif [[ "$#" -eq 1 ]] && { [[ -d "$1" ]] || [[ "$1" = '-' ]] || [[ "$1" =~ ^[-+][0-9]$ ]]; }; 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
|
|
||||||
__zoxide_cd "$1"
|
__zoxide_cd "$1"
|
||||||
elif [[ "$@[-1]" == "${__zoxide_z_prefix}"* ]]; then
|
elif [[ "$@[-1]" == "${__zoxide_z_prefix}"?* ]]; then
|
||||||
# shellcheck disable=SC2124
|
# shellcheck disable=SC2124
|
||||||
\builtin local result="${@[-1]}"
|
\builtin local result="${@[-1]}"
|
||||||
__zoxide_cd "{{ "${result:${#__zoxide_z_prefix}}" }}"
|
__zoxide_cd "{{ "${result:${#__zoxide_z_prefix}}" }}"
|
||||||
else
|
else
|
||||||
\builtin local result
|
\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}"
|
__zoxide_cd "${result}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
@ -81,69 +76,44 @@ function __zoxide_z() {
|
||||||
# Jump to a directory using interactive search.
|
# Jump to a directory using interactive search.
|
||||||
function __zoxide_zi() {
|
function __zoxide_zi() {
|
||||||
\builtin local result
|
\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 }}
|
{{ section }}
|
||||||
# Convenient aliases for zoxide. Disable these using --no-aliases.
|
# Commands for zoxide. Disable these using --no-cmd.
|
||||||
#
|
#
|
||||||
|
|
||||||
{%- match cmd %}
|
{%- match cmd %}
|
||||||
{%- when Some with (cmd) %}
|
{%- when Some with (cmd) %}
|
||||||
|
|
||||||
# Remove definitions.
|
\builtin alias {{cmd}}=__zoxide_z
|
||||||
function __zoxide_unset() {
|
\builtin alias {{cmd}}i=__zoxide_zi
|
||||||
\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
|
|
||||||
|
|
||||||
{%- when None %}
|
{%- when None %}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,17 @@ use assert_cmd::Command;
|
||||||
#[test]
|
#[test]
|
||||||
fn completions_bash() {
|
fn completions_bash() {
|
||||||
let source = include_str!("../contrib/completions/zoxide.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: the completions file uses editor commands to add completions to the
|
||||||
// Elvish does not support running editor commands from a script, so we can't create a test for
|
// shell. However, Elvish does not support running editor commands from a
|
||||||
// this. See: https://github.com/elves/elvish/issues/1299
|
// script, so we can't create a test for this. See: https://github.com/elves/elvish/issues/1299
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completions_fish() {
|
fn completions_fish() {
|
||||||
|
@ -21,7 +26,7 @@ fn completions_fish() {
|
||||||
|
|
||||||
Command::new("fish")
|
Command::new("fish")
|
||||||
.env("HOME", tempdir)
|
.env("HOME", tempdir)
|
||||||
.args(&["--command", source, "--private"])
|
.args(["--command", source, "--private"])
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout("")
|
.stdout("")
|
||||||
|
@ -32,7 +37,7 @@ fn completions_fish() {
|
||||||
fn completions_powershell() {
|
fn completions_powershell() {
|
||||||
let source = include_str!("../contrib/completions/_zoxide.ps1");
|
let source = include_str!("../contrib/completions/_zoxide.ps1");
|
||||||
Command::new("pwsh")
|
Command::new("pwsh")
|
||||||
.args(&["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", source])
|
.args(["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", source])
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout("")
|
.stdout("")
|
||||||
|
@ -50,5 +55,5 @@ fn completions_zsh() {
|
||||||
compinit -u
|
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