Add z completions for zsh (#309)

This commit is contained in:
Ajeet D'Souza 2021-12-05 14:28:31 +05:30
parent 72fd48ed97
commit 58430d8c54
14 changed files with 128 additions and 74 deletions

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, windows-latest] # FIXME: macos-latest
steps:
- uses: actions/checkout@v2
@ -28,5 +28,5 @@ jobs:
- run: cargo xtask ci
if: ${{ matrix.os == 'windows-latest' }}
- run: nix-shell --cores 0 --pure --run 'cargo xtask ci'
- run: nix-shell --cores 0 --pure --run 'rm -rf ~/.cargo/bin; cargo xtask ci'
if: ${{ matrix.os != 'windows-latest' }}

View File

@ -9,16 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Zsh: completions for `z` command.
### Changed
- fzf: better default options.
- fish: interactive completions are only triggered when the last argument is empty.
- Fzf: better default options.
- Fish: interactive completions are only triggered when the last argument is empty.
### Fixed
- PowerShell: use global scope for aliases.
- Zsh: fix errors with `set -eu`.
- fzf: handle early selection.
- Fzf: handle early selection.
## [0.7.9] - 2021-11-02

View File

@ -2,7 +2,7 @@
authors = ["Ajeet D'Souza <98ajeet@gmail.com>"]
categories = ["command-line-utilities", "filesystem"]
description = "A smarter cd command for your terminal"
edition = "2018"
edition = "2021"
keywords = ["cli"]
license = "MIT"
name = "zoxide"

View File

