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.
#