Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions master_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
1 change: 1 addition & 0 deletions src/client/opamArg.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/client/opamClient.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions src/client/opamCommands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand All @@ -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 ->
Expand Down Expand Up @@ -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
Expand Down
13 changes: 10 additions & 3 deletions src/client/opamConfigCommand.ml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ let print_sexp_env output env =
aux env;
output ")\n"

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 =
let v = modf @@ OpamStd.String.split v ':' in
Expand Down Expand Up @@ -183,7 +187,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 =
Expand All @@ -202,6 +206,9 @@ 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
print_nu_env never_with_cr env;
end
else
print_env never_with_cr env

Expand Down Expand Up @@ -331,7 +338,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
Expand Down Expand Up @@ -371,7 +378,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 =
Expand Down
4 changes: 2 additions & 2 deletions src/client/opamConfigCommand.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
14 changes: 13 additions & 1 deletion src/core/opamStd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -936,14 +936,15 @@ 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 =
[SH_sh; SH_bash;
SH_zsh;
SH_csh;
SH_fish;
SH_nu;
SH_pwsh Powershell_pwsh;
SH_pwsh Powershell;
SH_cmd]
Expand All @@ -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
Expand Down Expand Up @@ -1078,6 +1080,16 @@ module OpamSys = struct
match shell with
| SH_fish ->
Some (List.fold_left Filename.concat (home ".config") ["fish"; "config.fish"])
| SH_nu ->
(* 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 ->
let zsh_home f =
try Filename.concat (Env.get "ZDOTDIR") f
Expand Down
2 changes: 1 addition & 1 deletion src/core/opamStd.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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 *)
Expand Down
2 changes: 1 addition & 1 deletion src/format/opamTypes.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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} *)
Expand Down
1 change: 1 addition & 0 deletions src/format/opamTypesBase.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/state/dune
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
37 changes: 30 additions & 7 deletions src/state/opamEnv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand Down Expand Up @@ -877,7 +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 ->
| SH_sh | SH_bash | SH_zsh | SH_csh | SH_fish | SH_nu ->
Printf.sprintf " '--%s=%s'" argname
in
if filepath_needs_quote pathval then
Expand Down Expand Up @@ -928,24 +930,26 @@ 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_nu -> Some "env_hook.nu"
| SH_pwsh _ | SH_cmd -> 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"

Expand All @@ -954,20 +958,22 @@ 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
| SH_sh | SH_bash -> Some OpamScript.env_hook
| SH_zsh -> Some OpamScript.env_hook_zsh
| SH_csh -> Some OpamScript.env_hook_csh
| SH_fish -> Some OpamScript.env_hook_fish
| SH_nu -> Some OpamScript.env_hook_nu
| SH_pwsh _ | SH_cmd -> None

let export_in_shell ~unconditional shell =
Expand Down Expand Up @@ -1008,6 +1014,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 "$env.%s = %s %s\n" k v (make_comment comment)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there seems to be a problem here. The function returns a function that takes k * v * comment in which v is expected to be quoted (or not) with the general rules from posix shell.

However nushell seems to require that parameter to be quoted. For example when this function is called by env_hook_script, true isn't quoted as will simply make nushell error out.

I think the function needs to be changed in a way where the quoting is done inside export in shell instead of by the callers of the function (string_of_update and env_hook_script). This might require some kind of intermediate datatype though i'm not sure yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

funny thing:

$env.TEST = true

would actually work, but instead of setting the environment this is actually a boolean test (like in OCaml)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused here, because it seems to be acting like an assignment for me:

❯ $env.TEST = true
❯ $env.TEST
true
❯ $env.TEST == true
true

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but as far as i understand it becomes some sort of internal variable. If you do env | grep TEST afterwards the variable doesn't appear (but it does if the argument is quoted)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh, it shows up for me. Starting from a fresh shell:

❯ $env.TEST = true
❯ env | grep TEST
TEST=true

This is on nushell 0.111.0 on linux. Could it be a platform difference?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm testing using 0.99.1/linux

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regardless of the special case of true, what does $env.TEST = blah give you? With my version it's a parse error

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me too. Along with a little help message:

  help: the parsing of assignments was changed in
        0.97.0, and this would have previously been
        treated as a string. Alternatively, quote the
        string with single or double quotes to avoid it
        being interpreted as a command name. This
        restriction may be removed in a future release.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a go at moving the quoting into export_in_shell, but I had some trouble because string_of_update generates shell code that's tricky to quote. Instead, I just added the quoting to env_hook_script

in
let pwsh (k,v,comment) =
Printf.sprintf "%s$env:%s=%s\n"
(make_comment comment) k v in
Expand All @@ -1020,19 +1029,24 @@ 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

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)

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
Expand All @@ -1044,6 +1058,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
Comment thread
kit-ty-kate marked this conversation as resolved.
| SH_cmd ->
Printf.sprintf "if defined %s if \"%%%s%%\" neq \"\" goto :EOF\n" var var

Expand All @@ -1062,6 +1078,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 "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"
Expand All @@ -1088,6 +1106,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
Expand All @@ -1097,6 +1116,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 "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 _ ->
Expand Down Expand Up @@ -1143,11 +1164,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
Comment thread
kit-ty-kate marked this conversation as resolved.
| _ -> 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
Comment thread
kit-ty-kate marked this conversation as resolved.
| _ -> Printf.sprintf "\"$%s\":'%s'" envu_var string)
in
export_in_shell shell ~unconditional:(envu_op = Eq) (key, value, envu_comment) in
Expand Down Expand Up @@ -1243,7 +1266,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)"
Expand Down
1 change: 1 addition & 0 deletions src/state/opamScript.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 5 additions & 0 deletions src/state/shellscripts/env_hook.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
$env.config = ($env.config | upsert hooks.pre_prompt {
default [] | append {
load-env (opam env --shell=nu | from json)
}
})