From a64855dfd6d34446a41d27db2a34fc73b9ca8fdb Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 10 Dec 2024 13:28:01 +0100 Subject: [PATCH 1/7] client: add support for nushell in the `opam env` command --- src/client/opamArg.ml | 1 + src/client/opamClient.ml | 2 +- src/client/opamCommands.ml | 8 ++++---- src/client/opamConfigCommand.ml | 19 ++++++++++++++++--- src/client/opamConfigCommand.mli | 4 ++-- src/core/opamStd.ml | 6 +++++- src/core/opamStd.mli | 2 +- src/format/opamTypes.mli | 2 +- src/format/opamTypesBase.ml | 1 + src/state/opamEnv.ml | 22 +++++++++++++++++----- 10 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/client/opamArg.ml b/src/client/opamArg.ml index a2783006409..e8905644fb9 100644 --- a/src/client/opamArg.ml +++ b/src/client/opamArg.ml @@ -1155,6 +1155,7 @@ let shell_opt ?section cli validity = None,"csh",SH_csh; None,"zsh",SH_zsh; None,"fish",SH_fish; + None,"nu",SH_nu; Some cli2_2,"pwsh",SH_pwsh Powershell_pwsh; Some cli2_2,"cmd",SH_cmd; Some cli2_2,"powershell",SH_pwsh Powershell diff --git a/src/client/opamClient.ml b/src/client/opamClient.ml index 16d9368a35f..db9ba3b1355 100644 --- a/src/client/opamClient.ml +++ b/src/client/opamClient.ml @@ -1137,7 +1137,7 @@ let rec cygwin_menu ~bypass_checks ~interactive header = options, internal_option, Some warning | None -> match OpamStd.Sys.guess_shell_compat () with - | SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish -> + | SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish | SH_nu -> let default, warning = if kind = `Msys2 && OpamSystem.resolve_command pacman = None then internal_option, Printf.sprintf diff --git a/src/client/opamCommands.ml b/src/client/opamCommands.ml index cac6ffbeb8d..0ef13e2a324 100644 --- a/src/client/opamCommands.ml +++ b/src/client/opamCommands.ml @@ -1448,7 +1448,7 @@ let config cli = `Ok (OpamConfigCommand.env gt sw ~set_opamroot ~set_opamswitch ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) - ~pwsh ~cmd:(shell=SH_cmd) + ~pwsh ~cmd:(shell=SH_cmd) ~nu:(shell=SH_nu) ~inplace_path)) | Some `revert_env, [] -> OpamGlobalState.with_ `Lock_none @@ fun gt -> @@ -1458,7 +1458,7 @@ let config cli = `Ok (OpamConfigCommand.ensure_env gt sw; OpamConfigCommand.print_eval_env ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) - ~pwsh ~cmd:(shell=SH_cmd) + ~pwsh ~cmd:(shell=SH_cmd) ~nu:(shell=SH_nu) (OpamEnv.add [] []))) | Some `list, [] -> OpamGlobalState.with_ `Lock_none @@ fun gt -> @@ -1763,12 +1763,12 @@ let env cli = OpamConfigCommand.env gt sw ~set_opamroot ~set_opamswitch ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) - ~pwsh ~cmd:(shell=SH_cmd) + ~pwsh ~cmd:(shell=SH_cmd) ~nu:(shell=SH_nu) ~inplace_path); | true -> OpamConfigCommand.print_eval_env ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) - ~pwsh ~cmd:(shell=SH_cmd) + ~pwsh ~cmd:(shell=SH_cmd) ~nu:(shell=SH_nu) (OpamEnv.add [] []) in let open Common_config_flags in diff --git a/src/client/opamConfigCommand.ml b/src/client/opamConfigCommand.ml index 1fd67fef36c..9db2fd0b813 100644 --- a/src/client/opamConfigCommand.ml +++ b/src/client/opamConfigCommand.ml @@ -131,6 +131,14 @@ let print_sexp_env output env = aux env; output ")\n" +let rec print_nu_env output env = + match env with + | [] -> () + | (k,v,_)::r -> begin + Printf.ksprintf output "\t\"%s\" : \"%s\",\n" k v; + print_nu_env output r + end + let rec print_fish_env output env = let set_arr_cmd ?(modf=fun x -> x) k v = let v = modf @@ OpamStd.String.split v ':' in @@ -183,7 +191,7 @@ let print_without_cr s = output_string stdout s; flush stdout -let print_eval_env ~csh ~sexp ~fish ~pwsh ~cmd env = +let print_eval_env ~csh ~sexp ~fish ~pwsh ~cmd ~nu env = let env = (env : OpamTypes.env :> (string * string * string option) list) in let output_normally = OpamConsole.msg "%s" in let never_with_cr = @@ -202,6 +210,11 @@ let print_eval_env ~csh ~sexp ~fish ~pwsh ~cmd env = print_pwsh_env output_normally env else if cmd then print_cmd_env output_normally env + else if nu then begin + Printf.ksprintf never_with_cr "{\n"; + print_nu_env never_with_cr env; + Printf.ksprintf never_with_cr "}" + end else print_env never_with_cr env @@ -331,7 +344,7 @@ let ensure_env gt switch = ignore (ensure_env_aux gt switch) let env gt switch ?(set_opamroot=false) ?(set_opamswitch=false) - ~csh ~sexp ~fish ~pwsh ~cmd ~inplace_path = + ~csh ~sexp ~fish ~pwsh ~cmd ~nu ~inplace_path = log "config-env"; let opamroot_not_current = let current = gt.root in @@ -371,7 +384,7 @@ let env gt switch ?(set_opamroot=false) ?(set_opamswitch=false) let env = ensure_env_aux ~set_opamroot ~set_opamswitch ~force_path gt switch in - print_eval_env ~csh ~sexp ~fish ~pwsh ~cmd env + print_eval_env ~csh ~sexp ~fish ~pwsh ~cmd ~nu env [@@ocaml.warning "-16"] let subst gt fs = diff --git a/src/client/opamConfigCommand.mli b/src/client/opamConfigCommand.mli index a205bd376a5..d38f1a4b222 100644 --- a/src/client/opamConfigCommand.mli +++ b/src/client/opamConfigCommand.mli @@ -23,7 +23,7 @@ open OpamStateTypes val env: 'a global_state -> switch -> ?set_opamroot:bool -> ?set_opamswitch:bool -> - csh:bool -> sexp:bool -> fish:bool -> pwsh:bool -> cmd:bool -> + csh:bool -> sexp:bool -> fish:bool -> pwsh:bool -> cmd:bool -> nu:bool -> inplace_path:bool -> unit (** Ensures that the environment file exists in the given switch, regenerating @@ -32,7 +32,7 @@ val ensure_env: 'a global_state -> switch -> unit (** Like [env] but allows one to specify the precise env to print rather than compute it from a switch state *) -val print_eval_env: csh:bool -> sexp:bool -> fish:bool -> pwsh:bool -> cmd:bool -> env -> unit +val print_eval_env: csh:bool -> sexp:bool -> fish:bool -> pwsh:bool -> cmd:bool -> nu:bool -> env -> unit (** Display the content of all available packages variables *) val list: 'a switch_state -> name list -> unit diff --git a/src/core/opamStd.ml b/src/core/opamStd.ml index 253abf7fc7f..bf2886cee23 100644 --- a/src/core/opamStd.ml +++ b/src/core/opamStd.ml @@ -936,7 +936,7 @@ module OpamSys = struct fun () -> Lazy.force os type powershell_host = Powershell_pwsh | Powershell - type shell = SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish + type shell = SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish | SH_nu | SH_pwsh of powershell_host | SH_cmd let all_shells = @@ -944,6 +944,7 @@ module OpamSys = struct SH_zsh; SH_csh; SH_fish; + SH_nu; SH_pwsh Powershell_pwsh; SH_pwsh Powershell; SH_cmd] @@ -958,6 +959,7 @@ module OpamSys = struct | "zsh" -> Some SH_zsh | "bash" -> Some SH_bash | "fish" -> Some SH_fish + | "nu" -> Some SH_nu | "pwsh" -> Some (SH_pwsh Powershell_pwsh) | "dash" | "sh" -> Some SH_sh @@ -1078,6 +1080,8 @@ module OpamSys = struct match shell with | SH_fish -> Some (List.fold_left Filename.concat (home ".config") ["fish"; "config.fish"]) + | SH_nu -> + Some (List.fold_left Filename.concat (home ".config") ["nushell"; "env.nu"]) | SH_zsh -> let zsh_home f = try Filename.concat (Env.get "ZDOTDIR") f diff --git a/src/core/opamStd.mli b/src/core/opamStd.mli index d219de17640..971b2b637ad 100644 --- a/src/core/opamStd.mli +++ b/src/core/opamStd.mli @@ -503,7 +503,7 @@ module Sys : sig (** The different families of shells we know about *) type powershell_host = Powershell_pwsh | Powershell - type shell = SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish + type shell = SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish | SH_nu | SH_pwsh of powershell_host | SH_cmd (** List of all supported shells *) diff --git a/src/format/opamTypes.mli b/src/format/opamTypes.mli index e7bf8036128..0b8f174f7f8 100644 --- a/src/format/opamTypes.mli +++ b/src/format/opamTypes.mli @@ -321,7 +321,7 @@ type pin_kind = [ `version | OpamUrl.backend ] (** Shell compatibility modes *) type powershell_host = OpamStd.Sys.powershell_host = Powershell_pwsh | Powershell type shell = OpamStd.Sys.shell = - | SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish | SH_pwsh of powershell_host + | SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish | SH_nu | SH_pwsh of powershell_host | SH_cmd (** {2 Generic command-line definitions with filters} *) diff --git a/src/format/opamTypesBase.ml b/src/format/opamTypesBase.ml index 9e3d09a47a8..6a5126f90c1 100644 --- a/src/format/opamTypesBase.ml +++ b/src/format/opamTypesBase.ml @@ -42,6 +42,7 @@ let all_std_paths = let string_of_shell = function | SH_fish -> "fish" + | SH_nu -> "nu" | SH_csh -> "csh" | SH_zsh -> "zsh" | SH_sh -> "sh" diff --git a/src/state/opamEnv.ml b/src/state/opamEnv.ml index 7a671c30666..6c939b05fbc 100644 --- a/src/state/opamEnv.ml +++ b/src/state/opamEnv.ml @@ -879,6 +879,8 @@ let opam_env_invocation ?root ?switch ?(set_opamswitch=false) shell = Printf.sprintf " \"--%s=%s\"" argname | SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish -> Printf.sprintf " '--%s=%s'" argname + | SH_nu -> + Printf.sprintf " '--%s=%s'" argname in if filepath_needs_quote pathval then quoted pathval @@ -928,24 +930,25 @@ let eval_string gt ?(set_opamswitch=false) switch = (** The shells for which we generate init scripts (bash and sh are the same entry) *) -let shells_list = [ SH_sh; SH_zsh; SH_csh; SH_fish; SH_pwsh Powershell; SH_cmd ] +let shells_list = [ SH_sh; SH_zsh; SH_csh; SH_fish; SH_pwsh Powershell; SH_cmd ; SH_nu] let complete_file = function | SH_sh | SH_bash -> Some "complete.sh" | SH_zsh -> Some "complete.zsh" - | SH_csh | SH_fish | SH_pwsh _ | SH_cmd -> None + | SH_csh | SH_fish | SH_pwsh _ | SH_cmd | SH_nu-> None let env_hook_file = function | SH_sh | SH_bash -> Some "env_hook.sh" | SH_zsh -> Some "env_hook.zsh" | SH_csh -> Some "env_hook.csh" | SH_fish -> Some "env_hook.fish" - | SH_pwsh _ | SH_cmd -> None + | SH_pwsh _ | SH_cmd | SH_nu -> None let variables_file = function | SH_sh | SH_bash | SH_zsh -> "variables.sh" | SH_csh -> "variables.csh" | SH_fish -> "variables.fish" + | SH_nu -> "variables.nu" | SH_pwsh _ -> "variables.ps1" | SH_cmd -> "variables.cmd" @@ -954,13 +957,14 @@ let init_file = function | SH_zsh -> "init.zsh" | SH_csh -> "init.csh" | SH_fish -> "init.fish" + | SH_nu -> "init.nu" | SH_pwsh _ -> "init.ps1" | SH_cmd -> "init.cmd" let complete_script = function | SH_sh | SH_bash -> Some OpamScript.complete | SH_zsh -> Some OpamScript.complete_zsh - | SH_csh | SH_fish -> None + | SH_csh | SH_fish | SH_nu -> None | SH_pwsh _ | SH_cmd -> None let env_hook_script_base = function @@ -968,7 +972,7 @@ let env_hook_script_base = function | SH_zsh -> Some OpamScript.env_hook_zsh | SH_csh -> Some OpamScript.env_hook_csh | SH_fish -> Some OpamScript.env_hook_fish - | SH_pwsh _ | SH_cmd -> None + | SH_pwsh _ | SH_cmd | SH_nu -> None let export_in_shell ~unconditional shell = let make_comment comment_opt = @@ -1008,6 +1012,9 @@ let export_in_shell ~unconditional shell = Printf.sprintf "%sset -gx %s %s;\n" (make_comment comment) k v in + let nu (k,v,comment) = + Printf.sprintf "load-env (opam env %s %s %s)\n" (make_comment comment) k v + in let pwsh (k,v,comment) = Printf.sprintf "%s$env:%s=%s\n" (make_comment comment) k v in @@ -1020,6 +1027,7 @@ let export_in_shell ~unconditional shell = match shell with | SH_zsh | SH_bash | SH_sh -> sh | SH_fish -> fish + | SH_nu -> nu | SH_csh -> csh | SH_pwsh _ -> pwsh | SH_cmd -> cmd @@ -1062,6 +1070,8 @@ let source root shell f = | SH_fish -> let fname = unix_transform ~using_backslashes:true () in Printf.sprintf "test -r '%s' && source '%s' > /dev/null 2> /dev/null; or true\n" fname fname + | SH_nu -> + Printf.sprintf "%s:TODO" fname | SH_sh | SH_bash -> let fname = unix_transform () in Printf.sprintf "test -r '%s' && . '%s' > /dev/null 2> /dev/null || true\n" @@ -1097,6 +1107,8 @@ let if_interactive_script shell t e = Printf.sprintf "if ( $?prompt ) then\n %s%sendif\n" t @@ ielse e | SH_fish -> Printf.sprintf "if status is-interactive\n %s%send\n" t @@ ielse e + | SH_nu -> + Printf.sprintf "%sTODO%s" t @@ ielse e | SH_cmd -> Printf.sprintf "echo %%cmdcmdline%% | find /i \"%%~0\" >nul\nif errorlevel 1 (\n%s%s)\n" t @@ ielse_cmd e | SH_pwsh _ -> From d1490d38c02e75d5d30e10c9edba48939813b21d Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 10 Dec 2024 14:17:47 +0100 Subject: [PATCH 2/7] add changes to master_changes.md file --- master_changes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/master_changes.md b/master_changes.md index a39d475e315..7589177e6c0 100644 --- a/master_changes.md +++ b/master_changes.md @@ -69,6 +69,7 @@ users) ## Clean ## Env + * [NEW] Add support for nushell in the opam env command ## Opamfile * The `url` file now only supports the legacy opam 1.2 fields [#6827 @kit-ty-kate] From 5ec5f349f52e0f5b133452033e53b2412b3534af Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Thu, 26 Mar 2026 22:18:18 -0500 Subject: [PATCH 3/7] Make initial shell environment work on nushell Auto-reloading is not yet implemented --- src/core/opamStd.ml | 6 +++++- src/state/opamEnv.ml | 16 +++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/core/opamStd.ml b/src/core/opamStd.ml index bf2886cee23..1e902f1138b 100644 --- a/src/core/opamStd.ml +++ b/src/core/opamStd.ml @@ -1081,7 +1081,11 @@ module OpamSys = struct | SH_fish -> Some (List.fold_left Filename.concat (home ".config") ["fish"; "config.fish"]) | SH_nu -> - Some (List.fold_left Filename.concat (home ".config") ["nushell"; "env.nu"]) + let config_home = + try Env.get "XDG_CONFIG_HOME" + with Not_found -> home ".config" + in + Some (List.fold_left Filename.concat config_home ["nushell"; "autoload"; "opam.nu"]) | SH_zsh -> let zsh_home f = try Filename.concat (Env.get "ZDOTDIR") f diff --git a/src/state/opamEnv.ml b/src/state/opamEnv.ml index 6c939b05fbc..ec8ee215b40 100644 --- a/src/state/opamEnv.ml +++ b/src/state/opamEnv.ml @@ -830,6 +830,8 @@ let shell_eval_invocation shell cmd = Printf.sprintf "(& %s) -split '\\r?\\n' | ForEach-Object { Invoke-Expression $_ }" cmd | SH_fish -> Printf.sprintf "eval (%s)" cmd + | SH_nu -> + Printf.sprintf "load-env (%s | from-json)" cmd | SH_csh -> Printf.sprintf "eval `%s`" cmd | SH_cmd -> @@ -1013,7 +1015,7 @@ let export_in_shell ~unconditional shell = (make_comment comment) k v in let nu (k,v,comment) = - Printf.sprintf "load-env (opam env %s %s %s)\n" (make_comment comment) k v + Printf.sprintf "$env.%s = %s %s\n" k v (make_comment comment) in let pwsh (k,v,comment) = Printf.sprintf "%s$env:%s=%s\n" @@ -1040,7 +1042,7 @@ let env_hook_script shell = let abort_if_set var shell = (* Each of these incantations aborts the currently running script if [var] is - unset or empty ("null") *) + set to something non-empty *) match shell with | SH_zsh | SH_bash | SH_sh -> Printf.sprintf "test -z \"${%s:+x}\" || return\n" var @@ -1052,6 +1054,8 @@ let abort_if_set var shell = var var | SH_fish -> Printf.sprintf "test -z \"$%s\"; or return\n" var + | SH_nu -> + Printf.sprintf "if ($env.%s? | default \"\") != \"\" { return }\n" var | SH_cmd -> Printf.sprintf "if defined %s if \"%%%s%%\" neq \"\" goto :EOF\n" var var @@ -1071,7 +1075,7 @@ let source root shell f = let fname = unix_transform ~using_backslashes:true () in Printf.sprintf "test -r '%s' && source '%s' > /dev/null 2> /dev/null; or true\n" fname fname | SH_nu -> - Printf.sprintf "%s:TODO" fname + Printf.sprintf "const source_path = if ('%s' | path exists) { '%s' } else { null }\nsource $source_path\n" fname fname | SH_sh | SH_bash -> let fname = unix_transform () in Printf.sprintf "test -r '%s' && . '%s' > /dev/null 2> /dev/null || true\n" @@ -1108,7 +1112,7 @@ let if_interactive_script shell t e = | SH_fish -> Printf.sprintf "if status is-interactive\n %s%send\n" t @@ ielse e | SH_nu -> - Printf.sprintf "%sTODO%s" t @@ ielse e + Printf.sprintf "if $nu.is_interactive {\n %s%s}\n" t @@ ielse_pwsh e | SH_cmd -> Printf.sprintf "echo %%cmdcmdline%% | find /i \"%%~0\" >nul\nif errorlevel 1 (\n%s%s)\n" t @@ ielse_cmd e | SH_pwsh _ -> @@ -1155,11 +1159,13 @@ let string_of_update st shell updates = Printf.sprintf "'%s%c' + \"$env:%s\"" (OpamStd.Env.escape_powershell string) sep envu_var | SH_cmd -> Printf.sprintf "%s%c%%%s%%" string sep envu_var + | SH_nu -> Printf.sprintf "$env.%s? | default [] | prepend '%s'" envu_var string | _ -> Printf.sprintf "'%s':\"$%s\"" string envu_var) | EqColon | EqPlus -> (match shell with | SH_pwsh _ -> Printf.sprintf "\"$env:%s\" + '%c%s'" envu_var sep string | SH_cmd -> Printf.sprintf "%%%s%%%c%s" envu_var sep string + | SH_nu -> Printf.sprintf "$env.%s? | default [] | append '%s'" envu_var string | _ -> Printf.sprintf "\"$%s\":'%s'" envu_var string) in export_in_shell shell ~unconditional:(envu_op = Eq) (key, value, envu_comment) in @@ -1255,7 +1261,7 @@ let write_dynamic_init_scripts st = (variables_file shell, abort_if_set "OPAM_SWITCH_PREFIX" shell ^ string_of_update st shell updates)) - [SH_sh; SH_csh; SH_fish; SH_pwsh Powershell; SH_cmd] + [SH_sh; SH_csh; SH_fish; SH_nu; SH_pwsh Powershell; SH_cmd] with OpamSystem.Locked -> OpamConsole.warning "Global shell init scripts not installed (could not acquire lock)" From 11a157ab2da05e234ffa6bb90d377c601d02a3c2 Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Sun, 29 Mar 2026 22:32:18 -0500 Subject: [PATCH 4/7] Use json output for the env, and the appropriate system fallback for nushell config --- src/client/opamConfigCommand.ml | 12 +++--------- src/core/opamStd.ml | 10 +++++++--- src/state/opamEnv.ml | 4 +--- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/client/opamConfigCommand.ml b/src/client/opamConfigCommand.ml index 9db2fd0b813..13cd86e3811 100644 --- a/src/client/opamConfigCommand.ml +++ b/src/client/opamConfigCommand.ml @@ -131,13 +131,9 @@ let print_sexp_env output env = aux env; output ")\n" -let rec print_nu_env output env = - match env with - | [] -> () - | (k,v,_)::r -> begin - Printf.ksprintf output "\t\"%s\" : \"%s\",\n" k v; - print_nu_env output r - end +let print_nu_env output env = + let json = `O (List.map (fun (k,v,_) -> (k, `String v)) env) in + output @@ OpamJson.to_string json let rec print_fish_env output env = let set_arr_cmd ?(modf=fun x -> x) k v = @@ -211,9 +207,7 @@ let print_eval_env ~csh ~sexp ~fish ~pwsh ~cmd ~nu env = else if cmd then print_cmd_env output_normally env else if nu then begin - Printf.ksprintf never_with_cr "{\n"; print_nu_env never_with_cr env; - Printf.ksprintf never_with_cr "}" end else print_env never_with_cr env diff --git a/src/core/opamStd.ml b/src/core/opamStd.ml index 1e902f1138b..9649f7933d3 100644 --- a/src/core/opamStd.ml +++ b/src/core/opamStd.ml @@ -1081,9 +1081,13 @@ module OpamSys = struct | SH_fish -> Some (List.fold_left Filename.concat (home ".config") ["fish"; "config.fish"]) | SH_nu -> - let config_home = - try Env.get "XDG_CONFIG_HOME" - with Not_found -> home ".config" + (* nu checks XDG_CONFIG_HOME on all operating systems, + and then falls back to the system-dependent "dirs" crate *) + let config_home = try Env.get "XDG_CONFIG_HOME" + with Not_found -> match os () with + | Darwin -> home "Library/Application Support" + | Win32 -> (try Env.get "APPDATA" with Not_found -> home ".config") + | _ -> home ".config" in Some (List.fold_left Filename.concat config_home ["nushell"; "autoload"; "opam.nu"]) | SH_zsh -> diff --git a/src/state/opamEnv.ml b/src/state/opamEnv.ml index ec8ee215b40..9f5f24d9637 100644 --- a/src/state/opamEnv.ml +++ b/src/state/opamEnv.ml @@ -879,9 +879,7 @@ let opam_env_invocation ?root ?switch ?(set_opamswitch=false) shell = let quoted = match shell with | SH_cmd | SH_pwsh _ -> Printf.sprintf " \"--%s=%s\"" argname - | SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish -> - Printf.sprintf " '--%s=%s'" argname - | SH_nu -> + | SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish | SH_nu -> Printf.sprintf " '--%s=%s'" argname in if filepath_needs_quote pathval then From dd885defd2626d4a0ddc612539b1ebcf87086362 Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Sun, 29 Mar 2026 22:33:25 -0500 Subject: [PATCH 5/7] Remove redundant else null --- src/state/opamEnv.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state/opamEnv.ml b/src/state/opamEnv.ml index 9f5f24d9637..d1a45ff9786 100644 --- a/src/state/opamEnv.ml +++ b/src/state/opamEnv.ml @@ -1073,7 +1073,7 @@ let source root shell f = let fname = unix_transform ~using_backslashes:true () in Printf.sprintf "test -r '%s' && source '%s' > /dev/null 2> /dev/null; or true\n" fname fname | SH_nu -> - Printf.sprintf "const source_path = if ('%s' | path exists) { '%s' } else { null }\nsource $source_path\n" fname fname + Printf.sprintf "const source_path = if ('%s' | path exists) { '%s' } \nsource $source_path\n" fname fname | SH_sh | SH_bash -> let fname = unix_transform () in Printf.sprintf "test -r '%s' && . '%s' > /dev/null 2> /dev/null || true\n" From 5ba20c80a63be2364c882c042e05762d70d2d185 Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Sun, 29 Mar 2026 22:35:19 -0500 Subject: [PATCH 6/7] Fix is-interactive variable --- src/state/opamEnv.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/state/opamEnv.ml b/src/state/opamEnv.ml index d1a45ff9786..1f7dceb6393 100644 --- a/src/state/opamEnv.ml +++ b/src/state/opamEnv.ml @@ -1100,6 +1100,7 @@ let if_interactive_script shell t e = | None -> "" | Some e -> Printf.sprintf "} else {\n %s" e in + let ielse_nu = ielse_pwsh in match shell with | SH_sh| SH_bash -> Printf.sprintf "if [ -t 0 ]; then\n %s%sfi\n" t @@ ielse e @@ -1110,7 +1111,7 @@ let if_interactive_script shell t e = | SH_fish -> Printf.sprintf "if status is-interactive\n %s%send\n" t @@ ielse e | SH_nu -> - Printf.sprintf "if $nu.is_interactive {\n %s%s}\n" t @@ ielse_pwsh e + Printf.sprintf "if $nu.is-interactive {\n %s%s}\n" t @@ ielse_nu e | SH_cmd -> Printf.sprintf "echo %%cmdcmdline%% | find /i \"%%~0\" >nul\nif errorlevel 1 (\n%s%s)\n" t @@ ielse_cmd e | SH_pwsh _ -> From 070aea565d8b5f92b9167a39219c0c6be5acc457 Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Mon, 30 Mar 2026 20:00:31 -0500 Subject: [PATCH 7/7] Support autoload in nushell --- src/state/dune | 2 +- src/state/opamEnv.ml | 12 +++++++++--- src/state/opamScript.mli | 1 + src/state/shellscripts/env_hook.nu | 5 +++++ 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 src/state/shellscripts/env_hook.nu diff --git a/src/state/dune b/src/state/dune index 3f4338b003b..2dd14a30fe6 100644 --- a/src/state/dune +++ b/src/state/dune @@ -13,7 +13,7 @@ (rule (targets opamScript.ml) - (deps ../../shell/crunch.ml (glob_files shellscripts/*.*sh)) + (deps ../../shell/crunch.ml (glob_files shellscripts/*.*sh) (glob_files shellscripts/*.nu)) (action (with-stdout-to %{targets} (run ocaml %{deps})))) ; Embedded Cygwin mechanism (done in the configure script on Windows if requested) diff --git a/src/state/opamEnv.ml b/src/state/opamEnv.ml index 1f7dceb6393..b5427602035 100644 --- a/src/state/opamEnv.ml +++ b/src/state/opamEnv.ml @@ -942,7 +942,8 @@ let env_hook_file = function | SH_zsh -> Some "env_hook.zsh" | SH_csh -> Some "env_hook.csh" | SH_fish -> Some "env_hook.fish" - | SH_pwsh _ | SH_cmd | SH_nu -> None + | SH_nu -> Some "env_hook.nu" + | SH_pwsh _ | SH_cmd -> None let variables_file = function | SH_sh | SH_bash | SH_zsh -> "variables.sh" @@ -972,7 +973,8 @@ let env_hook_script_base = function | SH_zsh -> Some OpamScript.env_hook_zsh | SH_csh -> Some OpamScript.env_hook_csh | SH_fish -> Some OpamScript.env_hook_fish - | SH_pwsh _ | SH_cmd | SH_nu -> None + | SH_nu -> Some OpamScript.env_hook_nu + | SH_pwsh _ | SH_cmd -> None let export_in_shell ~unconditional shell = let make_comment comment_opt = @@ -1034,7 +1036,11 @@ let export_in_shell ~unconditional shell = let env_hook_script shell = Option.map (fun script -> - export_in_shell shell ~unconditional:true ("OPAMNOENVNOTICE", "true", None) + let var = match shell with + | SH_nu -> "'true'" + | _ -> "true" + in + export_in_shell shell ~unconditional:true ("OPAMNOENVNOTICE", var, None) ^ script) (env_hook_script_base shell) diff --git a/src/state/opamScript.mli b/src/state/opamScript.mli index 8bfe74d0e3e..78f99c5eb5e 100644 --- a/src/state/opamScript.mli +++ b/src/state/opamScript.mli @@ -20,3 +20,4 @@ val env_hook : string val env_hook_zsh : string val env_hook_csh : string val env_hook_fish : string +val env_hook_nu: string diff --git a/src/state/shellscripts/env_hook.nu b/src/state/shellscripts/env_hook.nu new file mode 100644 index 00000000000..413a2b1db7b --- /dev/null +++ b/src/state/shellscripts/env_hook.nu @@ -0,0 +1,5 @@ +$env.config = ($env.config | upsert hooks.pre_prompt { + default [] | append { + load-env (opam env --shell=nu | from json) + } +})