Compare commits

...

21 Commits
v0.9.9 ... main

Author SHA1 Message Date
dependabot[bot] 8caffa1b57
Bump actions/checkout from 6 to 7 (#1251)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-23 07:30:27 +05:30
Puneet Dixit c8a47a068b
Skip zsh doctor in non-interactive shells (#1241)
Co-authored-by: Puneet Dixit <236133619+puneetdixit200@users.noreply.github.com>
Co-authored-by: Ajeet D'Souza <98ajeet@gmail.com>
2026-05-22 00:13:25 +05:30
Ajeet D'Souza cff57b7734
Remove nix dependency (#1237) 2026-05-12 14:22:25 +05:30
Ajeet D'Souza b151e6da3d
pwsh: Use fully qualified names when invoking cmdlets (#1236) 2026-05-12 05:12:30 +05:30
Ajeet D'Souza 1683e7b8ed
import: auto-detect databases; add Atuin support (#1232) 2026-05-11 00:24:43 +05:30
dependabot[bot] 0cc031e98e
Bump softprops/action-gh-release from 2 to 3 (#1220)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 04:00:24 +05:30
dependabot[bot] 67ca1bc959
Bump cachix/cachix-action from 16 to 17 (#1202)
Bumps [cachix/cachix-action](https://github.com/cachix/cachix-action) from 16 to 17.
- [Release notes](https://github.com/cachix/cachix-action/releases)
- [Commits](https://github.com/cachix/cachix-action/compare/v16...v17)

---
updated-dependencies:
- dependency-name: cachix/cachix-action
  dependency-version: '17'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 03:50:23 +05:30
Bruno Verachten 4f04fd4167
ci: add riscv64 to release build matrix (#1201)
---------

Signed-off-by: Bruno Verachten <gounthar@gmail.com>
Co-authored-by: Ajeet D'Souza <98ajeet@gmail.com>
2026-03-23 21:04:59 +05:30
dependabot[bot] f74ed040fe
Bump actions/upload-artifact from 6 to 7 (#1190)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-22 03:45:35 +05:30
dependabot[bot] 108c41ac82
Bump Swatinem/rust-cache from 2.8.2 to 2.9.1 (#1196)
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.2 to 2.9.1.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/v2.8.2...v2.9.1)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-version: 2.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-17 01:49:29 +05:30
Ajeet D'Souza 61f19a60d9 Update README 2026-03-05 11:11:54 +05:30
Ajeet D'Souza a970320caf
Update README 2026-03-05 08:02:31 +05:30
dependabot[bot] 7e4cbb8380
Bump actions/checkout from 5 to 6 (#1191)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-03 03:32:43 +05:30
Ajeet D'Souza fb1495b0ad
Handle prompts ending in space (#1189) 2026-03-01 15:32:40 +05:30
Ajeet D'Souza a461c9585f Update README 2026-03-01 14:02:20 +05:30
Helmut K. C. Tessarek e10ebcc2bd
ci: add workflow to publish to crates.io (#1169)
Thanks!
2026-03-01 13:02:36 +05:30
Chawye Hsu f07a76e115
pwsh: handling `z ~` and `z` separately (#1161)
* pwsh: handling `z ~` and `z` separately

This patch enables the capability of navigating to the current user's home
directory with `z` when cwd is on a PSDrive that does not have a definition
of the `HOME` location.

Context: The fact that `~` can be counterintuitive on PowerShell, as it's
relative to the cwd, **and its value is defined with a `.Home` property of
cwd's PSProvider\[1]. Not all PSProviders however define this property, leaving
that sometimes you cannot navigate to the undefined `HOME` location with `~`.

To validate this, you can use `Get-PSDrive` to iterate your PSDrives and see
which providers define a `Home` property. On Windows,

```plain
Get-PSDrive C,HKLM | select Name,{$_.Provider.Name},{$_.Provider.Home}

Name $_.Provider.Name $_.Provider.Home
---- ---------------- ----------------
C    FileSystem       C:\Users\<username>
HKLM Registry
```

or on Linux/macOS,

```plain
Get-PSDrive /,Function | select Name,{$_.Provider.Name},{$_.Provider.Home}

Name     $_.Provider.Name $_.Provider.Home
----     ---------------- ----------------
/        FileSystem       /Users/<username>
Function Function
```

When cwd is on a PSDrive without a defined `HOME` location, the original
`Set-Location` alias `cd` in PowerShell handles this case by falling back
to the user's home directory when no argument is provided\[2], and when `~`
is explicitly provided, it results in an error.

```plain
PS C:\> Set-Location Function:
PS Function:\> cd ~
Home location for this provider is not set. To set the home location, call "(get-psprovider 'Function').Home = 'path'".
PS Function:\> cd
PS C:\Users\<username>\>
```

This patch implements the same behavior for `z` by handling `z ~` and `z` separately.

\[1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_providers
\[2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-location?#-path

Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>

* No explicit case needed for ~

---------

Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
Co-authored-by: Ajeet D'Souza <98ajeet@gmail.com>
2026-03-01 12:43:50 +05:30
Ajeet D'Souza f3a07cd3d1 Update README.md 2026-02-19 01:13:09 +05:30
Ajeet D'Souza ce46915901 install.sh: Prefix global variables 2026-02-07 12:15:06 +05:30
Ajeet D'Souza d208a66f58 install.sh: early exit on error 2026-02-07 11:59:53 +05:30
Andrew Bradley d7458b756e
Support symlinks on Windows (#1149) 2026-02-02 11:07:36 +05:30
33 changed files with 1785 additions and 658 deletions

16
.github/workflows/cd.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Continuous Deployment
on:
push:
tags:
- "v*.*.*"
jobs:
publish-crates-io:
name: Publish on crates.io
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v7
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Publish
run: cargo publish --token ${{ secrets.CARGO_TOKEN }}

View File

@ -21,7 +21,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
@ -40,13 +40,13 @@ jobs:
if: ${{ matrix.os != 'windows-latest' }} if: ${{ matrix.os != 'windows-latest' }}
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v16 - uses: cachix/cachix-action@v17
if: ${{ matrix.os != 'windows-latest' && env.CACHIX_AUTH_TOKEN != '' }} if: ${{ matrix.os != 'windows-latest' && env.CACHIX_AUTH_TOKEN != '' }}
with: with:
authToken: ${{ env.CACHIX_AUTH_TOKEN }} authToken: ${{ env.CACHIX_AUTH_TOKEN }}
name: zoxide name: zoxide
- name: Setup cache - name: Setup cache
uses: Swatinem/rust-cache@v2.8.2 uses: Swatinem/rust-cache@v2.9.1
with: with:
key: ${{ matrix.os }} key: ${{ matrix.os }}
- name: Install just - name: Install just

View File

@ -30,6 +30,9 @@ jobs:
- os: ubuntu-latest - os: ubuntu-latest
target: i686-unknown-linux-musl target: i686-unknown-linux-musl
deb: true deb: true
- os: ubuntu-latest
target: riscv64gc-unknown-linux-musl
deb: true
- os: ubuntu-latest - os: ubuntu-latest
target: aarch64-linux-android target: aarch64-linux-android
- os: ubuntu-latest - os: ubuntu-latest
@ -44,7 +47,7 @@ jobs:
target: aarch64-pc-windows-msvc target: aarch64-pc-windows-msvc
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Get version - name: Get version
@ -61,7 +64,7 @@ jobs:
override: true override: true
target: ${{ matrix.target }} target: ${{ matrix.target }}
- name: Setup cache - name: Setup cache
uses: Swatinem/rust-cache@v2.8.2 uses: Swatinem/rust-cache@v2.9.1
with: with:
key: ${{ matrix.target }} key: ${{ matrix.target }}
- name: Install cross - name: Install cross
@ -102,7 +105,7 @@ jobs:
CHANGELOG.md LICENSE README.md ./man/ ./contrib/completions/ ` CHANGELOG.md LICENSE README.md ./man/ ./contrib/completions/ `
./target/${{ matrix.target }}/release/zoxide.exe ./target/${{ matrix.target }}/release/zoxide.exe
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
with: with:
name: ${{ matrix.target }} name: ${{ matrix.target }}
path: | path: |
@ -112,7 +115,7 @@ jobs:
- name: Create release - name: Create release
if: | if: |
github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'chore(release)') github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'chore(release)')
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
with: with:
draft: true draft: true
files: | files: |

View File

@ -7,6 +7,22 @@ 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
### Added
- POSIX: support for non-Cygwin Windows environments (e.g. Busybox).
- `import` now supports fetching entries from `atuin`.
### Changed
- `import` now auto-detects database files.
### Fixed
- PowerShell: use fully qualified names when invoking cmdlets.
- Bash/Fish/POSIX/Zsh: resolve symlinks on Windows.
## [0.9.9] - 2026-01-31 ## [0.9.9] - 2026-01-31
### Added ### Added

689
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@ license = "MIT"
name = "zoxide" name = "zoxide"
readme = "README.md" readme = "README.md"
repository = "https://github.com/ajeetdsouza/zoxide" repository = "https://github.com/ajeetdsouza/zoxide"
rust-version = "1.85.0" rust-version = "1.88.0"
version = "0.9.9" version = "0.9.9"
[badges] [badges]
@ -17,7 +17,7 @@ maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
anyhow = "1.0.32" anyhow = "1.0.32"
askama = { version = "0.14.0", default-features = false, features = [ askama = { version = "0.16.0", default-features = false, features = [
"derive", "derive",
"std", "std",
] } ] }
@ -30,15 +30,10 @@ fastrand = "2.0.0"
glob = "0.3.0" glob = "0.3.0"
ouroboros = "0.18.3" ouroboros = "0.18.3"
serde = { version = "1.0.116", features = ["derive"] } serde = { version = "1.0.116", features = ["derive"] }
time = { version = "0.3.47", default-features = false, features = ["parsing", "macros", "std"] }
[target.'cfg(unix)'.dependencies]
nix = { version = "0.30.1", default-features = false, features = [
"fs",
"user",
] }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
which = "7.0.3" which = "8.0.2"
[build-dependencies] [build-dependencies]
clap = { version = "4.3.0", features = ["derive"] } clap = { version = "4.3.0", features = ["derive"] }

View File

@ -11,8 +11,11 @@
<sup>Special thanks to:</sup> <sup>Special thanks to:</sup>
<table>
<tr>
<td align="center">
<!-- markdownlint-disable-next-line MD013 --> <!-- markdownlint-disable-next-line MD013 -->
<div><a href="https://go.warp.dev/zoxide"><img alt="Sponsored by Warp" width="230" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-03.png" /></a></div> <a href="https://go.warp.dev/zoxide"><img alt="Sponsored by Warp" width="230" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-03.png" /></a>
<div><sup><b>Warp, built for coding with multiple AI agents.</b></sup></div> <div><sup><b>Warp, built for coding with multiple AI agents.</b></sup></div>
<div><sup>Available for macOS, Linux, and Windows.</sup></div> <div><sup>Available for macOS, Linux, and Windows.</sup></div>
<div><sup> <div><sup>
@ -20,6 +23,16 @@
<a href="https://go.warp.dev/zoxide"><u>warp.dev</u></a> <a href="https://go.warp.dev/zoxide"><u>warp.dev</u></a>
to learn more. to learn more.
</sup></div> </sup></div>
</td>
<td align="center">
<!-- markdownlint-disable-next-line MD013 -->
<a href="https://www.recall.ai/careers?ashby_jid=7b02811e-bc91-4ef2-925d-f56a5acac13b&utm_source=github&utm_medium=sponsorship&utm_campaign=zoxide"><img alt="Sponsored by Recall.ai" width="230" src="https://github.com/user-attachments/assets/0c5cb177-561c-4637-bef6-cf584939c829" /></a>
<div><sup>Processing over 3TB/s of video at peak load,</sup></div>
<!-- markdownlint-disable-next-line MD013 -->
<div><sup>now <a href="https://www.recall.ai/careers?ashby_jid=7b02811e-bc91-4ef2-925d-f56a5acac13b&utm_source=github&utm_medium=sponsorship&utm_campaign=zoxide">hiring in SF.</a></sup></div>
</td>
</tr>
</table>
<hr /> <hr />
@ -58,7 +71,7 @@ z - # cd into previous directory
zi foo # cd with interactive selection (using fzf) zi foo # cd with interactive selection (using fzf)
z foo<SPACE><TAB> # show interactive completions (zoxide v0.8.0+, bash 4.4+/fish/zsh only) z foo<SPACE><TAB> # show interactive completions (bash 4.4+/fish/zsh only)
``` ```
Read more about the matching algorithm [here][algorithm-matching]. Read more about the matching algorithm [here][algorithm-matching].
@ -222,7 +235,7 @@ zoxide can be installed in 4 easy steps:
> eval (zoxide init elvish | slurp) > eval (zoxide init elvish | slurp)
> ``` > ```
> >
> **Note** > **Note:**
> zoxide only supports elvish v0.18.0 and above. > zoxide only supports elvish v0.18.0 and above.
</details> </details>
@ -256,7 +269,7 @@ zoxide can be installed in 4 easy steps:
> source ~/.zoxide.nu > source ~/.zoxide.nu
> ``` > ```
> >
> **Note** > **Note:**
> zoxide only supports Nushell v0.89.0+. > zoxide only supports Nushell v0.89.0+.
</details> </details>
@ -322,72 +335,36 @@ zoxide can be installed in 4 easy steps:
</details> </details>
> **Note:**
> [Warp] provides its own completions, so `Space+Tab` completions are not
> supported there.
3. **Install fzf** <sup>(optional)</sup> 3. **Install fzf** <sup>(optional)</sup>
[fzf] is a command-line fuzzy finder, used by zoxide for completions / [fzf] is a command-line fuzzy finder, used by zoxide for completions /
interactive selection. It can be installed from [here][fzf-installation]. interactive selection. It can be installed from [here][fzf-installation].
> **Note** > **Note:**
> The minimum supported fzf version is v0.51.0. > The minimum supported fzf version is v0.51.0.
4. **Import your data** <sup>(optional)</sup> 4. **Import your data** <sup>(optional)</sup>
If you currently use any of these plugins, you may want to import your data If you currently use any of these plugins, you may want to import your data
into zoxide: into zoxide. The data file is auto-detected using each plugin's standard
conventions.
<details> ```sh
<summary>autojump</summary> zoxide import <plugin>
```
> Run this command in your terminal: | Plugin | Command |
> | ---------- | ------------------------- |
> ```sh | atuin | `zoxide import atuin` |
> zoxide import --from=autojump "/path/to/autojump/db" | autojump | `zoxide import autojump` |
> ``` | fasd | `zoxide import fasd` |
> | z | `zoxide import z` |
> The path usually varies according to your system: | z.lua | `zoxide import z.lua` |
> | zsh-z | `zoxide import zsh-z` |
> | 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
@ -554,6 +531,7 @@ Environment variables[^2] can be used for configuration. They must be set before
[ubuntu packages]: https://packages.ubuntu.com/jammy/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
[warp]: https://www.warp.dev
[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"
[xplr]: https://github.com/sayanarijit/xplr [xplr]: https://github.com/sayanarijit/xplr
[xxh-plugin-prerun-zoxide]: https://github.com/xxh/xxh-plugin-prerun-zoxide [xxh-plugin-prerun-zoxide]: https://github.com/xxh/xxh-plugin-prerun-zoxide

View File

@ -45,7 +45,7 @@ _arguments "${_arguments_options[@]}" : \
'--help[Print help]' \ '--help[Print help]' \
'-V[Print version]' \ '-V[Print version]' \
'--version[Print version]' \ '--version[Print version]' \
":: :_zoxide__edit_commands" \ ":: :_zoxide__subcmd__edit_commands" \
"*::: :->edit" \ "*::: :->edit" \
&& ret=0 && ret=0
@ -96,14 +96,78 @@ esac
;; ;;
(import) (import)
_arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \
'--from=[Application to import from]:FROM:(autojump z)' \
'--merge[Merge into existing database]' \ '--merge[Merge into existing database]' \
'-h[Print help]' \ '-h[Print help]' \
'--help[Print help]' \ '--help[Print help]' \
'-V[Print version]' \ '-V[Print version]' \
'--version[Print version]' \ '--version[Print version]' \
':path:_files' \ ":: :_zoxide__subcmd__import_commands" \
"*::: :->import" \
&& ret=0 && ret=0
case $state in
(import)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:zoxide-import-command-$line[1]:"
case $line[1] in
(atuin)
_arguments "${_arguments_options[@]}" : \
'--merge[Merge into existing database]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
&& ret=0
;;
(autojump)
_arguments "${_arguments_options[@]}" : \
'--merge[Merge into existing database]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
&& ret=0
;;
(fasd)
_arguments "${_arguments_options[@]}" : \
'--merge[Merge into existing database]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
&& ret=0
;;
(z)
_arguments "${_arguments_options[@]}" : \
'--merge[Merge into existing database]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
&& ret=0
;;
(z.lua)
_arguments "${_arguments_options[@]}" : \
'--merge[Merge into existing database]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
&& ret=0
;;
(zsh-z)
_arguments "${_arguments_options[@]}" : \
'--merge[Merge into existing database]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
&& ret=0
;;
esac
;;
esac
;; ;;
(init) (init)
_arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \
@ -162,13 +226,13 @@ _zoxide_commands() {
) )
_describe -t commands 'zoxide commands' commands "$@" _describe -t commands 'zoxide commands' commands "$@"
} }
(( $+functions[_zoxide__add_commands] )) || (( $+functions[_zoxide__subcmd__add_commands] )) ||
_zoxide__add_commands() { _zoxide__subcmd__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_commands] )) || (( $+functions[_zoxide__subcmd__edit_commands] )) ||
_zoxide__edit_commands() { _zoxide__subcmd__edit_commands() {
local commands; commands=( local commands; commands=(
'decrement:' \ 'decrement:' \
'delete:' \ 'delete:' \
@ -177,43 +241,80 @@ _zoxide__edit_commands() {
) )
_describe -t commands 'zoxide edit commands' commands "$@" _describe -t commands 'zoxide edit commands' commands "$@"
} }
(( $+functions[_zoxide__edit__decrement_commands] )) || (( $+functions[_zoxide__subcmd__edit__subcmd__decrement_commands] )) ||
_zoxide__edit__decrement_commands() { _zoxide__subcmd__edit__subcmd__decrement_commands() {
local commands; commands=() local commands; commands=()
_describe -t commands 'zoxide edit decrement commands' commands "$@" _describe -t commands 'zoxide edit decrement commands' commands "$@"
} }
(( $+functions[_zoxide__edit__delete_commands] )) || (( $+functions[_zoxide__subcmd__edit__subcmd__delete_commands] )) ||
_zoxide__edit__delete_commands() { _zoxide__subcmd__edit__subcmd__delete_commands() {
local commands; commands=() local commands; commands=()
_describe -t commands 'zoxide edit delete commands' commands "$@" _describe -t commands 'zoxide edit delete commands' commands "$@"
} }
(( $+functions[_zoxide__edit__increment_commands] )) || (( $+functions[_zoxide__subcmd__edit__subcmd__increment_commands] )) ||
_zoxide__edit__increment_commands() { _zoxide__subcmd__edit__subcmd__increment_commands() {
local commands; commands=() local commands; commands=()
_describe -t commands 'zoxide edit increment commands' commands "$@" _describe -t commands 'zoxide edit increment commands' commands "$@"
} }
(( $+functions[_zoxide__edit__reload_commands] )) || (( $+functions[_zoxide__subcmd__edit__subcmd__reload_commands] )) ||
_zoxide__edit__reload_commands() { _zoxide__subcmd__edit__subcmd__reload_commands() {
local commands; commands=() local commands; commands=()
_describe -t commands 'zoxide edit reload commands' commands "$@" _describe -t commands 'zoxide edit reload commands' commands "$@"
} }
(( $+functions[_zoxide__import_commands] )) || (( $+functions[_zoxide__subcmd__import_commands] )) ||
_zoxide__import_commands() { _zoxide__subcmd__import_commands() {
local commands; commands=() local commands; commands=(
'atuin:Import from atuin' \
'autojump:Import from autojump' \
'fasd:Import from fasd' \
'z:Import from z' \
'z.lua:Import from z.lua' \
'zsh-z:Import from zsh-z' \
)
_describe -t commands 'zoxide import commands' commands "$@" _describe -t commands 'zoxide import commands' commands "$@"
} }
(( $+functions[_zoxide__init_commands] )) || (( $+functions[_zoxide__subcmd__import__subcmd__atuin_commands] )) ||
_zoxide__init_commands() { _zoxide__subcmd__import__subcmd__atuin_commands() {
local commands; commands=()
_describe -t commands 'zoxide import atuin commands' commands "$@"
}
(( $+functions[_zoxide__subcmd__import__subcmd__autojump_commands] )) ||
_zoxide__subcmd__import__subcmd__autojump_commands() {
local commands; commands=()
_describe -t commands 'zoxide import autojump commands' commands "$@"
}
(( $+functions[_zoxide__subcmd__import__subcmd__fasd_commands] )) ||
_zoxide__subcmd__import__subcmd__fasd_commands() {
local commands; commands=()
_describe -t commands 'zoxide import fasd commands' commands "$@"
}
(( $+functions[_zoxide__subcmd__import__subcmd__z_commands] )) ||
_zoxide__subcmd__import__subcmd__z_commands() {
local commands; commands=()
_describe -t commands 'zoxide import z commands' commands "$@"
}
(( $+functions[_zoxide__subcmd__import__subcmd__z.lua_commands] )) ||
_zoxide__subcmd__import__subcmd__z.lua_commands() {
local commands; commands=()
_describe -t commands 'zoxide import z.lua commands' commands "$@"
}
(( $+functions[_zoxide__subcmd__import__subcmd__zsh-z_commands] )) ||
_zoxide__subcmd__import__subcmd__zsh-z_commands() {
local commands; commands=()
_describe -t commands 'zoxide import zsh-z commands' commands "$@"
}
(( $+functions[_zoxide__subcmd__init_commands] )) ||
_zoxide__subcmd__init_commands() {
local commands; commands=() local commands; commands=()
_describe -t commands 'zoxide init commands' commands "$@" _describe -t commands 'zoxide init commands' commands "$@"
} }
(( $+functions[_zoxide__query_commands] )) || (( $+functions[_zoxide__subcmd__query_commands] )) ||
_zoxide__query_commands() { _zoxide__subcmd__query_commands() {
local commands; commands=() local commands; commands=()
_describe -t commands 'zoxide query commands' commands "$@" _describe -t commands 'zoxide query commands' commands "$@"
} }
(( $+functions[_zoxide__remove_commands] )) || (( $+functions[_zoxide__subcmd__remove_commands] )) ||
_zoxide__remove_commands() { _zoxide__subcmd__remove_commands() {
local commands; commands=() local commands; commands=()
_describe -t commands 'zoxide remove commands' commands "$@" _describe -t commands 'zoxide remove commands' commands "$@"
} }

View File

@ -82,7 +82,60 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
break break
} }
'zoxide;import' { 'zoxide;import' {
[CompletionResult]::new('--from', '--from', [CompletionResultType]::ParameterName, 'Application to import from') [CompletionResult]::new('--merge', '--merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')
[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('atuin', 'atuin', [CompletionResultType]::ParameterValue, 'Import from atuin')
[CompletionResult]::new('autojump', 'autojump', [CompletionResultType]::ParameterValue, 'Import from autojump')
[CompletionResult]::new('fasd', 'fasd', [CompletionResultType]::ParameterValue, 'Import from fasd')
[CompletionResult]::new('z', 'z', [CompletionResultType]::ParameterValue, 'Import from z')
[CompletionResult]::new('z.lua', 'z.lua', [CompletionResultType]::ParameterValue, 'Import from z.lua')
[CompletionResult]::new('zsh-z', 'zsh-z', [CompletionResultType]::ParameterValue, 'Import from zsh-z')
break
}
'zoxide;import;atuin' {
[CompletionResult]::new('--merge', '--merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
break
}
'zoxide;import;autojump' {
[CompletionResult]::new('--merge', '--merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
break
}
'zoxide;import;fasd' {
[CompletionResult]::new('--merge', '--merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
break
}
'zoxide;import;z' {
[CompletionResult]::new('--merge', '--merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
break
}
'zoxide;import;z.lua' {
[CompletionResult]::new('--merge', '--merge', [CompletionResultType]::ParameterName, 'Merge into existing database')
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
break
}
'zoxide;import;zsh-z' {
[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') [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help') [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')

View File

@ -17,34 +17,52 @@ _zoxide() {
cmd="zoxide" cmd="zoxide"
;; ;;
zoxide,add) zoxide,add)
cmd="zoxide__add" cmd="zoxide__subcmd__add"
;; ;;
zoxide,edit) zoxide,edit)
cmd="zoxide__edit" cmd="zoxide__subcmd__edit"
;; ;;
zoxide,import) zoxide,import)
cmd="zoxide__import" cmd="zoxide__subcmd__import"
;; ;;
zoxide,init) zoxide,init)
cmd="zoxide__init" cmd="zoxide__subcmd__init"
;; ;;
zoxide,query) zoxide,query)
cmd="zoxide__query" cmd="zoxide__subcmd__query"
;; ;;
zoxide,remove) zoxide,remove)
cmd="zoxide__remove" cmd="zoxide__subcmd__remove"
;; ;;
zoxide__edit,decrement) zoxide__subcmd__edit,decrement)
cmd="zoxide__edit__decrement" cmd="zoxide__subcmd__edit__subcmd__decrement"
;; ;;
zoxide__edit,delete) zoxide__subcmd__edit,delete)
cmd="zoxide__edit__delete" cmd="zoxide__subcmd__edit__subcmd__delete"
;; ;;
zoxide__edit,increment) zoxide__subcmd__edit,increment)
cmd="zoxide__edit__increment" cmd="zoxide__subcmd__edit__subcmd__increment"
;; ;;
zoxide__edit,reload) zoxide__subcmd__edit,reload)
cmd="zoxide__edit__reload" cmd="zoxide__subcmd__edit__subcmd__reload"
;;
zoxide__subcmd__import,atuin)
cmd="zoxide__subcmd__import__subcmd__atuin"
;;
zoxide__subcmd__import,autojump)
cmd="zoxide__subcmd__import__subcmd__autojump"
;;
zoxide__subcmd__import,fasd)
cmd="zoxide__subcmd__import__subcmd__fasd"
;;
zoxide__subcmd__import,z)
cmd="zoxide__subcmd__import__subcmd__z"
;;
zoxide__subcmd__import,z.lua)
cmd="zoxide__subcmd__import__subcmd__z.lua"
;;
zoxide__subcmd__import,zsh-z)
cmd="zoxide__subcmd__import__subcmd__zsh__subcmd__z"
;; ;;
*) *)
;; ;;
@ -66,7 +84,7 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__add) zoxide__subcmd__add)
opts="-s -h -V --score --help --version <PATHS>..." opts="-s -h -V --score --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}") )
@ -88,7 +106,7 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__edit) zoxide__subcmd__edit)
opts="-h -V --help --version decrement delete increment reload" opts="-h -V --help --version decrement delete increment reload"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
@ -102,7 +120,7 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__edit__decrement) zoxide__subcmd__edit__subcmd__decrement)
opts="-h -V --help --version <PATH>" opts="-h -V --help --version <PATH>"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
@ -116,7 +134,7 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__edit__delete) zoxide__subcmd__edit__subcmd__delete)
opts="-h -V --help --version <PATH>" opts="-h -V --help --version <PATH>"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
@ -130,7 +148,7 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__edit__increment) zoxide__subcmd__edit__subcmd__increment)
opts="-h -V --help --version <PATH>" opts="-h -V --help --version <PATH>"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
@ -144,7 +162,7 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__edit__reload) zoxide__subcmd__edit__subcmd__reload)
opts="-h -V --help --version" opts="-h -V --help --version"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
@ -158,17 +176,13 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__import) zoxide__subcmd__import)
opts="-h -V --from --merge --help --version <PATH>" opts="-h -V --merge --help --version atuin autojump fasd z z.lua zsh-z"
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
fi fi
case "${prev}" in case "${prev}" in
--from)
COMPREPLY=($(compgen -W "autojump z" -- "${cur}"))
return 0
;;
*) *)
COMPREPLY=() COMPREPLY=()
;; ;;
@ -176,7 +190,91 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__init) zoxide__subcmd__import__subcmd__atuin)
opts="-h -V --merge --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__subcmd__import__subcmd__autojump)
opts="-h -V --merge --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__subcmd__import__subcmd__fasd)
opts="-h -V --merge --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__subcmd__import__subcmd__z)
opts="-h -V --merge --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__subcmd__import__subcmd__z.lua)
opts="-h -V --merge --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__subcmd__import__subcmd__zsh__subcmd__z)
opts="-h -V --merge --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__subcmd__init)
opts="-h -V --no-cmd --cmd --hook --help --version bash elvish fish nushell posix powershell tcsh xonsh zsh" opts="-h -V --no-cmd --cmd --hook --help --version bash elvish fish nushell posix powershell tcsh 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}") )
@ -198,7 +296,7 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__query) zoxide__subcmd__query)
opts="-a -i -l -s -h -V --all --interactive --list --score --exclude --base-dir --help --version [KEYWORDS]..." opts="-a -i -l -s -h -V --all --interactive --list --score --exclude --base-dir --help --version [KEYWORDS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
@ -226,7 +324,7 @@ _zoxide() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
;; ;;
zoxide__remove) zoxide__subcmd__remove)
opts="-h -V --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}") )

