From c1d7c4f4c73e3162127ebc5f02f47ac6ca721f61 Mon Sep 17 00:00:00 2001 From: Ajeet D'Souza <98ajeet@gmail.com> Date: Fri, 13 Sep 2024 05:00:42 +0530 Subject: [PATCH] Add support for ksh (#885) --- .github/workflows/release.yml | 2 + CHANGELOG.md | 26 +++++-- README.md | 15 +++- contrib/completions/_zoxide | 2 +- contrib/completions/zoxide.bash | 2 +- contrib/completions/zoxide.ts | 1 + man/man1/zoxide-init.1 | 7 ++ shell.nix | 1 + src/cmd/cmd.rs | 1 + src/cmd/init.rs | 3 +- src/shell.rs | 45 ++++++++++-- templates/bash.txt | 2 + templates/ksh.txt | 118 ++++++++++++++++++++++++++++++++ templates/posix.txt | 2 + templates/zsh.txt | 2 + 15 files changed, 216 insertions(+), 13 deletions(-) create mode 100644 templates/ksh.txt diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e94cbe4..4c63648 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,8 @@ name: release on: push: + branches: [main] + pull_request: workflow_dispatch: env: CARGO_INCREMENTAL: 0 diff --git a/CHANGELOG.md b/CHANGELOG.md index f531604..057141f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,15 +9,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- zsh: improved `cd` completions. +- Lazily delete excluded directories from the database. +- fish: detect infinite loop when using `alias cd=z`. +- Installer: added flags for `--bin-dir`, `--man-dir`, `--arch`, and `--sudo`. +- Nushell: support for v0.94.0+. +- bash/fish/zsh: support for `z -- dir` style queries. +- fish: improved Space-Tab completions. +- ksh: added support for the Korn shell. + +### Changed + +- fzf: removed `--select-1` from default options. The interactive selector will + now open up even if there is only one match. +- Enforce that `$_ZO_DATA_DIR` is an absolute path. + ### Fixed - zsh: Space-Tab completion repeating output multiple times when matching single directory -- fish: detect infinite loop when using `alias cd=z`. - fish / Nushell / PowerShell: handle queries that look like args (e.g. `z -x`). -- zsh: better cd completions. -- elvish: `z -` now work as expected. -- Lazily delete excluded directories from the database. +- elvish: `z -` now works as expected. +- fish: generated shell code avoids using aliased builtins. +- fish: `cd` command is now copied directly from + `$__fish_data_dir/functions/cd.fish`. This should minimize the chances of an + infinite loop when aliasing `cd=z`. ## [0.9.4] - 2024-02-21 diff --git a/README.md b/README.md index 71dde2d..9d67f57 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ zoxide can be installed in 4 easy steps: > > | Repository | Instructions | > | ---------- | -------------------- | - > | [Termux] | `pkg install zoxide` | + > | [Termux] | `pkg install zoxide` | > > Or, run this command in your terminal: > @@ -237,12 +237,23 @@ zoxide can be installed in 4 easy steps: > Add this to the **end** of your config file (usually > `~/.config/fish/config.fish`): > - > ```fish + > ```sh > zoxide init fish | source > ``` +
+ Ksh + + > Add this to the **end** of your config file (usually `~/.kshrc`): + > + > ```sh + > eval "$(zoxide init ksh)" + > ``` + +
+
Nushell diff --git a/contrib/completions/_zoxide b/contrib/completions/_zoxide index fd898e6..5be044e 100644 --- a/contrib/completions/_zoxide +++ b/contrib/completions/_zoxide @@ -112,7 +112,7 @@ _arguments "${_arguments_options[@]}" \ '--help[Print help]' \ '-V[Print version]' \ '--version[Print version]' \ -':shell:(bash elvish fish nushell posix powershell xonsh zsh)' \ +':shell:(bash elvish fish ksh nushell posix powershell xonsh zsh)' \ && ret=0 ;; (query) diff --git a/contrib/completions/zoxide.bash b/contrib/completions/zoxide.bash index 73dbd45..6bc165a 100644 --- a/contrib/completions/zoxide.bash +++ b/contrib/completions/zoxide.bash @@ -165,7 +165,7 @@ _zoxide() { return 0 ;; zoxide__init) - opts="-h -V --no-cmd --cmd --hook --help --version bash elvish fish nushell posix powershell xonsh zsh" + opts="-h -V --no-cmd --cmd --hook --help --version bash elvish fish ksh nushell posix powershell xonsh zsh" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/contrib/completions/zoxide.ts b/contrib/completions/zoxide.ts index 0200591..1e987b1 100644 --- a/contrib/completions/zoxide.ts +++ b/contrib/completions/zoxide.ts @@ -182,6 +182,7 @@ const completion: Fig.Spec = { "bash", "elvish", "fish", + "ksh", "nushell", "posix", "powershell", diff --git a/man/man1/zoxide-init.1 b/man/man1/zoxide-init.1 index 387c318..b637295 100644 --- a/man/man1/zoxide-init.1 +++ b/man/man1/zoxide-init.1 @@ -30,6 +30,13 @@ Add this to the \fBend\fR of your config file (usually \fBzoxide init fish | source\fR .fi .TP +.B ksh +Add this to the \fBend\fR of your config file (usually \fB~/.kshrc\fR): +.sp +.nf + \fBeval $(zoxide init ksh)\fR +.fi +.TP .B nushell Add this to the \fBend\fR of your env file (find it by running \fB$nu.env-path\fR in Nushell): diff --git a/shell.nix b/shell.nix index c1e66ff..8e9c6c4 100644 --- a/shell.nix +++ b/shell.nix @@ -24,6 +24,7 @@ in pkgs.mkShell { pkgs.dash pkgs.elvish pkgs.fish + pkgs.ksh pkgs.nushell pkgs.powershell pkgs.xonsh diff --git a/src/cmd/cmd.rs b/src/cmd/cmd.rs index 0e5f6c4..3b05a60 100644 --- a/src/cmd/cmd.rs +++ b/src/cmd/cmd.rs @@ -137,6 +137,7 @@ pub enum InitShell { Bash, Elvish, Fish, + Ksh, Nushell, Posix, Powershell, diff --git a/src/cmd/init.rs b/src/cmd/init.rs index 3f79ed6..70bdca0 100644 --- a/src/cmd/init.rs +++ b/src/cmd/init.rs @@ -6,7 +6,7 @@ use rinja::Template; use crate::cmd::{Init, InitShell, Run}; use crate::config; use crate::error::BrokenPipeHandler; -use crate::shell::{Bash, Elvish, Fish, Nushell, Opts, Posix, Powershell, Xonsh, Zsh}; +use crate::shell::{Bash, Elvish, Fish, Ksh, Nushell, Opts, Posix, Powershell, Xonsh, Zsh}; impl Run for Init { fn run(&self) -> Result<()> { @@ -19,6 +19,7 @@ impl Run for Init { InitShell::Bash => Bash(opts).render(), InitShell::Elvish => Elvish(opts).render(), InitShell::Fish => Fish(opts).render(), + InitShell::Ksh => Ksh(opts).render(), InitShell::Nushell => Nushell(opts).render(), InitShell::Posix => Posix(opts).render(), InitShell::Powershell => Powershell(opts).render(), diff --git a/src/shell.rs b/src/shell.rs index 742c6fd..19e72c2 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -26,6 +26,7 @@ macro_rules! make_template { make_template!(Bash, "bash.txt"); make_template!(Elvish, "elvish.txt"); make_template!(Fish, "fish.txt"); +make_template!(Ksh, "ksh.txt"); make_template!(Nushell, "nushell.txt"); make_template!(Posix, "posix.txt"); make_template!(Powershell, "powershell.txt"); @@ -70,7 +71,7 @@ mod tests { let source = Bash(&opts).render().unwrap(); Command::new("shellcheck") - .args(["--enable", "all", "--shell", "bash", "-"]) + .args(["--enable=all", "-"]) .write_stdin(source) .assert() .success() @@ -148,6 +149,42 @@ mod tests { .stderr(""); } + #[apply(opts)] + fn ksh_ksh(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { + let opts = Opts { cmd, hook, echo, resolve_symlinks }; + let source = Ksh(&opts).render().unwrap(); + Command::new("ksh").args(["-n", "-c", &source]).assert().success().stdout("").stderr(""); + } + + #[apply(opts)] + fn ksh_shellcheck(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { + let opts = Opts { cmd, hook, echo, resolve_symlinks }; + let source = Ksh(&opts).render().unwrap(); + + Command::new("shellcheck") + .args(["--enable=all", "-"]) + .write_stdin(source) + .assert() + .success() + .stdout("") + .stderr(""); + } + + #[apply(opts)] + fn ksh_shfmt(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { + let opts = Opts { cmd, hook, echo, resolve_symlinks }; + let mut source = Ksh(&opts).render().unwrap(); + source.push('\n'); + + Command::new("shfmt") + .args(["--diff", "--indent=4", "--language-dialect=mksh", "--simplify", "-"]) + .write_stdin(source) + .assert() + .success() + .stdout("") + .stderr(""); + } + #[apply(opts)] fn nushell_nushell(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { let opts = Opts { cmd, hook, echo, resolve_symlinks }; @@ -196,12 +233,12 @@ mod tests { } #[apply(opts)] - fn posix_shellcheck_(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { + fn posix_shellcheck(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { let opts = Opts { cmd, hook, echo, resolve_symlinks }; let source = Posix(&opts).render().unwrap(); Command::new("shellcheck") - .args(["--enable", "all", "--shell", "sh", "-"]) + .args(["--enable=all", "-"]) .write_stdin(source) .assert() .success() @@ -298,7 +335,7 @@ mod tests { // ShellCheck doesn't support zsh yet: https://github.com/koalaman/shellcheck/issues/809 Command::new("shellcheck") - .args(["--enable", "all", "--shell", "bash", "-"]) + .args(["--enable=all", "-"]) .write_stdin(source) .assert() .success() diff --git a/templates/bash.txt b/templates/bash.txt index 368d3f6..8a4ba47 100644 --- a/templates/bash.txt +++ b/templates/bash.txt @@ -1,6 +1,8 @@ {%- let section = "# =============================================================================\n#" -%} {%- let not_configured = "# -- not configured --" -%} +# shellcheck shell=bash + {{ section }} # Utility functions for zoxide. # diff --git a/templates/ksh.txt b/templates/ksh.txt new file mode 100644 index 0000000..2f8fd06 --- /dev/null +++ b/templates/ksh.txt @@ -0,0 +1,118 @@ +{%- let section = "# =============================================================================\n#" -%} +{%- let not_configured = "# -- not configured --" -%} + +# shellcheck shell=ksh + +{{ section }} +# Utility functions for zoxide. +# + +# pwd based on the value of _ZO_RESOLVE_SYMLINKS. +__zoxide_pwd() { +{%- if cfg!(windows) %} + \command cygpath -w "$(\builtin pwd -P)" +{%- else if resolve_symlinks %} + \command pwd -P +{%- else %} + \command pwd -L +{%- endif %} +} + +# cd + custom logic based on the value of _ZO_ECHO. +__zoxide_cd() { + # shellcheck disable=SC2164 + \command cd "$@" {%- if echo %} && __zoxide_pwd {%- endif %} +} + +{{ section }} +# Hook configuration for zoxide. +# + +{% match hook %} +{%- when InitHook::None -%} +{{ not_configured }} + +{%- when InitHook::Prompt -%} +# Hook to add new entries to the database. +__zoxide_hook() { + \command zoxide add -- "$(__zoxide_pwd || \builtin true)" +} + +# Initialize hook. +if [[ ${PS1:=} == "${PS1#*\$(__zoxide_hook)}" ]]; then + PS1="${PS1}\$(__zoxide_hook)" +fi + +{%- when InitHook::Pwd -%} +# Hook to add new entries to the database. +__zoxide_hook() { + __zoxide_retval="$?" + __zoxide_newpwd="$(__zoxide_pwd)" + if [[ ${__zoxide_oldpwd:-__zoxide_newpwd} != "${__zoxide_newpwd}" ]]; then + \command zoxide add -- "${__zoxide_newpwd}" + __zoxide_oldpwd="${__zoxide_newpwd}" + fi + return "${__zoxide_retval}" +} + +# Initialize hook. +__zoxide_trap="$(\command trap -p DEBUG)" +if [[ ${__zoxide_trap} != *'__zoxide_hook'* ]]; then + \command trap "__zoxide_hook;${__zoxide_trap#';'}" DEBUG +fi +{%- endmatch %} + +{{ section }} +# When using zoxide with --no-cmd, alias these internal functions as desired. +# + +# Jump to a directory using only keywords. +__zoxide_z() { + # shellcheck disable=SC2199 + if (($# == 0)); then + __zoxide_cd ~ + elif [[ ($# == 1) && $1 == '-' ]]; then + __zoxide_cd "${OLDPWD}" + elif [[ ($# == 1) && -d $1 ]]; then + __zoxide_cd "$1" + elif [[ ($# == 2) && $1 == '--' ]]; then + __zoxide_cd "$2" + else + # shellcheck disable=SC2312 + __zoxide_result="$(\command zoxide query --exclude "$(__zoxide_pwd)" -- "$@")" && + __zoxide_cd "${__zoxide_result}" + fi +} + +# Jump to a directory using interactive search. +__zoxide_zi() { + __zoxide_result="$(\command zoxide query --interactive -- "$@")" && __zoxide_cd "${__zoxide_result}" +} + +{{ section }} +# Commands for zoxide. Disable these using --no-cmd. +# + +{%- match cmd %} +{%- when Some with (cmd) %} + +\command unalias {{cmd}} >/dev/null 2>&1 || \true +{{cmd}}() { + __zoxide_z "$@" +} + +\command unalias {{cmd}}i >/dev/null 2>&1 || \true +{{cmd}}i() { + __zoxide_zi "$@" +} + +{%- when None %} + +{{ not_configured }} + +{%- endmatch %} + +{{ section }} +# To initialize zoxide, add this to your configuration (usually ~/.kshrc): +# +# eval "$(zoxide init ksh)" diff --git a/templates/posix.txt b/templates/posix.txt index ca4b53f..a1793ad 100644 --- a/templates/posix.txt +++ b/templates/posix.txt @@ -1,6 +1,8 @@ {%- let section = "# =============================================================================\n#" -%} {%- let not_configured = "# -- not configured --" -%} +# shellcheck shell=sh + {{ section }} # Utility functions for zoxide. # diff --git a/templates/zsh.txt b/templates/zsh.txt index a2d9542..a8aed66 100644 --- a/templates/zsh.txt +++ b/templates/zsh.txt @@ -1,6 +1,8 @@ {%- let section = "# =============================================================================\n#" -%} {%- let not_configured = "# -- not configured --" -%} +# shellcheck shell=bash + {{ section }} # Utility functions for zoxide. #