@ -33,15 +33,17 @@ zoxide works on all major shells.
![Tutorial][tutorial]
```sh
z foo # cd into highest ranked directory matching foo
z foo bar # cd into highest ranked directory matching foo and bar
z foo # cd into highest ranked directory matching foo
z foo bar # cd into highest ranked directory matching foo and bar
z ~/foo # z also works like a regular cd command
z foo/ # cd into relative path
z .. # cd one level up
z - # cd into previous directory
z ~/foo # z also works like a regular cd command
z foo/ # cd into relative path
z .. # cd one level up
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.7.10+, bash/fish/zsh only)
```
Read more about the matching algorithm [here][algorithm-matching].
@ -231,6 +233,8 @@ Add this to your configuration (usually `~/.zshrc`):
eval "$(zoxide init zsh)"
```
For completions to work, this line must be added _after_ calling `compinit`.
</details>
<details>
@ -347,16 +351,16 @@ They must be set before `zoxide init` is called.
[algorithm-matching]: https://github.com/ajeetdsouza/zoxide/wiki/Algorithm#matching
[alpine linux packages]: https://pkgs.alpinelinux.org/packages?name=zoxide
[arch linux community]: https://archlinux.org/packages/community/x86_64/zoxide/
[builtwithnix-badge]: https://img.shields.io/badge/builtwith-nix-7d81f7
[builtwithnix-badge]: https://img.shields.io/badge/builtwith-nix-7d81f7?style=flat-square
[builtwithnix]: https://builtwithnix.org/
[chocolatey]: https://community.chocolatey.org/packages/zoxide
[conda-forge]: https://anaconda.org/conda-forge/zoxide
[copr]: https://copr.fedorainfracloud.org/coprs/atim/zoxide/
[crates.io-badge]: https://img.shields.io/crates/v/zoxide
[crates.io-badge]: https://img.shields.io/crates/v/zoxide?style=flat-square
[crates.io]: https://crates.io/crates/zoxide
[debian packages]: https://packages.debian.org/stable/admin/zoxide
[devuan packages]: https://pkginfo.devuan.org/cgi-bin/package-query.html?c=package&q=zoxide
[downloads-badge]: https://img.shields.io/github/downloads/ajeetdsouza/zoxide/total
[downloads-badge]: https://img.shields.io/github/downloads/ajeetdsouza/zoxide/total?style=flat-square
[dports]: https://github.com/DragonFlyBSD/DPorts/tree/master/sysutils/zoxide
[emacs]: https://www.gnu.org/software/emacs/
[fedora packages]: https://src.fedoraproject.org/rpms/rust-zoxide

View File

@ -1,41 +1,41 @@
let
rust = import (builtins.fetchTarball
"https://github.com/oxalica/rust-overlay/archive/ad311f5bb5c5ef475985f1e0f264e831470a8510.tar.gz");
pkgs = import <nixpkgs> { overlays = [ rust ]; };
pkgs-latest = import (builtins.fetchTarball
"https://github.com/NixOS/nixpkgs/archive/3ef1d2a9602c18f8742e1fb63d5ae9867092e3d6.tar.gz")
{ };
"https://github.com/oxalica/rust-overlay/archive/783722a22ee5d762ac5c1c7b418b57b3010c827a.tar.gz");
pkgs = import (builtins.fetchTarball
"https://github.com/NixOS/nixpkgs/archive/58f87c20e1abbbe835f1f3106ecea10fd93c4a90.tar.gz") {
overlays = [ rust ];
};
in pkgs.mkShell {
buildInputs = [
# Rust
pkgs.rust-bin.stable.latest.default
# Shells
pkgs-latest.elvish
pkgs-latest.fish
pkgs-latest.nushell
pkgs-latest.xonsh
pkgs.bash
pkgs.dash
pkgs.elvish
pkgs.fish
pkgs.nushell
pkgs.powershell
pkgs.xonsh
pkgs.zsh
# Tools
pkgs-latest.cargo-audit
pkgs-latest.mandoc
pkgs-latest.nixfmt
pkgs-latest.nodePackages.markdownlint-cli
pkgs-latest.python3Packages.black
pkgs-latest.python3Packages.mypy
pkgs-latest.python3Packages.pylint
pkgs-latest.shellcheck
pkgs-latest.shfmt
pkgs.cargo-audit
pkgs.mandoc
pkgs.nixfmt
pkgs.nodePackages.markdownlint-cli
pkgs.python3Packages.black
pkgs.python3Packages.mypy
pkgs.python3Packages.pylint
pkgs.shellcheck
pkgs.shfmt
# Dependencies
pkgs.cacert
pkgs.libiconv
pkgs.fzf
pkgs.git
pkgs.libiconv
];
RUST_BACKTRACE = 1;

View File

@ -32,16 +32,17 @@ impl Query {
if self.interactive {
let mut fzf = Fzf::new(false)?;
let stdin = fzf.stdin();
let selection = loop {
let dir = match stream.next() {
Some(dir) => dir,
None => break fzf.select()?,
};
match writeln!(fzf.stdin(), "{}", dir.display_score(now)) {
Ok(()) => (()),
match writeln!(stdin, "{}", dir.display_score(now)) {
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break fzf.select()?,
Err(e) => Err(e).context("could not write to fzf")?,
result => result.context("could not write to fzf")?,
}
};

View File

@ -19,16 +19,17 @@ impl Run for Remove {
let mut stream = db.stream(now).with_keywords(keywords);
let mut fzf = Fzf::new(true)?;
let stdin = fzf.stdin();
let selection = loop {
let dir = match stream.next() {
Some(dir) => dir,
None => break fzf.select()?,
};
match writeln!(fzf.stdin(), "{}", dir.display_score(now)) {
Ok(()) => (()),
match writeln!(stdin, "{}", dir.display_score(now)) {
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break fzf.select()?,
Err(e) => Err(e).context("could not write to fzf")?,
result => result.context("could not write to fzf")?,
}
};

View File

@ -3,7 +3,6 @@ use std::ffi::OsString;
use std::path::PathBuf;
use anyhow::{bail, Context, Result};
use dirs;
use glob::Pattern;
use crate::db::Rank;

View File

@ -24,7 +24,7 @@ impl Fzf {
command.args(&[
"--bind=ctrl-z:ignore",
"--exit-0",
"--height=35%",
"--height=40%",
"--inline-info",
"--no-sort",
"--reverse",

View File

@ -32,7 +32,7 @@ function __zoxide_cd() {
{%- if hook == InitHook::Prompt %}
function __zoxide_hook() {
\builtin local -r retval="$?"
\builtin command zoxide add -- "$(__zoxide_pwd)"
\command zoxide add -- "$(__zoxide_pwd || \builtin true)"
return "${retval}"
}
{%- else if hook == InitHook::Pwd %}
@ -40,10 +40,11 @@ __zoxide_oldpwd="$(__zoxide_pwd)"
function __zoxide_hook() {
\builtin local -r retval="$?"
\builtin local -r pwd_tmp="$(__zoxide_pwd)"
\builtin local pwd_tmp
pwd_tmp="$(__zoxide_pwd)"
if [[ ${__zoxide_oldpwd} != "${pwd_tmp}" ]]; then
__zoxide_oldpwd="${pwd_tmp}"
\builtin command zoxide add -- "${__zoxide_oldpwd}"
\command zoxide add -- "${__zoxide_oldpwd}"
fi
return "${retval}"
}
@ -76,14 +77,15 @@ function __zoxide_z() {
__zoxide_cd "${result:2}"
else
\builtin local result
result="$(\builtin command zoxide query --exclude "$(__zoxide_pwd)" -- "$@")" && __zoxide_cd "${result}"
result="$(\command zoxide query --exclude "$(__zoxide_pwd || \builtin true)" -- "$@")" &&
__zoxide_cd "${result}"
fi
}
# Jump to a directory using interactive search.
function __zoxide_zi() {
\builtin local result
result="$(\builtin command zoxide query -i -- "$@")" && __zoxide_cd "${result}"
result="$(\command zoxide query -i -- "$@")" && __zoxide_cd "${result}"
}
{{ section }}
@ -123,11 +125,12 @@ if [[ :"${SHELLOPTS}": =~ :(vi|emacs): && ${TERM} != 'dumb' ]]; then
# If there is only one argument, use `cd` completions.
if [[ {{ "${#COMP_WORDS[@]}" }} -eq 2 ]]; then
\builtin mapfile -t COMPREPLY < <(compgen -A directory -S / -- "${COMP_WORDS[-1]}")
\builtin mapfile -t COMPREPLY < \
<(\builtin compgen -A directory -S / -- "${COMP_WORDS[-1]}" || \builtin true)
# If there is a space after the last word, use interactive selection.
elif [[ -z ${COMP_WORDS[-1]} ]]; then
\local result
result="$(\builtin command zoxide query -i -- "${COMP_WORDS[@]:1}")" &&
\builtin local result
result="$(\command zoxide query -i -- "{{ "${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}" }}")" &&
COMPREPLY=("${__zoxide_z_prefix}${result}")
\builtin printf '\e[5n'
fi

View File

@ -8,16 +8,16 @@
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
__zoxide_pwd() {
{%- if resolve_symlinks %}
\pwd -P
\command pwd -P
{%- else %}
\pwd -L
\command pwd -L
{%- endif %}
}
# cd + custom logic based on the value of _ZO_ECHO.
__zoxide_cd() {
# shellcheck disable=SC2164
\cd "$@" {%- if echo %} && __zoxide_pwd {%- endif %}
\command cd "$@" {%- if echo %} && __zoxide_pwd {%- endif %}
}
{{ section }}
@ -31,7 +31,7 @@ __zoxide_cd() {
{%- when InitHook::Prompt -%}
# Hook to add new entries to the database.
__zoxide_hook() {
zoxide add -- "$(__zoxide_pwd)"
\command zoxide add -- "$(__zoxide_pwd || \builtin true)"
}
# Initialize hook.
@ -40,7 +40,7 @@ if [ "${PS1:=}" = "${PS1#*\$(__zoxide_hook)}" ]; then
fi
{%- when InitHook::Pwd -%}
\printf "%s\n%s\n" \
\command printf "%s\n%s\n" \
"zoxide: PWD hooks are not supported on POSIX shells." \
" Use 'zoxide init posix --hook prompt' instead."
@ -60,19 +60,20 @@ __zoxide_z() {
__zoxide_cd "${OLDPWD}"
else
# shellcheck disable=SC2016
\printf 'zoxide: $OLDPWD is not set'
\command printf 'zoxide: $OLDPWD is not set'
return 1
fi
elif [ "$#" -eq 1 ] && [ -d "$1" ]; then
__zoxide_cd "$1"
else
__zoxide_result="$(zoxide query --exclude "$(__zoxide_pwd)" -- "$@")" && __zoxide_cd "${__zoxide_result}"
__zoxide_result="$(\command zoxide query --exclude "$(__zoxide_pwd || \builtin true)" -- "$@")" &&
__zoxide_cd "${__zoxide_result}"
fi
}
# Jump to a directory using interactive search.
__zoxide_zi() {
__zoxide_result="$(zoxide query -i -- "$@")" && __zoxide_cd "${__zoxide_result}"
__zoxide_result="$(\command zoxide query -i -- "$@")" && __zoxide_cd "${__zoxide_result}"
}
{{ section }}
@ -84,10 +85,10 @@ __zoxide_zi() {
# Remove definitions.
__zoxide_unset() {
\unset -f "$@" >/dev/null 2>&1
\unset -v "$@" >/dev/null 2>&1
\command unset -f "$@" >/dev/null 2>&1
\command unset -v "$@" >/dev/null 2>&1
# shellcheck disable=SC1001
\unalias "$@" >/dev/null 2>&1 || \:
\command unalias "$@" >/dev/null 2>&1 || \:
}
__zoxide_unset '{{cmd}}'

View File

@ -30,7 +30,7 @@ function __zoxide_cd() {
{% else -%}
# Hook to add new entries to the database.
function __zoxide_hook() {
zoxide add -- "$(__zoxide_pwd)"
\command zoxide add -- "$(__zoxide_pwd || \builtin true)"
}
# Initialize hook.
@ -52,29 +52,29 @@ fi
# Jump to a directory using only keywords.
function __zoxide_z() {
if [ "$#" -eq 0 ]; then
if [[ "$#" -eq 0 ]]; then
__zoxide_cd ~
elif [ "$#" -eq 1 ] && [ "$1" = '-' ]; then
if [ -n "${OLDPWD}" ]; then
elif [[ "$#" -eq 1 ]] && [[ "$1" = '-' ]]; then
if [[ -n "${OLDPWD}" ]]; then
__zoxide_cd "${OLDPWD}"
else
# shellcheck disable=SC2016
\builtin printf 'zoxide: $OLDPWD is not set'
return 1
fi
elif [ "$#" -eq 1 ] && [ -d "$1" ]; then
elif [[ "$#" -eq 1 ]] && [[ -d "$1" ]]; then
__zoxide_cd "$1"
else
\builtin local result
result="$(zoxide query --exclude "$(__zoxide_pwd)" -- "$@")" \
&& __zoxide_cd "${result}"
result="$(\command zoxide query --exclude "$(__zoxide_pwd || \builtin true)" -- "$@")" &&
__zoxide_cd "${result}"
fi
}
# Jump to a directory using interactive search.
function __zoxide_zi() {
\builtin local result
result="$(zoxide query -i -- "$@")" && __zoxide_cd "${result}"
result="$(\command zoxide query -i -- "$@")" && __zoxide_cd "${result}"
}
{{ section }}
@ -86,8 +86,8 @@ function __zoxide_zi() {
# Remove definitions.
function __zoxide_unset() {
\builtin unalias "$@" &>/dev/null || true
\builtin unfunction "$@" &>/dev/null || true
\builtin unalias "$@" &>/dev/null || \builtin true
\builtin unfunction "$@" &>/dev/null || \builtin true
\builtin unset "$@" &>/dev/null
}
@ -101,6 +101,43 @@ function {{cmd}}i() {
__zoxide_zi "$@"
}
if [[ -o zle ]]; then
function _{{cmd}}() {
\builtin local buffer tokens
# shellcheck disable=SC2034,SC2153,SC2154
buffer="${BUFFER}."
# shellcheck disable=SC2206,SC2296
tokens=(${(Q)${(z)buffer}})
if [[ "{{ "${#tokens[@]}" }}" -eq 2 ]]; then
_files -/
elif [[ "${tokens[-1]}" == '.' ]]; then
\builtin printf '\e[5n'
fi
}
function _{{cmd}}_helper() {
\builtin local tokens result
# shellcheck disable=SC2154,SC2206,SC2296
tokens=(${(Q)${(z)BUFFER}})
# shellcheck disable=SC2086
if result="$(\command zoxide query -i -- ${tokens[2,-1]})"; then
# shellcheck disable=SC2034
RBUFFER=''
# shellcheck disable=SC2034,SC2296
LBUFFER="${tokens[1]} ${(q-)result}"
fi
\builtin zle reset-prompt
}
\builtin zle -N _{{cmd}}_helper
\builtin bindkey "\e[0n" _{{cmd}}_helper
if [[ "${+functions[compdef]}" -ne 0 ]]; then
\compdef -d {{cmd}}
\compdef _{{cmd}} {{cmd}}
fi
fi
{%- when None %}
{{ not_configured }}

View File

@ -1,7 +1,7 @@
[package]
name = "xtask"
version = "0.1.0"
edition = "2018"
edition = "2021"
publish = false
[dependencies]

View File

@ -87,7 +87,11 @@ fn run_fmt(nix_enabled: bool, check: bool) -> Result<()> {
fn run_lint(nix_enabled: bool) -> Result<()> {
// Run cargo-clippy.
let color: &[&str] = if is_ci() { &["--color=always"] } else { &[] };
Command::new("cargo").args(&["clippy", "--all-features", "--all-targets"]).args(color)._run()?;
Command::new("cargo")
.args(&["clippy", "--all-features", "--all-targets"])
.args(color)
.args(&["--", "-Dwarnings"])
._run()?;
if nix_enabled {
// Run cargo-audit.