View File

@ -72,7 +72,54 @@ set edit:completion:arg-completer[zoxide] = {|@words|
cand --version 'Print version' cand --version 'Print version'
} }
&'zoxide;import'= { &'zoxide;import'= {
cand --from 'Application to import from' cand --merge 'Merge into existing database'
cand -h 'Print help'
cand --help 'Print help'
cand -V 'Print version'
cand --version 'Print version'
cand atuin 'Import from atuin'
cand autojump 'Import from autojump'
cand fasd 'Import from fasd'
cand z 'Import from z'
cand z.lua 'Import from z.lua'
cand zsh-z 'Import from zsh-z'
}
&'zoxide;import;atuin'= {
cand --merge 'Merge into existing database'
cand -h 'Print help'
cand --help 'Print help'
cand -V 'Print version'
cand --version 'Print version'
}
&'zoxide;import;autojump'= {
cand --merge 'Merge into existing database'
cand -h 'Print help'
cand --help 'Print help'
cand -V 'Print version'
cand --version 'Print version'
}
&'zoxide;import;fasd'= {
cand --merge 'Merge into existing database'
cand -h 'Print help'
cand --help 'Print help'
cand -V 'Print version'
cand --version 'Print version'
}
&'zoxide;import;z'= {
cand --merge 'Merge into existing database'
cand -h 'Print help'
cand --help 'Print help'
cand -V 'Print version'
cand --version 'Print version'
}
&'zoxide;import;z.lua'= {
cand --merge 'Merge into existing database'
cand -h 'Print help'
cand --help 'Print help'
cand -V 'Print version'
cand --version 'Print version'
}
&'zoxide;import;zsh-z'= {
cand --merge 'Merge into existing database' cand --merge 'Merge into existing database'
cand -h 'Print help' cand -h 'Print help'
cand --help 'Print help' cand --help 'Print help'

View File

@ -49,11 +49,33 @@ complete -c zoxide -n "__fish_zoxide_using_subcommand edit; and __fish_seen_subc
complete -c zoxide -n "__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from increment" -s V -l version -d 'Print version' complete -c zoxide -n "__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from increment" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from reload" -s h -l help -d 'Print help' complete -c zoxide -n "__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from reload" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from reload" -s V -l version -d 'Print version' complete -c zoxide -n "__fish_zoxide_using_subcommand edit; and __fish_seen_subcommand_from reload" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand import" -l from -d 'Application to import from' -r -f -a "autojump\t'' complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -l merge -d 'Merge into existing database'
z\t''" complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand import" -l merge -d 'Merge into existing database' complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand import" -s h -l help -d 'Print help' complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -f -a "atuin" -d 'Import from atuin'
complete -c zoxide -n "__fish_zoxide_using_subcommand import" -s V -l version -d 'Print version' complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -f -a "autojump" -d 'Import from autojump'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -f -a "fasd" -d 'Import from fasd'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -f -a "z" -d 'Import from z'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -f -a "z.lua" -d 'Import from z.lua'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and not __fish_seen_subcommand_from atuin autojump fasd z z.lua zsh-z" -f -a "zsh-z" -d 'Import from zsh-z'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from atuin" -l merge -d 'Merge into existing database'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from atuin" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from atuin" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from autojump" -l merge -d 'Merge into existing database'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from autojump" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from autojump" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from fasd" -l merge -d 'Merge into existing database'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from fasd" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from fasd" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from z" -l merge -d 'Merge into existing database'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from z" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from z" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from z.lua" -l merge -d 'Merge into existing database'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from z.lua" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from z.lua" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from zsh-z" -l merge -d 'Merge into existing database'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from zsh-z" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand import; and __fish_seen_subcommand_from zsh-z" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand init" -l cmd -d 'Changes the prefix of the `z` and `zi` commands' -r complete -c zoxide -n "__fish_zoxide_using_subcommand init" -l cmd -d 'Changes the prefix of the `z` and `zi` commands' -r
complete -c zoxide -n "__fish_zoxide_using_subcommand init" -l hook -d 'Changes how often zoxide increments a directory\'s score' -r -f -a "none\t'' complete -c zoxide -n "__fish_zoxide_using_subcommand init" -l hook -d 'Changes how often zoxide increments a directory\'s score' -r -f -a "none\t''
prompt\t'' prompt\t''

View File

@ -8,10 +8,10 @@ module completions {
# Add a new directory or increment its rank # Add a new directory or increment its rank
export extern "zoxide add" [ export extern "zoxide add" [
...paths: path
--score(-s): string # The rank to increment the entry if it exists or initialize it with if it doesn't --score(-s): string # The rank to increment the entry if it exists or initialize it with if it doesn't
--help(-h) # Print help --help(-h) # Print help
--version(-V) # Print version --version(-V) # Print version
...paths: path
] ]
# Edit the database # Edit the database
@ -21,21 +21,21 @@ module completions {
] ]
export extern "zoxide edit decrement" [ export extern "zoxide edit decrement" [
path: string
--help(-h) # Print help --help(-h) # Print help
--version(-V) # Print version --version(-V) # Print version
path: string
] ]
export extern "zoxide edit delete" [ export extern "zoxide edit delete" [
path: string
--help(-h) # Print help --help(-h) # Print help
--version(-V) # Print version --version(-V) # Print version
path: string
] ]
export extern "zoxide edit increment" [ export extern "zoxide edit increment" [
path: string
--help(-h) # Print help --help(-h) # Print help
--version(-V) # Print version --version(-V) # Print version
path: string
] ]
export extern "zoxide edit reload" [ export extern "zoxide edit reload" [
@ -43,14 +43,50 @@ module completions {
--version(-V) # Print version --version(-V) # Print version
] ]
def "nu-complete zoxide import from" [] {
[ "autojump" "z" ]
}
# Import entries from another application # Import entries from another application
export extern "zoxide import" [ export extern "zoxide import" [
path: path --merge # Merge into existing database
--from: string@"nu-complete zoxide import from" # Application to import from --help(-h) # Print help
--version(-V) # Print version
]
# Import from atuin
export extern "zoxide import atuin" [
--merge # Merge into existing database
--help(-h) # Print help
--version(-V) # Print version
]
# Import from autojump
export extern "zoxide import autojump" [
--merge # Merge into existing database
--help(-h) # Print help
--version(-V) # Print version
]
# Import from fasd
export extern "zoxide import fasd" [
--merge # Merge into existing database
--help(-h) # Print help
--version(-V) # Print version
]
# Import from z
export extern "zoxide import z" [
--merge # Merge into existing database
--help(-h) # Print help
--version(-V) # Print version
]
# Import from z.lua
export extern "zoxide import z.lua" [
--merge # Merge into existing database
--help(-h) # Print help
--version(-V) # Print version
]
# Import from zsh-z
export extern "zoxide import zsh-z" [
--merge # Merge into existing database --merge # Merge into existing database
--help(-h) # Print help --help(-h) # Print help
--version(-V) # Print version --version(-V) # Print version
@ -66,17 +102,16 @@ module completions {
# Generate shell configuration # Generate shell configuration
export extern "zoxide init" [ export extern "zoxide init" [
shell: string@"nu-complete zoxide init shell"
--no-cmd # Prevents zoxide from defining the `z` and `zi` commands --no-cmd # Prevents zoxide from defining the `z` and `zi` commands
--cmd: string # Changes the prefix of the `z` and `zi` commands --cmd: string # Changes the prefix of the `z` and `zi` commands
--hook: string@"nu-complete zoxide init hook" # Changes how often zoxide increments a directory's score --hook: string@"nu-complete zoxide init hook" # Changes how often zoxide increments a directory's score
--help(-h) # Print help --help(-h) # Print help
--version(-V) # Print version --version(-V) # Print version
shell: string@"nu-complete zoxide init shell"
] ]
# Search for a directory in the database # Search for a directory in the database
export extern "zoxide query" [ export extern "zoxide query" [
...keywords: string
--all(-a) # Show unavailable directories --all(-a) # Show unavailable directories
--interactive(-i) # Use interactive selection --interactive(-i) # Use interactive selection
--list(-l) # List all matching directories --list(-l) # List all matching directories
@ -85,13 +120,14 @@ module completions {
--base-dir: path # Only search within this directory --base-dir: path # Only search within this directory
--help(-h) # Print help --help(-h) # Print help
--version(-V) # Print version --version(-V) # Print version
...keywords: string
] ]
# Remove a directory from the database # Remove a directory from the database
export extern "zoxide remove" [ export extern "zoxide remove" [
...paths: path
--help(-h) # Print help --help(-h) # Print help
--version(-V) # Print version --version(-V) # Print version
...paths: path
] ]
} }

View File

@ -114,19 +114,11 @@ const completion: Fig.Spec = {
{ {
name: "import", name: "import",
description: "Import entries from another application", description: "Import entries from another application",
options: [ subcommands: [
{ {
name: "--from", name: "atuin",
description: "Application to import from", description: "Import from atuin",
isRepeatable: true, options: [
args: {
name: "from",
suggestions: [
"autojump",
"z",
],
},
},
{ {
name: "--merge", name: "--merge",
description: "Merge into existing database", description: "Merge into existing database",
@ -140,10 +132,112 @@ const completion: Fig.Spec = {
description: "Print version", description: "Print version",
}, },
], ],
args: {
name: "path",
template: "filepaths",
}, },
{
name: "autojump",
description: "Import from autojump",
options: [
{
name: "--merge",
description: "Merge into existing database",
},
{
name: ["-h", "--help"],
description: "Print help",
},
{
name: ["-V", "--version"],
description: "Print version",
},
],
},
{
name: "fasd",
description: "Import from fasd",
options: [
{
name: "--merge",
description: "Merge into existing database",
},
{
name: ["-h", "--help"],
description: "Print help",
},
{
name: ["-V", "--version"],
description: "Print version",
},
],
},
{
name: "z",
description: "Import from z",
options: [
{
name: "--merge",
description: "Merge into existing database",
},
{
name: ["-h", "--help"],
description: "Print help",
},
{
name: ["-V", "--version"],
description: "Print version",
},
],
},
{
name: "z.lua",
description: "Import from z.lua",
options: [
{
name: "--merge",
description: "Merge into existing database",
},
{
name: ["-h", "--help"],
description: "Print help",
},
{
name: ["-V", "--version"],
description: "Print version",
},
],
},
{
name: "zsh-z",
description: "Import from zsh-z",
options: [
{
name: "--merge",
description: "Merge into existing database",
},
{
name: ["-h", "--help"],
description: "Print help",
},
{
name: ["-V", "--version"],
description: "Print version",
},
],
},
],
options: [
{
name: "--merge",
description: "Merge into existing database",
},
{
name: ["-h", "--help"],
description: "Print help",
},
{
name: ["-V", "--version"],
description: "Print version",
},
],
}, },
{ {
name: "init", name: "init",

View File

@ -19,7 +19,7 @@ main() {
parse_args "$@" parse_args "$@"
local _arch local _arch
_arch="${ARCH:-$(ensure get_architecture)}" _arch="${_ZOXIDE_ARCH:-$(get_architecture)}" || exit $?
assert_nz "${_arch}" "arch" assert_nz "${_arch}" "arch"
echo "Detected architecture: ${_arch}" echo "Detected architecture: ${_arch}"
@ -36,7 +36,7 @@ main() {
# Download and extract zoxide. # Download and extract zoxide.
local _package local _package
_package="$(ensure download_zoxide "${_arch}")" _package="$(download_zoxide "${_arch}")" || exit $?
assert_nz "${_package}" "package" assert_nz "${_package}" "package"
echo "Downloaded package: ${_package}" echo "Downloaded package: ${_package}"
case "${_package}" in case "${_package}" in
@ -54,44 +54,44 @@ main() {
esac esac
# Install binary. # Install binary.
ensure try_sudo mkdir -p -- "${BIN_DIR}" ensure try_sudo mkdir -p -- "${_ZOXIDE_BIN_DIR}"
ensure try_sudo cp -- "${_bin_name}" "${BIN_DIR}/${_bin_name}" ensure try_sudo cp -- "${_bin_name}" "${_ZOXIDE_BIN_DIR}/${_bin_name}"
ensure try_sudo chmod +x "${BIN_DIR}/${_bin_name}" ensure try_sudo chmod +x "${_ZOXIDE_BIN_DIR}/${_bin_name}"
echo "Installed zoxide to ${BIN_DIR}" echo "Installed zoxide to ${_ZOXIDE_BIN_DIR}"
# Install manpages. # Install manpages.
ensure try_sudo mkdir -p -- "${MAN_DIR}/man1" ensure try_sudo mkdir -p -- "${_ZOXIDE_MAN_DIR}/man1"
ensure try_sudo cp -- "man/man1/"* "${MAN_DIR}/man1/" ensure try_sudo cp -- "man/man1/"* "${_ZOXIDE_MAN_DIR}/man1/"
echo "Installed manpages to ${MAN_DIR}" echo "Installed manpages to ${_ZOXIDE_MAN_DIR}"
# Print success message and check $PATH. # Print success message and check $PATH.
echo "" echo ""
echo "zoxide is installed!" echo "zoxide is installed!"
if ! echo ":${PATH}:" | grep -Fq ":${BIN_DIR}:"; then if ! echo ":${PATH}:" | grep -Fq ":${_ZOXIDE_BIN_DIR}:"; then
echo "Note: ${BIN_DIR} is not on your \$PATH. zoxide will not work unless it is added to \$PATH." echo "Note: ${_ZOXIDE_BIN_DIR} is not on your \$PATH. zoxide will not work unless it is added to \$PATH."
fi fi
} }
# Parse the arguments passed and set variables accordingly. # Parse the arguments passed and set variables accordingly.
parse_args() { parse_args() {
BIN_DIR_DEFAULT="${HOME}/.local/bin" _ZOXIDE_BIN_DIR_DEFAULT="${HOME}/.local/bin"
MAN_DIR_DEFAULT="${HOME}/.local/share/man" _ZOXIDE_MAN_DIR_DEFAULT="${HOME}/.local/share/man"
SUDO_DEFAULT="sudo" _ZOXIDE_SUDO_DEFAULT="sudo"
BIN_DIR="${BIN_DIR_DEFAULT}" _ZOXIDE_BIN_DIR="${_ZOXIDE_BIN_DIR_DEFAULT}"
MAN_DIR="${MAN_DIR_DEFAULT}" _ZOXIDE_MAN_DIR="${_ZOXIDE_MAN_DIR_DEFAULT}"
SUDO="${SUDO_DEFAULT}" _ZOXIDE_SUDO="${_ZOXIDE_SUDO_DEFAULT}"
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
case "$1" in case "$1" in
--arch) ARCH="$2" && shift 2 ;; --arch) _ZOXIDE_ARCH="$2" && shift 2 ;;
--arch=*) ARCH="${1#*=}" && shift 1 ;; --arch=*) _ZOXIDE_ARCH="${1#*=}" && shift 1 ;;
--bin-dir) BIN_DIR="$2" && shift 2 ;; --bin-dir) _ZOXIDE_BIN_DIR="$2" && shift 2 ;;
--bin-dir=*) BIN_DIR="${1#*=}" && shift 1 ;; --bin-dir=*) _ZOXIDE_BIN_DIR="${1#*=}" && shift 1 ;;
--man-dir) MAN_DIR="$2" && shift 2 ;; --man-dir) _ZOXIDE_MAN_DIR="$2" && shift 2 ;;
--man-dir=*) MAN_DIR="${1#*=}" && shift 1 ;; --man-dir=*) _ZOXIDE_MAN_DIR="${1#*=}" && shift 1 ;;
--sudo) SUDO="$2" && shift 2 ;; --sudo) _ZOXIDE_SUDO="$2" && shift 2 ;;
--sudo=*) SUDO="${1#*=}" && shift 1 ;; --sudo=*) _ZOXIDE_SUDO="${1#*=}" && shift 1 ;;
-h | --help) usage && exit 0 ;; -h | --help) usage && exit 0 ;;
*) err "Unknown option: $1" ;; *) err "Unknown option: $1" ;;
esac esac
@ -119,9 +119,9 @@ ${_text_heading}Usage:${_text_reset}
${_text_heading}Options:${_text_reset} ${_text_heading}Options:${_text_reset}
--arch Override the architecture identified by the installer [current: ${_arch}] --arch Override the architecture identified by the installer [current: ${_arch}]
--bin-dir Override the installation directory [default: ${BIN_DIR_DEFAULT}] --bin-dir Override the installation directory [default: ${_ZOXIDE_BIN_DIR_DEFAULT}]
--man-dir Override the manpage installation directory [default: ${MAN_DIR_DEFAULT}] --man-dir Override the manpage installation directory [default: ${_ZOXIDE_MAN_DIR_DEFAULT}]
--sudo Override the command used to elevate to root privileges [default: ${SUDO_DEFAULT}] --sudo Override the command used to elevate to root privileges [default: ${_ZOXIDE_SUDO_DEFAULT}]
-h, --help Print help" -h, --help Print help"
} }
@ -176,19 +176,19 @@ try_sudo() {
fi fi
need_sudo need_sudo
"${SUDO}" "$@" "${_ZOXIDE_SUDO}" "$@"
} }
need_sudo() { need_sudo() {
if ! check_cmd "${SUDO}"; then if ! check_cmd "${_ZOXIDE_SUDO}"; then
err "\ err "\
could not find the command \`${SUDO}\` needed to get permissions for install. could not find the command \`${_ZOXIDE_SUDO}\` needed to get permissions for install.
If you are on Windows, please run your shell as an administrator, then rerun this script. If you are on Windows, please run your shell as an administrator, then rerun this script.
Otherwise, please run this script as root, or install \`sudo\`." Otherwise, please run this script as root, or install \`sudo\`."
fi fi
if ! "${SUDO}" -v; then if ! "${_ZOXIDE_SUDO}" -v; then
err "sudo permissions not granted, aborting installation" err "sudo permissions not granted, aborting installation"
fi fi
} }

View File

@ -4,7 +4,7 @@ let
overlays = [ rust ]; overlays = [ rust ];
}; };
rust = import (builtins.fetchTarball rust = import (builtins.fetchTarball
"https://github.com/oxalica/rust-overlay/archive/026e8fedefd6b167d92ed04b195c658d95ffc7a5.tar.gz"); "https://github.com/oxalica/rust-overlay/archive/4568a557ca325ff81fb354382d4a9968daa1001a.tar.gz");
rust-nightly = rust-nightly =
pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal); pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal);

View File

@ -95,23 +95,30 @@ pub enum EditCommand {
help_template = HelpTemplate, help_template = HelpTemplate,
)] )]
pub struct Import { pub struct Import {
#[clap(value_hint = ValueHint::FilePath)] #[clap(subcommand)]
pub path: PathBuf,
/// Application to import from
#[clap(value_enum, long)]
pub from: ImportFrom, pub from: ImportFrom,
/// Merge into existing database /// Merge into existing database
#[clap(long)] #[clap(long, global = true)]
pub merge: bool, pub merge: bool,
} }
#[derive(ValueEnum, Clone, Debug)] #[derive(Subcommand, Clone, Debug)]
pub enum ImportFrom { pub enum ImportFrom {
/// Import from atuin
Atuin,
/// Import from autojump
Autojump, Autojump,
#[clap(alias = "fasd")] /// Import from fasd
Fasd,
/// Import from z
Z, Z,
/// Import from z.lua
#[clap(name = "z.lua")]
ZLua,
/// Import from zsh-z
#[clap(name = "zsh-z")]
ZshZ,
} }
/// Generate shell configuration /// Generate shell configuration

View File

@ -1,166 +1,25 @@
use std::fs; use anyhow::{Result, bail};
use anyhow::{Context, Result, bail};
use crate::cmd::{Import, ImportFrom, Run}; use crate::cmd::{Import, ImportFrom, Run};
use crate::db::Database; use crate::db::Database;
use crate::import;
impl Run for Import { impl Run for Import {
fn run(&self) -> Result<()> { fn run(&self) -> Result<()> {
let buffer = fs::read_to_string(&self.path).with_context(|| {
format!("could not open database for importing: {}", &self.path.display())
})?;
let mut db = Database::open()?; let mut db = Database::open()?;
if !self.merge && !db.dirs().is_empty() { 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 => import_autojump(&mut db, &buffer), ImportFrom::Atuin => import::run(&import::Atuin {}, &mut db)?,
ImportFrom::Z => import_z(&mut db, &buffer), ImportFrom::Autojump => import::run(&import::Autojump {}, &mut db)?,
ImportFrom::Fasd => import::run(&import::Fasd {}, &mut db)?,
ImportFrom::Z => import::run(&import::Z {}, &mut db)?,
ImportFrom::ZLua => import::run(&import::ZLua {}, &mut db)?,
ImportFrom::ZshZ => import::run(&import::ZshZ {}, &mut db)?,
} }
.context("import error")?;
db.save() db.save()
} }
} }
fn import_autojump(db: &mut Database, buffer: &str) -> Result<()> {
for line in buffer.lines() {
if line.is_empty() {
continue;
}
let (rank, path) =
line.split_once('\t').with_context(|| format!("invalid entry: {line}"))?;
let mut rank = rank.parse::<f64>().with_context(|| format!("invalid rank: {rank}"))?;
// Normalize the rank using a sigmoid function. Don't import actual ranks from
// autojump, since its scoring algorithm is very different and might
// take a while to normalize.
rank = sigmoid(rank);
db.add_unchecked(path, rank, 0);
}
if db.dirty() {
db.dedup();
}
Ok(())
}
fn import_z(db: &mut Database, buffer: &str) -> Result<()> {
for line in buffer.lines() {
if line.is_empty() {
continue;
}
let mut split = line.rsplitn(3, '|');
let last_accessed = split.next().with_context(|| format!("invalid entry: {line}"))?;
let last_accessed =
last_accessed.parse().with_context(|| format!("invalid epoch: {last_accessed}"))?;
let rank = split.next().with_context(|| format!("invalid entry: {line}"))?;
let rank = rank.parse().with_context(|| format!("invalid rank: {rank}"))?;
let path = split.next().with_context(|| format!("invalid entry: {line}"))?;
db.add_unchecked(path, rank, last_accessed);
}
if db.dirty() {
db.dedup();
}
Ok(())
}
fn sigmoid(x: f64) -> f64 {
1.0 / (1.0 + (-x).exp())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::db::Dir;
#[test]
fn from_autojump() {
let data_dir = tempfile::tempdir().unwrap();
let mut db = Database::open_dir(data_dir.path()).unwrap();
for (path, rank, last_accessed) in [
("/quux/quuz", 1.0, 100),
("/corge/grault/garply", 6.0, 600),
("/waldo/fred/plugh", 3.0, 300),
("/xyzzy/thud", 8.0, 800),
("/foo/bar", 9.0, 900),
] {
db.add_unchecked(path, rank, last_accessed);
}
let buffer = "\
7.0 /baz
2.0 /foo/bar
5.0 /quux/quuz";
import_autojump(&mut db, buffer).unwrap();
db.sort_by_path();
println!("got: {:?}", &db.dirs());
let exp = [
Dir { path: "/baz".into(), rank: sigmoid(7.0), last_accessed: 0 },
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
Dir { path: "/foo/bar".into(), rank: 9.0 + sigmoid(2.0), last_accessed: 900 },
Dir { path: "/quux/quuz".into(), rank: 1.0 + sigmoid(5.0), last_accessed: 100 },
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
];
println!("exp: {exp:?}");
for (dir1, dir2) in db.dirs().iter().zip(exp) {
assert_eq!(dir1.path, dir2.path);
assert!((dir1.rank - dir2.rank).abs() < 0.01);
assert_eq!(dir1.last_accessed, dir2.last_accessed);
}
}
#[test]
fn from_z() {
let data_dir = tempfile::tempdir().unwrap();
let mut db = Database::open_dir(data_dir.path()).unwrap();
for (path, rank, last_accessed) in [
("/quux/quuz", 1.0, 100),
("/corge/grault/garply", 6.0, 600),
("/waldo/fred/plugh", 3.0, 300),
("/xyzzy/thud", 8.0, 800),
("/foo/bar", 9.0, 900),
] {
db.add_unchecked(path, rank, last_accessed);
}
let buffer = "\
/baz|7|700
/quux/quuz|4|400
/foo/bar|2|200
/quux/quuz|5|500";
import_z(&mut db, buffer).unwrap();
db.sort_by_path();
println!("got: {:?}", &db.dirs());
let exp = [
Dir { path: "/baz".into(), rank: 7.0, last_accessed: 700 },
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
Dir { path: "/foo/bar".into(), rank: 11.0, last_accessed: 900 },
Dir { path: "/quux/quuz".into(), rank: 10.0, last_accessed: 500 },
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
];
println!("exp: {exp:?}");
for (dir1, dir2) in db.dirs().iter().zip(exp) {
assert_eq!(dir1.path, dir2.path);
assert!((dir1.rank - dir2.rank).abs() < 0.01);
assert_eq!(dir1.last_accessed, dir2.last_accessed);
}
}
}

75
src/import.rs Normal file
View File

@ -0,0 +1,75 @@
pub(crate) use crate::import::atuin::Atuin;
pub(crate) use crate::import::autojump::Autojump;
pub(crate) use crate::import::fasd::Fasd;
pub(crate) use crate::import::z::Z;
pub(crate) use crate::import::z_lua::ZLua;
pub(crate) use crate::import::zsh_z::ZshZ;
mod atuin;
mod autojump;
mod fasd;
mod z;
mod z_lua;
mod zsh_z;
use std::io::{self, Write};
use std::path::PathBuf;
use anyhow::Result;
use crate::config;
use crate::db::{Database, Dir};
pub(crate) trait Importer {
/// Yields directory entries to be imported.
///
/// The outer `Result` reports failure to fetch the input (e.g. missing
/// file, subprocess errored). The per-item `Result` reports a malformed
/// row, which doesn't necessarily abort the whole import.
fn dirs(&self) -> Result<impl Iterator<Item = Result<Dir<'static>, ImportError>>>;
}
/// A single record that failed to import.
#[derive(Debug)]
pub(crate) struct ImportError {
/// Path of the source file containing the offending record. `None` if the
/// importer is not file-based (e.g. atuin streams from a subprocess).
pub path: Option<PathBuf>,
/// 1-indexed line number of the offending input.
pub line_num: usize,
/// Underlying reason the record could not be imported.
pub source: anyhow::Error,
}
/// Drives a single importer end-to-end: writes each `Ok` dir into the
/// database and prints each `Err` to stderr in `<path>:<line>: <reason>`
/// format. Doesn't abort on per-record errors — bad rows are skipped, the
/// rest of the import continues. After the iteration completes successfully,
/// the database is deduplicated and aged.
pub(crate) fn run(importer: &impl Importer, db: &mut Database) -> Result<()> {
let stderr = io::stderr();
let mut stderr = stderr.lock();
for entry in importer.dirs()? {
match entry {
Ok(dir) => db.add_unchecked(dir.path, dir.rank, dir.last_accessed),
Err(e) => {
let location = match &e.path {
Some(path) => format!("{}:{}", path.display(), e.line_num),
None => format!("line {}", e.line_num),
};
_ = writeln!(stderr, "{location}: {:#}", e.source);
}
}
}
if db.dirty() {
db.dedup();
let max_age = config::maxage()?;
db.age(max_age);
}
Ok(())
}

116
src/import/atuin.rs Normal file
View File

@ -0,0 +1,116 @@
use std::borrow::Cow;
use std::io::{BufRead, BufReader};
use std::process::{Child, ChildStdout, Command, Stdio};
use std::str;
use anyhow::{Context, Result, anyhow};
use crate::db::{Dir, Epoch};
use crate::import::{ImportError, Importer};
#[derive(clap::Args, Clone, Debug)]
pub(crate) struct Atuin {}
impl Importer for Atuin {
fn dirs(&self) -> Result<impl Iterator<Item = Result<Dir<'static>, ImportError>>> {
// atuin renders `{time}` as `YYYY-MM-DD HH:MM:SS` in UTC.
let mut child = Command::new("atuin")
.args(["history", "list", "--format={time}\t{directory}", "--print0"])
.stdout(Stdio::piped())
.spawn()
.context("failed to run `atuin`; is it installed and on PATH?")?;
let stdout = child.stdout.take().expect("stdout piped");
let reader = BufReader::new(stdout);
Ok(Iter::new(reader, child))
}
}
/// Iterates atuin's NUL-separated `{time}\t{directory}` records, emitting one
/// `Dir` per directory transition (consecutive same-path records collapse).
/// Owns the `Child` handle so the subprocess is reaped on Drop.
struct Iter {
reader: BufReader<ChildStdout>,
buf: Vec<u8>,
line_num: usize,
child: Child,
prev_cwd: Option<String>,
}
impl Iter {
fn new(reader: BufReader<ChildStdout>, child: Child) -> Self {
Self { reader, buf: Vec::new(), line_num: 0, child, prev_cwd: None }
}
fn err(&self, source: anyhow::Error) -> ImportError {
ImportError { path: None, line_num: self.line_num, source }
}
fn parse_line(&self, line: &[u8]) -> Result<Dir<'static>, ImportError> {
let line =
str::from_utf8(line).map_err(|e| self.err(anyhow!(e).context("invalid utf-8")))?;
let (timestamp, path) =
line.split_once('\t').ok_or_else(|| self.err(anyhow!("invalid entry: {line}")))?;
let timestamp_format =
time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
let timestamp = time::PrimitiveDateTime::parse(timestamp, timestamp_format)
.map_err(|e| self.err(anyhow!(e).context(format!("invalid timestamp: {timestamp:?}"))))?
.assume_utc()
.unix_timestamp();
let dir = Dir {
path: Cow::Owned(path.to_string()),
rank: 1.0,
last_accessed: timestamp as Epoch,
};
Ok(dir)
}
}
impl Iterator for Iter {
type Item = Result<Dir<'static>, ImportError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
self.buf.clear();
self.line_num += 1;
match self.reader.read_until(b'\0', &mut self.buf) {
Ok(0) => return None,
Ok(_) => {
if self.buf.last() == Some(&b'\0') {
self.buf.pop();
}
if self.buf.is_empty() {
continue;
}
let result = self.parse_line(&self.buf);
match &result {
Ok(dir) => {
let path = dir.path.as_ref();
if self.prev_cwd.as_deref() == Some(path) {
continue; // dedup consecutive same-path entries
}
self.prev_cwd = Some(path.to_string());
return Some(result);
}
Err(_) => return Some(result),
}
}
Err(e) => {
return Some(Err(self.err(anyhow!(e).context("could not read from atuin"))));
}
}
}
}
}
impl Drop for Iter {
fn drop(&mut self) {
_ = self.child.kill();
_ = self.child.wait();
}
}

124
src/import/autojump.rs Normal file
View File

@ -0,0 +1,124 @@
use std::borrow::Cow;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
use std::{env, str};
use anyhow::{Context, Result, anyhow};
use crate::db::Dir;
use crate::import::{ImportError, Importer};
#[derive(clap::Args, Clone, Debug)]
pub(crate) struct Autojump {}
impl Importer for Autojump {
fn dirs(&self) -> Result<impl Iterator<Item = Result<Dir<'static>, ImportError>>> {
let path = data_path()?;
let file = File::open(&path).with_context(|| format!("could not read {path:?}"))?;
let reader = BufReader::new(file);
Ok(Iter::new(reader, path))
}
}
struct Iter<R: BufRead> {
reader: R,
buf: Vec<u8>,
line_num: usize,
path: PathBuf,
}
impl<R: BufRead> Iter<R> {
fn new(reader: R, path: PathBuf) -> Self {
Self { reader, buf: Vec::new(), line_num: 0, path }
}
fn err(&self, source: anyhow::Error) -> ImportError {
ImportError { path: Some(self.path.clone()), line_num: self.line_num, source }
}
fn parse_line(&self, line: &[u8]) -> Result<Dir<'static>, ImportError> {
let line =
str::from_utf8(line).map_err(|e| self.err(anyhow!(e).context("invalid utf-8")))?;
let (rank, path) =
line.split_once('\t').ok_or_else(|| self.err(anyhow!("invalid entry: {line}")))?;
let rank = rank
.parse::<f64>()
.map_err(|e| self.err(anyhow!(e).context(format!("invalid rank: {rank}"))))?;
// Normalize the rank using a sigmoid function. Don't import actual ranks from
// autojump, since its scoring algorithm is very different and might
// take a while to normalize.
let rank = sigmoid(rank);
Ok(Dir { path: Cow::Owned(path.to_string()), rank, last_accessed: 0 })
}
}
impl<R: BufRead> Iterator for Iter<R> {
type Item = Result<Dir<'static>, ImportError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
self.buf.clear();
self.line_num += 1;
match self.reader.read_until(b'\n', &mut self.buf) {
Ok(0) => return None,
Ok(_) => {
if self.buf.last() == Some(&b'\n') {
self.buf.pop();
}
if self.buf.last() == Some(&b'\r') {
self.buf.pop();
}
if self.buf.is_empty() {
continue;
}
return Some(self.parse_line(&self.buf));
}
Err(e) => return Some(Err(self.err(anyhow::Error::from(e)))),
}
}
}
}
/// Mirrors autojump's path logic:
///
/// ```python
/// if is_osx():
/// data_home = os.path.join(os.path.expanduser('~'), 'Library')
/// elif is_windows():
/// data_home = os.getenv('APPDATA')
/// else:
/// data_home = os.getenv(
/// 'XDG_DATA_HOME',
/// os.path.join(os.path.expanduser('~'), '.local', 'share'),
/// )
/// data_path = os.path.join(data_home, 'autojump', 'autojump.txt')
/// ```
fn data_path() -> Result<PathBuf> {
let mut path = if cfg!(target_os = "macos") {
let mut path = dirs::home_dir().context("could not find home directory")?;
path.push("Library");
path
} else if cfg!(target_os = "windows") {
let appdata = env::var_os("APPDATA").context("%APPDATA% is not set")?;
PathBuf::from(appdata)
} else if let Some(xdg) = env::var_os("XDG_DATA_HOME") {
PathBuf::from(xdg)
} else {
let mut path = dirs::home_dir().context("could not find home directory")?;
path.push(".local");
path.push("share");
path
};
path.push("autojump");
path.push("autojump.txt");
Ok(path)
}
fn sigmoid(x: f64) -> f64 {
1.0 / (1.0 + (-x).exp())
}

39
src/import/fasd.rs Normal file
View File

@ -0,0 +1,39 @@
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;
use anyhow::{Context, Result};
use crate::db::Dir;
use crate::import::{ImportError, Importer, z};
#[derive(clap::Args, Clone, Debug)]
pub(crate) struct Fasd {}
impl Importer for Fasd {
fn dirs(&self) -> Result<impl Iterator<Item = Result<Dir<'static>, ImportError>>> {
let path = data_path()?;
let file = File::open(&path).with_context(|| format!("could not read {path:?}"))?;
let reader = BufReader::new(file);
// fasd uses the same `path|rank|last_accessed` line format as z, so reuse z's
// iterator.
Ok(z::Iter::new(reader, path))
}
}
/// Mirrors fasd's path logic:
///
/// ```sh
/// [ -z "$_FASD_DATA" ] && _FASD_DATA="$HOME/.fasd"
/// ```
fn data_path() -> Result<PathBuf> {
match env::var_os("_FASD_DATA") {
Some(path) => Ok(PathBuf::from(path)),
None => {
let mut path = dirs::home_dir().context("could not find home directory")?;
path.push(".fasd");
Ok(path)
}
}
}

103
src/import/z.rs Normal file
View File

@ -0,0 +1,103 @@
use std::borrow::Cow;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
use std::{env, str};
use anyhow::{Context, Result, anyhow};
use crate::db::Dir;
use crate::import::{ImportError, Importer};
#[derive(clap::Args, Clone, Debug)]
pub(crate) struct Z {}
impl Importer for Z {
fn dirs(&self) -> Result<impl Iterator<Item = Result<Dir<'static>, ImportError>>> {
let path = data_path()?;
let file = File::open(&path).with_context(|| format!("could not read {path:?}"))?;
let reader = BufReader::new(file);
Ok(Iter::new(reader, path))
}
}
pub(crate) struct Iter<R: BufRead> {
reader: R,
buf: Vec<u8>,
line_num: usize,
path: PathBuf,
}
impl<R: BufRead> Iter<R> {
pub(crate) fn new(reader: R, path: PathBuf) -> Self {
Self { reader, buf: Vec::new(), line_num: 0, path }
}
fn err(&self, source: anyhow::Error) -> ImportError {
ImportError { path: Some(self.path.clone()), line_num: self.line_num, source }
}
fn parse_line(&self, line: &[u8]) -> Result<Dir<'static>, ImportError> {
let line =
str::from_utf8(line).map_err(|e| self.err(anyhow!(e).context("invalid utf-8")))?;
let err = || self.err(anyhow!("invalid entry: {line}"));
// z stores entries as `path|rank|last_accessed`. Use `rsplitn` so paths
// containing `|` are preserved.
let mut split = line.rsplitn(3, '|');
let last_accessed = split.next().ok_or_else(err)?;
let last_accessed = last_accessed.parse::<u64>().map_err(|_| err())?;
let rank = split.next().ok_or_else(err)?;
let rank = rank.parse::<f64>().map_err(|_| err())?;
let path = split.next().ok_or_else(err)?;
Ok(Dir { path: Cow::Owned(path.to_string()), rank, last_accessed })
}
}
impl<R: BufRead> Iterator for Iter<R> {
type Item = Result<Dir<'static>, ImportError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
self.buf.clear();
self.line_num += 1;
match self.reader.read_until(b'\n', &mut self.buf) {
Ok(0) => return None,
Ok(_) => {
if self.buf.last() == Some(&b'\n') {
self.buf.pop();
}
if self.buf.last() == Some(&b'\r') {
self.buf.pop();
}
if self.buf.is_empty() {
continue;
}
return Some(self.parse_line(&self.buf));
}
Err(e) => return Some(Err(self.err(anyhow::Error::from(e)))),
}
}
}
}
/// Mirrors z's path logic:
///
/// ```sh
/// local datafile="${_Z_DATA:-$HOME/.z}"
/// ```
fn data_path() -> Result<PathBuf> {
match env::var_os("_Z_DATA") {
Some(path) => Ok(PathBuf::from(path)),
None => {
let mut path = dirs::home_dir().context("could not find home directory")?;
path.push(".z");
Ok(path)
}
}
}

