From 255419606a7aefbd747b0b81765746172a106ee5 Mon Sep 17 00:00:00 2001 From: Jeff Melton Date: Sat, 6 Sep 2025 22:38:01 -0500 Subject: [PATCH 1/8] Adds Murex shell support to init --- contrib/completions/_zoxide | 2 +- contrib/completions/zoxide.bash | 2 +- contrib/completions/zoxide.nu | 2 +- contrib/completions/zoxide.ts | 1 + shell.nix | 1 + src/cmd/cmd.rs | 1 + src/cmd/init.rs | 3 +- src/shell.rs | 17 +++++++ templates/murex.txt | 83 +++++++++++++++++++++++++++++++++ 9 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 templates/murex.txt diff --git a/contrib/completions/_zoxide b/contrib/completions/_zoxide index 97e654f3c..53117a1aa 100644 --- a/contrib/completions/_zoxide +++ b/contrib/completions/_zoxide @@ -114,7 +114,7 @@ _arguments "${_arguments_options[@]}" : \ '--help[Print help]' \ '-V[Print version]' \ '--version[Print version]' \ -':shell:(bash elvish fish nushell posix powershell tcsh xonsh zsh)' \ +':shell:(bash elvish fish murex nushell posix powershell tcsh xonsh zsh)' \ && ret=0 ;; (query) diff --git a/contrib/completions/zoxide.bash b/contrib/completions/zoxide.bash index 82b174e33..7e19cd347 100644 --- a/contrib/completions/zoxide.bash +++ b/contrib/completions/zoxide.bash @@ -177,7 +177,7 @@ _zoxide() { return 0 ;; zoxide__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 murex nushell posix powershell tcsh xonsh zsh" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/contrib/completions/zoxide.nu b/contrib/completions/zoxide.nu index 642908e68..6f4c6fda6 100644 --- a/contrib/completions/zoxide.nu +++ b/contrib/completions/zoxide.nu @@ -57,7 +57,7 @@ module completions { ] def "nu-complete zoxide init shell" [] { - [ "bash" "elvish" "fish" "nushell" "posix" "powershell" "tcsh" "xonsh" "zsh" ] + [ "bash" "elvish" "fish" "murex" "nushell" "posix" "powershell" "tcsh" "xonsh" "zsh" ] } def "nu-complete zoxide init hook" [] { diff --git a/contrib/completions/zoxide.ts b/contrib/completions/zoxide.ts index 1e0d4045c..f1dce89af 100644 --- a/contrib/completions/zoxide.ts +++ b/contrib/completions/zoxide.ts @@ -191,6 +191,7 @@ const completion: Fig.Spec = { "bash", "elvish", "fish", + "murex", "nushell", "posix", "powershell", diff --git a/shell.nix b/shell.nix index b7b7400e1..591363c85 100644 --- a/shell.nix +++ b/shell.nix @@ -27,6 +27,7 @@ in pkgs.mkShell { pkgs.ksh pkgs.nushell pkgs.powershell + pkgs.murex pkgs.tcsh pkgs.xonsh pkgs.zsh diff --git a/src/cmd/cmd.rs b/src/cmd/cmd.rs index 7359786cf..e3a560129 100644 --- a/src/cmd/cmd.rs +++ b/src/cmd/cmd.rs @@ -149,6 +149,7 @@ pub enum InitShell { Bash, Elvish, Fish, + Murex, Nushell, #[clap(alias = "ksh")] Posix, diff --git a/src/cmd/init.rs b/src/cmd/init.rs index 980513ec0..c67f1e237 100644 --- a/src/cmd/init.rs +++ b/src/cmd/init.rs @@ -6,7 +6,7 @@ use askama::Template; use crate::cmd::{Init, InitShell, Run}; use crate::config; use crate::error::BrokenPipeHandler; -use crate::shell::{Bash, Elvish, Fish, Nushell, Opts, Posix, Powershell, Tcsh, Xonsh, Zsh}; +use crate::shell::{Bash, Elvish, Fish, Murex, Nushell, Opts, Posix, Powershell, Tcsh, 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::Murex => Murex(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 37fe1a24d..f826698a4 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!(Murex, "murex.txt"); make_template!(Nushell, "nushell.txt"); make_template!(Posix, "posix.txt"); make_template!(Powershell, "powershell.txt"); @@ -249,6 +250,22 @@ mod tests { .stderr(""); } + #[apply(opts)] + fn murex_murex(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { + let opts = Opts { cmd, hook, echo, resolve_symlinks }; + let source = Murex(&opts).render().unwrap(); + + let assert = Command::new("murex") + .args(["-c", &source, "--quiet"]) + .assert() + .success() + .stderr(""); + + if opts.hook != InitHook::Pwd { + assert.stdout(""); + } + } + #[apply(opts)] fn tcsh_tcsh(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { let opts = Opts { cmd, hook, echo, resolve_symlinks }; diff --git a/templates/murex.txt b/templates/murex.txt new file mode 100644 index 000000000..0c39fd093 --- /dev/null +++ b/templates/murex.txt @@ -0,0 +1,83 @@ +{%- let section = "# =============================================================================\n#" -%} +{%- let not_configured = "# -- not configured --" -%} + +# Code generated by zoxide. DO NOT EDIT. + +{{ section }} +# Hook configuration for zoxide. +# + +{% match hook %} +{%- when InitHook::None -%} +{{ not_configured }} + +{%- when InitHook::Prompt -%} +# Update database after each command completes. +event onPrompt zoxide=command-completion { + zoxide add -- $PWD +} + +{%- when InitHook::Pwd -%} +out "zoxide: PWD hooks are not supported on Murex." +out " Use 'zoxide init murex --hook prompt' instead." + +{%- endmatch %} + +{{ section }} +# When using zoxide with --no-cmd, alias these internal functions as desired. +# + +# Jump to a directory using only keywords. +function __zoxide_z { + # No args: go home + if { !$ARGS } { + cd ~ + {%- if echo %} out $PWD {%- endif %} + return + } + + # Previous dir + if { $ARGS[0] == "-" } { + cd - + {%- if echo %} out $PWD {%- endif %} + return + } + + # Query and jump + zoxide query --exclude $PWD -- @PARAMS -> set: str __z + cd $__z + {%- if echo %} + out $PWD + {%- endif %} +} + +# Jump to a directory using interactive search. +function __zoxide_zi { + zoxide query --interactive -- @PARAMS -> set: str __z + cd $__z + {%- if echo %} + out $PWD + {%- endif %} +} + +{{ section }} +# Commands for zoxide. Disable these using --no-cmd. +# + +{%- match cmd %} +{%- when Some with (cmd) %} + +function {{cmd}} { __zoxide_z @PARAMS } +function {{cmd}}i { __zoxide_zi @PARAMS } + +{%- when None %} + +{{ not_configured }} + +{%- endmatch %} + +{{ section }} +# To initialize zoxide, add this to your Murex profile (~/.murex_profile): +# +# zoxide init murex --hook prompt -> source + From 11362b0c974e5cb8bba681cba4c87e0aae1ebc94 Mon Sep 17 00:00:00 2001 From: Ajeet D'Souza <98ajeet@gmail.com> Date: Sun, 7 Sep 2025 11:05:54 +0530 Subject: [PATCH 2/8] Bugfixes + consistency --- templates/murex.txt | 62 ++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/templates/murex.txt b/templates/murex.txt index 0c39fd093..cb39f735f 100644 --- a/templates/murex.txt +++ b/templates/murex.txt @@ -1,7 +1,17 @@ {%- let section = "# =============================================================================\n#" -%} {%- let not_configured = "# -- not configured --" -%} -# Code generated by zoxide. DO NOT EDIT. +{{ section }} +# Utility functions for zoxide. +# + +# cd + custom logic based on the value of _ZO_ECHO. +fexec builtin function __zoxide_cd { + fexec builtin cd $PARAMS[0] + {%- if echo %} + fexec builtin out $PWD + {%- endif %} +} {{ section }} # Hook configuration for zoxide. @@ -12,14 +22,14 @@ {{ not_configured }} {%- when InitHook::Prompt -%} -# Update database after each command completes. -event onPrompt zoxide=command-completion { - zoxide add -- $PWD +# Initialize hook to add new entries to the database. +fexec builtin event onPrompt __zoxide_hook=command-completion { + exec zoxide add -- $PWD } {%- when InitHook::Pwd -%} -out "zoxide: PWD hooks are not supported on Murex." -out " Use 'zoxide init murex --hook prompt' instead." +fexec builtin out "zoxide: PWD hooks are not supported on Murex. +fexec builtin out " Use 'zoxide init murex --hook prompt' instead." {%- endmatch %} @@ -28,36 +38,21 @@ out " Use 'zoxide init murex --hook prompt' instead." # # Jump to a directory using only keywords. -function __zoxide_z { - # No args: go home - if { !$ARGS } { - cd ~ - {%- if echo %} out $PWD {%- endif %} - return +fexec builtin function __zoxide_z { + if { !$PARAMS } then { + fexec function __zoxide_cd ~ + fexec builtin return } - - # Previous dir - if { $ARGS[0] == "-" } { - cd - - {%- if echo %} out $PWD {%- endif %} - return + if { $PARAMS[0] == "-" } then { + fexec function __zoxide_cd - + fexec builtin return } - - # Query and jump - zoxide query --exclude $PWD -- @PARAMS -> set: str __z - cd $__z - {%- if echo %} - out $PWD - {%- endif %} + exec zoxide query --exclude $PWD -- @PARAMS -> set: str __zoxide_result && fexec function __zoxide_cd $__zoxide_result } # Jump to a directory using interactive search. -function __zoxide_zi { - zoxide query --interactive -- @PARAMS -> set: str __z - cd $__z - {%- if echo %} - out $PWD - {%- endif %} +fexec builtin function __zoxide_zi { + exec zoxide query --interactive -- @PARAMS -> set: str __zoxide_result && fexec function __zoxide_cd $__zoxide_result } {{ section }} @@ -77,7 +72,6 @@ function {{cmd}}i { __zoxide_zi @PARAMS } {%- endmatch %} {{ section }} -# To initialize zoxide, add this to your Murex profile (~/.murex_profile): +# To initialize zoxide, add this to your shell configuration file (usually ~/.murex_profile): # -# zoxide init murex --hook prompt -> source - +# zoxide init murex --hook prompt -> source From 918595bca86b0291650cc64c06094bb1408de19d Mon Sep 17 00:00:00 2001 From: Ajeet D'Souza <98ajeet@gmail.com> Date: Sun, 7 Sep 2025 11:23:39 +0530 Subject: [PATCH 3/8] typo --- templates/murex.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/murex.txt b/templates/murex.txt index cb39f735f..f79e90d4b 100644 --- a/templates/murex.txt +++ b/templates/murex.txt @@ -28,7 +28,7 @@ fexec builtin event onPrompt __zoxide_hook=command-completion { } {%- when InitHook::Pwd -%} -fexec builtin out "zoxide: PWD hooks are not supported on Murex. +fexec builtin out "zoxide: PWD hooks are not supported on Murex." fexec builtin out " Use 'zoxide init murex --hook prompt' instead." {%- endmatch %} From e891fc9942f2b48878f6b303e91cb7fdf9fdecd9 Mon Sep 17 00:00:00 2001 From: Jeff Melton Date: Sun, 7 Sep 2025 10:41:03 -0500 Subject: [PATCH 4/8] implements feedback from maintainer - PWD hook - no-argument `z` goes HOME - empty query results no longer throw a stack trace --- templates/murex.txt | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/templates/murex.txt b/templates/murex.txt index f79e90d4b..240e742e4 100644 --- a/templates/murex.txt +++ b/templates/murex.txt @@ -28,8 +28,14 @@ fexec builtin event onPrompt __zoxide_hook=command-completion { } {%- when InitHook::Pwd -%} -fexec builtin out "zoxide: PWD hooks are not supported on Murex." -fexec builtin out " Use 'zoxide init murex --hook prompt' instead." +# Emulate a PWD hook by tracking the last directory and updating on prompt. +fexec builtin out $PWD -> set: str __zoxide_oldpwd +fexec builtin event onPrompt __zoxide_hook=command-completion { + if { $__zoxide_oldpwd != $PWD } then { + fexec builtin out $PWD -> set: str __zoxide_oldpwd + exec zoxide add -- $PWD + } +} {%- endmatch %} @@ -39,20 +45,37 @@ fexec builtin out " Use 'zoxide init murex --hook prompt' instead." # Jump to a directory using only keywords. fexec builtin function __zoxide_z { - if { !$PARAMS } then { - fexec function __zoxide_cd ~ - fexec builtin return - } + __zoxide_argc = 0 + trypipe { @PARAMS -> count -> set int __zoxide_argc } + if { $__zoxide_argc == 0 } then { fexec function __zoxide_cd $HOME; fexec builtin return } if { $PARAMS[0] == "-" } then { fexec function __zoxide_cd - fexec builtin return } - exec zoxide query --exclude $PWD -- @PARAMS -> set: str __zoxide_result && fexec function __zoxide_cd $__zoxide_result + # Quiet query: capture result; suppress noise; return 1 on no match + fexec builtin out '' -> set: str __zoxide_result + trypipe { + exec zoxide query --exclude $PWD -- @PARAMS -> set: str __zoxide_result + } + if { $__zoxide_result } then { + fexec function __zoxide_cd $__zoxide_result + } else { + fexec builtin return 1 + } } # Jump to a directory using interactive search. fexec builtin function __zoxide_zi { - exec zoxide query --interactive -- @PARAMS -> set: str __zoxide_result && fexec function __zoxide_cd $__zoxide_result + # Quiet interactive query; return 1 when no selection + fexec builtin out '' -> set: str __zoxide_result + trypipe { + exec zoxide query --interactive -- @PARAMS -> set: str __zoxide_result + } + if { $__zoxide_result } then { + fexec function __zoxide_cd $__zoxide_result + } else { + fexec builtin return 1 + } } {{ section }} From 7ab8293fc9ce608e27bbc533523acbbba1c7a9df Mon Sep 17 00:00:00 2001 From: Jeff Melton Date: Sun, 7 Sep 2025 12:24:12 -0500 Subject: [PATCH 5/8] fixes flaw in implementation; adds additional test coverage --- src/shell.rs | 22 ++++++++++++++++++++++ templates/murex.txt | 14 +++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/shell.rs b/src/shell.rs index f826698a4..d0e5ba448 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -266,6 +266,28 @@ mod tests { } } + #[test] + fn murex_template_has_direct_path_handling() { + let opts = Opts { cmd: Some("z"), hook: InitHook::None, echo: false, resolve_symlinks: false }; + let source = Murex(&opts).render().unwrap(); + + // Ensure murex z handles: "-- path" and tries direct cd on single-arg + assert!( + source.contains("if { $__zoxide_argc == 2 && $PARAMS[0] == \"--\" }"), + "murex template should handle literal path with --" + ); + assert!( + source.contains("fexec function __zoxide_cd $PARAMS[0]"), + "murex template should attempt cd directly on single-arg" + ); + + // Ensure __zoxide_zi exists (interactive-only) + assert!( + source.contains("fexec builtin function __zoxide_zi"), + "murex template should define __zoxide_zi" + ); + } + #[apply(opts)] fn tcsh_tcsh(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { let opts = Opts { cmd, hook, echo, resolve_symlinks }; diff --git a/templates/murex.txt b/templates/murex.txt index 240e742e4..91d50ceca 100644 --- a/templates/murex.txt +++ b/templates/murex.txt @@ -52,6 +52,18 @@ fexec builtin function __zoxide_z { fexec function __zoxide_cd - fexec builtin return } + # If a literal path is provided with "--", cd to it directly. + if { $__zoxide_argc == 2 && $PARAMS[0] == "--" } then { + fexec function __zoxide_cd $PARAMS[1] + fexec builtin return + } + # If a single argument is provided, try cd directly; if it fails, fall back to query. + if { $__zoxide_argc == 1 } then { + trypipe { + fexec function __zoxide_cd $PARAMS[0] + fexec builtin return + } + } # Quiet query: capture result; suppress noise; return 1 on no match fexec builtin out '' -> set: str __zoxide_result trypipe { @@ -66,7 +78,7 @@ fexec builtin function __zoxide_z { # Jump to a directory using interactive search. fexec builtin function __zoxide_zi { - # Quiet interactive query; return 1 when no selection + # Interactive query; return 1 when no selection fexec builtin out '' -> set: str __zoxide_result trypipe { exec zoxide query --interactive -- @PARAMS -> set: str __zoxide_result From 0f8583540fe54510da6bdc945e8cc6cc80f2cb61 Mon Sep 17 00:00:00 2001 From: Jeff Melton Date: Mon, 8 Sep 2025 10:42:07 -0500 Subject: [PATCH 6/8] fixing variable scoping issue --- templates/murex.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/templates/murex.txt b/templates/murex.txt index 91d50ceca..84618bc78 100644 --- a/templates/murex.txt +++ b/templates/murex.txt @@ -29,10 +29,12 @@ fexec builtin event onPrompt __zoxide_hook=command-completion { {%- when InitHook::Pwd -%} # Emulate a PWD hook by tracking the last directory and updating on prompt. -fexec builtin out $PWD -> set: str __zoxide_oldpwd +fexec builtin out $PWD -> global: str __zoxide_oldpwd fexec builtin event onPrompt __zoxide_hook=command-completion { + # Initialize global if missing (eg new session or cleared state) + if { !$__zoxide_oldpwd } then { fexec builtin out $PWD -> global: str __zoxide_oldpwd } if { $__zoxide_oldpwd != $PWD } then { - fexec builtin out $PWD -> set: str __zoxide_oldpwd + fexec builtin out $PWD -> global: str __zoxide_oldpwd exec zoxide add -- $PWD } } @@ -109,4 +111,4 @@ function {{cmd}}i { __zoxide_zi @PARAMS } {{ section }} # To initialize zoxide, add this to your shell configuration file (usually ~/.murex_profile): # -# zoxide init murex --hook prompt -> source +# zoxide init murex -> source From a03c34e9533afa73c7145789cc46fc2e7fa397d3 Mon Sep 17 00:00:00 2001 From: Ajeet D'Souza <98ajeet@gmail.com> Date: Wed, 19 Nov 2025 05:30:03 +0530 Subject: [PATCH 7/8] Upgrade murex --- shell.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell.nix b/shell.nix index 591363c85..ac26905fd 100644 --- a/shell.nix +++ b/shell.nix @@ -1,6 +1,6 @@ let pkgs = import (builtins.fetchTarball - "https://github.com/NixOS/nixpkgs/archive/ec9ef366451af88284d7dfd18ee017b7e86a0710.tar.gz") { + "https://github.com/NixOS/nixpkgs/archive/f7a9bed32efd78bf1fafb504e1a41228c2c0e7d2.tar.gz") { overlays = [ rust ]; }; rust = import (builtins.fetchTarball @@ -27,7 +27,7 @@ in pkgs.mkShell { pkgs.ksh pkgs.nushell pkgs.powershell - pkgs.murex + (pkgs.murex.overrideAttrs (old: { doCheck = false; })) pkgs.tcsh pkgs.xonsh pkgs.zsh From f81cd6014b8648bbf6f5481a176886d4b786af47 Mon Sep 17 00:00:00 2001 From: Ajeet D'Souza <98ajeet@gmail.com> Date: Wed, 19 Nov 2025 05:32:34 +0530 Subject: [PATCH 8/8] Only check exit code --- src/shell.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/shell.rs b/src/shell.rs index d0e5ba448..e41a86673 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -254,16 +254,10 @@ mod tests { fn murex_murex(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { let opts = Opts { cmd, hook, echo, resolve_symlinks }; let source = Murex(&opts).render().unwrap(); - - let assert = Command::new("murex") + Command::new("murex") .args(["-c", &source, "--quiet"]) .assert() - .success() - .stderr(""); - - if opts.hook != InitHook::Pwd { - assert.stdout(""); - } + .success(); } #[test]