diff --git a/contrib/completions/_zoxide b/contrib/completions/_zoxide index 97e654f3..53117a1a 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 82b174e3..7e19cd34 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 642908e6..6f4c6fda 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 1e0d4045..f1dce89a 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 b7b7400e..591363c8 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 7359786c..e3a56012 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 980513ec..c67f1e23 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 37fe1a24..d0e5ba44 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,44 @@ 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(""); + } + } + + #[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 new file mode 100644 index 00000000..84618bc7 --- /dev/null +++ b/templates/murex.txt @@ -0,0 +1,114 @@ +{%- let section = "# =============================================================================\n#" -%} +{%- let not_configured = "# -- not configured --" -%} + +{{ 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. +# + +{% match hook %} +{%- when InitHook::None -%} +{{ not_configured }} + +{%- when InitHook::Prompt -%} +# Initialize hook to add new entries to the database. +fexec builtin event onPrompt __zoxide_hook=command-completion { + exec zoxide add -- $PWD +} + +{%- when InitHook::Pwd -%} +# Emulate a PWD hook by tracking the last directory and updating on prompt. +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 -> global: str __zoxide_oldpwd + exec zoxide add -- $PWD + } +} + +{%- endmatch %} + +{{ section }} +# When using zoxide with --no-cmd, alias these internal functions as desired. +# + +# Jump to a directory using only keywords. +fexec builtin function __zoxide_z { + __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 + } + # 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 { + 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 { + # 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 }} +# 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 shell configuration file (usually ~/.murex_profile): +# +# zoxide init murex -> source