108
src/import/z_lua.rs Normal file
View File

@ -0,0 +1,108 @@
use std::env;
use std::ffi::OsStr;
use std::fs::File;
use std::io::{self, BufReader};
use std::path::PathBuf;
use anyhow::{Context, Result};
use crate::db::Dir;
use crate::import::{ImportError, Importer, z};
#[derive(clap::Args, Clone, Debug)]
pub(crate) struct ZLua {}
impl Importer for ZLua {
fn dirs(&self) -> Result<impl Iterator<Item = Result<Dir<'static>, ImportError>>> {
let path = data_path()?;
let err = match File::open(&path) {
Ok(file) => return Ok(z::Iter::new(BufReader::new(file), path)),
Err(e) if e.kind() == io::ErrorKind::NotFound => e,
Err(e) => return Err(e).with_context(|| format!("could not read {path:?}")),
};
let fish_path = data_path_fish()?;
let file = match File::open(&fish_path) {
Ok(file) => file,
// Both paths missing - report the original path's error.
Err(e) if e.kind() == io::ErrorKind::NotFound => {
return Err(err).with_context(|| format!("could not read {path:?}"));
}
// Fish path failed for some other reason (permissions, etc.)
Err(e) => return Err(e).with_context(|| format!("could not read {fish_path:?}")),
};
// z.lua uses the same `path|rank|last_accessed` line format as z.
Ok(z::Iter::new(BufReader::new(file), fish_path))
}
}
/// Mirrors z.lua's path logic:
///
/// ```lua
/// DATA_FILE = '~/.zlua' -- default
///
/// -- in z_init():
/// local _zl_data = os.getenv('_ZL_DATA')
/// if _zl_data ~= nil and _zl_data ~= "" then
/// if windows then
/// DATA_FILE = _zl_data
/// else
/// -- avoid windows environments affect cygwin & msys
/// if not string.match(_zl_data, '^%a:[/\\]') then
/// DATA_FILE = _zl_data
/// end
/// end
/// end
/// ```
fn data_path() -> Result<PathBuf> {
if let Some(path) = env::var_os("_ZL_DATA")
// Skip empty paths.
.filter(|path| !path.is_empty())
// On non-Windows, skip values that look like a Windows path (`C:\...`)
// — guards against Cygwin/MSYS environments leaking through.
.filter(|path| cfg!(target_os = "windows") || !looks_like_windows_path(path))
{
return Ok(PathBuf::from(path));
}
let mut path = dirs::home_dir().context("could not find home directory")?;
path.push(".zlua");
Ok(path)
}
/// Mirrors z.lua's path logic on Fish:
///
/// ```fish
/// if test -z "$XDG_DATA_HOME"
/// set -U _ZL_DATA_DIR "$HOME/.local/share/zlua"
/// else
/// set -U _ZL_DATA_DIR "$XDG_DATA_HOME/zlua"
/// end
/// set -x _ZL_DATA "$_ZL_DATA_DIR/zlua.txt"
/// ```
fn data_path_fish() -> Result<PathBuf> {
let mut path = match env::var_os("XDG_DATA_HOME") {
Some(xdg) => PathBuf::from(xdg),
None => {
let mut path = dirs::home_dir().context("could not find home directory")?;
path.push(".local");
path.push("share");
path
}
};
path.push("zlua");
path.push("zlua.txt");
Ok(path)
}
/// Matches Lua's `^%a:[/\\]` — ASCII letter, colon, slash-or-backslash.
fn looks_like_windows_path(s: &OsStr) -> bool {
let bytes = s.as_encoded_bytes();
bytes.len() >= 3
&& bytes[0].is_ascii_alphabetic()
&& bytes[1] == b':'
&& (bytes[2] == b'/' || bytes[2] == b'\\')
}

41
src/import/zsh_z.rs Normal file
View File

@ -0,0 +1,41 @@
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;
use anyhow::{Context, Result};
use crate::db::Dir;
use crate::import::{ImportError, Importer, z};
#[derive(clap::Args, Clone, Debug)]
pub(crate) struct ZshZ {}
impl Importer for ZshZ {
fn dirs(&self) -> Result<impl Iterator<Item = Result<Dir<'static>, ImportError>>> {
let path = data_path()?;
let file = File::open(&path).with_context(|| format!("could not read {path:?}"))?;
let reader = BufReader::new(file);
// zsh-z uses the same `path|rank|last_accessed` line format as z.
Ok(z::Iter::new(reader, path))
}
}
/// Mirrors zsh-z's path logic:
///
/// ```sh
/// # Allow the user to specify a custom datafile in $ZSHZ_DATA (or legacy $_Z_DATA)
/// local custom_datafile="${ZSHZ_DATA:-$_Z_DATA}"
/// # If the user specified a datafile, use that or default to ~/.z
/// local datafile=${${custom_datafile:-$HOME/.z}:A}
/// ```
fn data_path() -> Result<PathBuf> {
match env::var_os("ZSHZ_DATA").or_else(|| env::var_os("_Z_DATA")) {
Some(path) => Ok(PathBuf::from(path)),
None => {
let mut path = dirs::home_dir().context("could not find home directory")?;
path.push(".z");
Ok(path)
}
}
}

View File

@ -4,6 +4,7 @@ mod cmd;
mod config; mod config;
mod db; mod db;
mod error; mod error;
mod import;
mod shell; mod shell;
mod util; mod util;

View File

@ -168,13 +168,9 @@ pub fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<()> {
// 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 std::os::unix::fs::MetadataExt; use std::os::unix::fs::{MetadataExt, fchown};
use nix::unistd::{self, Gid, Uid}; _ = fchown(&tmp_file, Some(metadata.uid()), Some(metadata.gid()));
let uid = Uid::from_raw(metadata.uid());
let gid = Gid::from_raw(metadata.gid());
_ = unistd::fchown(&tmp_file, Some(uid), Some(gid));
} }
// Close and rename the tmpfile. // Close and rename the tmpfile.

View File

@ -9,12 +9,16 @@
# pwd based on the value of _ZO_RESOLVE_SYMLINKS. # pwd based on the value of _ZO_RESOLVE_SYMLINKS.
function __zoxide_pwd() { function __zoxide_pwd() {
{%- decl pwd -%}
{%- if resolve_symlinks -%}
{%- let pwd = "\\builtin pwd -P" -%}
{%- else -%}
{%- let pwd = "\\builtin pwd -L" -%}
{%- endif -%}
{%- if cfg!(windows) %} {%- if cfg!(windows) %}
\command cygpath -w "$(\builtin pwd -P)" \command cygpath -w "{{ pwd }}"
{%- else if resolve_symlinks %}
\builtin pwd -P
{%- else %} {%- else %}
\builtin pwd -L {{ pwd }}
{%- endif %} {%- endif %}
} }
@ -59,7 +63,7 @@ if [[ ${PROMPT_COMMAND:=} != *'__zoxide_hook'* ]]; then
PROMPT_COMMAND=("${PROMPT_COMMAND[@]}" __zoxide_hook) PROMPT_COMMAND=("${PROMPT_COMMAND[@]}" __zoxide_hook)
else else
# shellcheck disable=SC2128,SC2178 # shellcheck disable=SC2128,SC2178
PROMPT_COMMAND="${PROMPT_COMMAND%"${PROMPT_COMMAND##*[![:space:]]}"}" PROMPT_COMMAND="${PROMPT_COMMAND%"${PROMPT_COMMAND##*[![:space:];]}"}"
# shellcheck disable=SC2128,SC2178 # shellcheck disable=SC2128,SC2178
PROMPT_COMMAND="${PROMPT_COMMAND:+${PROMPT_COMMAND};}__zoxide_hook" PROMPT_COMMAND="${PROMPT_COMMAND:+${PROMPT_COMMAND};}__zoxide_hook"
fi fi

View File

@ -7,12 +7,16 @@
# pwd based on the value of _ZO_RESOLVE_SYMLINKS. # pwd based on the value of _ZO_RESOLVE_SYMLINKS.
function __zoxide_pwd function __zoxide_pwd
{%- decl pwd -%}
{%- if resolve_symlinks -%}
{%- let pwd = "builtin pwd -P" -%}
{%- else -%}
{%- let pwd = "builtin pwd -L" -%}
{%- endif -%}
{%- if cfg!(windows) %} {%- if cfg!(windows) %}
command cygpath -w (builtin pwd -P) command cygpath -w ({{ pwd }})
{%- else if resolve_symlinks %}
builtin pwd -P
{%- else %} {%- else %}
builtin pwd -L {{ pwd }}
{%- endif %} {%- endif %}
end end

View File

@ -8,15 +8,28 @@
# #
# pwd based on the value of _ZO_RESOLVE_SYMLINKS. # pwd based on the value of _ZO_RESOLVE_SYMLINKS.
__zoxide_pwd() { {%- decl pwd -%}
{%- if resolve_symlinks -%}
{%- let pwd = "\\command pwd -P" -%}
{%- else -%}
{%- let pwd = "\\command pwd -L" -%}
{%- endif -%}
{%- if cfg!(windows) %} {%- if cfg!(windows) %}
\command cygpath -w "$(\command pwd -P)" if \command -v cygpath >/dev/null
{%- else if resolve_symlinks %} then
\command pwd -P __zoxide_pwd() {
{%- else %} \command cygpath -w "$({{ pwd }})"
\command pwd -L
{%- endif %}
} }
else
__zoxide_pwd() {
{{ pwd }}
}
fi
{%- else %}
__zoxide_pwd() {
{{ pwd }}
}
{%- endif %}
# cd + custom logic based on the value of _ZO_ECHO. # cd + custom logic based on the value of _ZO_ECHO.
__zoxide_cd() { __zoxide_cd() {

View File

@ -19,7 +19,7 @@ function global:__zoxide_bin {
# pwd based on zoxide's format. # pwd based on zoxide's format.
function global:__zoxide_pwd { function global:__zoxide_pwd {
$cwd = Get-Location $cwd = Microsoft.PowerShell.Management\Get-Location
if ($cwd.Provider.Name -eq "FileSystem") { if ($cwd.Provider.Name -eq "FileSystem") {
$cwd.ProviderPath $cwd.ProviderPath
} }
@ -28,20 +28,24 @@ function global:__zoxide_pwd {
# cd + custom logic based on the value of _ZO_ECHO. # cd + custom logic based on the value of _ZO_ECHO.
function global:__zoxide_cd($dir, $literal) { function global:__zoxide_cd($dir, $literal) {
$dir = if ($literal) { $dir = if ($literal) {
Set-Location -LiteralPath $dir -Passthru -ErrorAction Stop if ($null -eq $dir) {
Microsoft.PowerShell.Management\Set-Location
} else {
Microsoft.PowerShell.Management\Set-Location -LiteralPath $dir -Passthru -ErrorAction Stop
}
} else { } else {
if ($dir -eq '-' -and ($PSVersionTable.PSVersion -lt 6.1)) { 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." Microsoft.PowerShell.Utility\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)) { 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." Microsoft.PowerShell.Utility\Write-Error "cd + is not supported below PowerShell 6.2. Please upgrade your version of PowerShell."
} }
else { else {
Set-Location -Path $dir -Passthru -ErrorAction Stop Microsoft.PowerShell.Management\Set-Location -Path $dir -Passthru -ErrorAction Stop
} }
} }
{%- if echo %} {%- if echo %}
Write-Output $dir.Path Microsoft.PowerShell.Utility\Write-Output $dir.Path
{%- endif %} {%- endif %}
} }
@ -80,7 +84,7 @@ function global:__zoxide_hook {
{%- endif %} {%- endif %}
# Initialize hook. # Initialize hook.
$global:__zoxide_hooked = (Get-Variable __zoxide_hooked -ErrorAction Ignore -ValueOnly) $global:__zoxide_hooked = (Microsoft.PowerShell.Utility\Get-Variable __zoxide_hooked -ErrorAction Ignore -ValueOnly)
if ($global:__zoxide_hooked -ne 1) { if ($global:__zoxide_hooked -ne 1) {
$global:__zoxide_hooked = 1 $global:__zoxide_hooked = 1
$global:__zoxide_prompt_old = $function:prompt $global:__zoxide_prompt_old = $function:prompt
@ -101,15 +105,15 @@ if ($global:__zoxide_hooked -ne 1) {
# Jump to a directory using only keywords. # Jump to a directory using only keywords.
function global:__zoxide_z { function global:__zoxide_z {
if ($args.Length -eq 0) { if ($args.Length -eq 0) {
__zoxide_cd ~ $true __zoxide_cd $null $true
} }
elseif ($args.Length -eq 1 -and ($args[0] -eq '-' -or $args[0] -eq '+')) { elseif ($args.Length -eq 1 -and ($args[0] -eq '-' -or $args[0] -eq '+')) {
__zoxide_cd $args[0] $false __zoxide_cd $args[0] $false
} }
elseif ($args.Length -eq 1 -and (Test-Path -PathType Container -LiteralPath $args[0])) { elseif ($args.Length -eq 1 -and (Microsoft.PowerShell.Management\Test-Path -PathType Container -LiteralPath $args[0])) {
__zoxide_cd $args[0] $true __zoxide_cd $args[0] $true
} }
elseif ($args.Length -eq 1 -and (Test-Path -PathType Container -Path $args[0] )) { elseif ($args.Length -eq 1 -and (Microsoft.PowerShell.Management\Test-Path -PathType Container -Path $args[0] )) {
__zoxide_cd $args[0] $false __zoxide_cd $args[0] $false
} }
else { else {
@ -141,8 +145,8 @@ function global:__zoxide_zi {
{%- match cmd %} {%- match cmd %}
{%- when Some with (cmd) %} {%- when Some with (cmd) %}
Set-Alias -Name {{cmd}} -Value __zoxide_z -Option AllScope -Scope Global -Force Microsoft.PowerShell.Utility\Set-Alias -Name {{cmd}} -Value __zoxide_z -Option AllScope -Scope Global -Force
Set-Alias -Name {{cmd}}i -Value __zoxide_zi -Option AllScope -Scope Global -Force Microsoft.PowerShell.Utility\Set-Alias -Name {{cmd}}i -Value __zoxide_zi -Option AllScope -Scope Global -Force
{%- when None %} {%- when None %}

View File

@ -1,7 +1,7 @@
{%- let section = "# =============================================================================\n#" -%} {%- let section = "# =============================================================================\n#" -%}
{%- let not_configured = "# -- not configured --" -%} {%- let not_configured = "# -- not configured --" -%}
{%- let pwd_cmd -%} {%- decl pwd_cmd -%}
{%- if resolve_symlinks -%} {%- if resolve_symlinks -%}
{%- let pwd_cmd = "pwd -P" -%} {%- let pwd_cmd = "pwd -P" -%}
{%- else -%} {%- else -%}

View File

@ -9,12 +9,16 @@
# pwd based on the value of _ZO_RESOLVE_SYMLINKS. # pwd based on the value of _ZO_RESOLVE_SYMLINKS.
function __zoxide_pwd() { function __zoxide_pwd() {
{%- decl pwd -%}
{%- if resolve_symlinks -%}
{%- let pwd = "\\builtin pwd -P" -%}
{%- else -%}
{%- let pwd = "\\builtin pwd -L" -%}
{%- endif -%}
{%- if cfg!(windows) %} {%- if cfg!(windows) %}
\command cygpath -w "$(\builtin pwd -P)" \command cygpath -w "{{ pwd }}"
{%- else if resolve_symlinks %}
\builtin pwd -P
{%- else %} {%- else %}
\builtin pwd -L {{ pwd }}
{%- endif %} {%- endif %}
} }
@ -55,6 +59,7 @@ function __zoxide_doctor() {
{%- else %} {%- else %}
[[ ${_ZO_DOCTOR:-1} -ne 0 ]] || return 0 [[ ${_ZO_DOCTOR:-1} -ne 0 ]] || return 0
[[ $- == *i* ]] || return 0
{%- if hook == InitHook::Prompt %} {%- if hook == InitHook::Prompt %}
[[ ${precmd_functions[(Ie)__zoxide_hook]:-} -eq 0 ]] || return 0 [[ ${precmd_functions[(Ie)__zoxide_hook]:-} -eq 0 ]] || return 0