Skip to content

Commit

Permalink
Add Nushell activation support (#2693)
Browse files Browse the repository at this point in the history
  • Loading branch information
cvanelteren authored Sep 14, 2023
1 parent 9b757c2 commit f4cea9b
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 6 deletions.
20 changes: 20 additions & 0 deletions libmamba/include/mamba/core/activation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,26 @@ namespace mamba
fs::u8path hook_source_path() override;
};

class NuActivator : public Activator
{
public:

explicit NuActivator(const Context& context)
: Activator(context)
{
}
virtual ~NuActivator() = default;

std::string script(const EnvironmentTransform& env_transform) override;
std::pair<std::string, std::string>
update_prompt(const std::string& conda_prompt_modifier) override;
std::string shell_extension() override;
std::string shell() override;

std::string hook_preamble() override;
std::string hook_postamble() override;
fs::u8path hook_source_path() override;
};

std::vector<fs::u8path> get_path_dirs(const fs::u8path& prefix);

Expand Down
4 changes: 4 additions & 0 deletions libmamba/src/api/shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ namespace mamba
{
return std::make_unique<mamba::FishActivator>(context);
}
if (name == "nu")
{
return std::make_unique<mamba::NuActivator>(context);
}
throw std::invalid_argument(fmt::format("Shell type not handled: {}", name));
}
}
Expand Down
69 changes: 69 additions & 0 deletions libmamba/src/core/activation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,4 +1216,73 @@ namespace mamba

return out.str();
}

std::string NuActivator::shell_extension()
{
return ".nu";
}

std::string NuActivator::shell()
{
return "nu";
}

std::string NuActivator::hook_preamble()
{
return "";
}

std::string NuActivator::hook_postamble()
{
return "";
}

fs::u8path NuActivator::hook_source_path()
{
return "";
}

std::pair<std::string, std::string>
NuActivator::update_prompt(const std::string& conda_prompt_modifier)
{
// hook is implemented in shell_init.cpp as nushell behaves like a compiled language;
// one cannot dynamically evaluate strings
return { "", "" };
}

std::string NuActivator::script(const EnvironmentTransform& env_transform)
{
std::stringstream out;

if (!env_transform.export_path.empty())
{
out << "PATH = " << env_transform.export_path << ";";
}

for (const fs::u8path& ds : env_transform.deactivate_scripts)
{
out << "source " << ds << ";";
}

for (const std::string& uvar : env_transform.unset_vars)
{
out << "hide-env " << uvar << ";";
}

for (const auto& [skey, svar] : env_transform.set_vars)
{
out << "let " << skey << " = " << svar << ";";
}

for (const auto& [ekey, evar] : env_transform.export_vars)
{
out << ekey << " = " << evar << ";";
}

for (const fs::u8path& p : env_transform.activate_scripts)
{
out << "source " << p << ";";
}
return out.str();
}
} // namespace mamba
94 changes: 93 additions & 1 deletion libmamba/src/core/shell_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ namespace mamba
{
return "dash";
}
if (util::contains(parent_process_name, "nu"))
{
return "nu";
}

// xonsh in unix, Python in macOS
if (util::contains(parent_process_name_lower, "python"))
Expand Down Expand Up @@ -382,6 +386,59 @@ namespace mamba
return content.str();
}

std::string
nu_content(const fs::u8path& env_prefix, const std::string& /*shell*/, const fs::u8path& mamba_exe)
{
std::stringstream content;
std::string s_mamba_exe;
if (util::on_win)
{
s_mamba_exe = native_path_to_unix(mamba_exe.string());
}
else
{
s_mamba_exe = mamba_exe.string();
}

content << "\n# >>> mamba initialize >>>\n";
content << "# !! Contents within this block are managed by 'mamba init' !!\n";
content << "$env.MAMBA_EXE = " << mamba_exe << "\n";
content << "$env.MAMBA_ROOT_PREFIX = " << env_prefix << "\n";
content << "$env.PATH = ($env.PATH | prepend ([$env.MAMBA_ROOT_PREFIX bin] | path join) | uniq)\n";
content << R"###(def-env "micromamba activate" [name: string] {
#add condabin when root
if $env.MAMBA_SHLVL? == null {
$env.MAMBA_SHLVL = 0
$env.PATH = ($env.PATH | prepend $"($env.MAMBA_ROOT_PREFIX)/condabin")
}
#ask mamba how to setup the environment and set the environment
(^($env.MAMBA_EXE) shell activate --shell nu $name
| split row ";"
| parse "{key} = {value}"
| transpose --header-row
| into record
| load-env
)
$env.PROMPT_COMMAND = {$env.CONDA_PROMPT_MODIFIER + (create_left_prompt)}
}
def-env "micromamba deactivate" [] {
#remove active environment except micromamba root
if $env.CONDA_PROMPT_MODIFIER? != null {
(^($env.MAMBA_EXE) shell deactivate --shell nu
| split row ";"
| str trim
| parse --regex '(.*?)(?:\s*=\s*|\s+)(.*?)'
| transpose --header-row
| into record
| load-env
)
$env.PROMPT_COMMAND = (create_left_prompt)
}})###"
<< "\n";
content << "# <<< mamba initialize <<<\n";
return content.str();
}

std::string
csh_content(const fs::u8path& env_prefix, const std::string& /*shell*/, const fs::u8path& mamba_exe)
{
Expand Down Expand Up @@ -445,6 +502,10 @@ namespace mamba
{
conda_init_content = fish_content(conda_prefix, shell, mamba_exe);
}
else if (shell == "nu")
{
conda_init_content = nu_content(conda_prefix, shell, mamba_exe);
}
else if (shell == "csh")
{
conda_init_content = csh_content(conda_prefix, shell, mamba_exe);
Expand Down Expand Up @@ -579,6 +640,11 @@ namespace mamba
util::replace_all(contents, "$MAMBA_EXE", exe.generic_string());
return contents;
}
else if (shell == "nu")
{
// not implemented due to inability in nu to evaluate dynamically generated code
return "";
}
return "";
}

Expand Down Expand Up @@ -760,6 +826,18 @@ namespace mamba
std::ofstream sh_file = open_ofstream(sh_source_path);
sh_file << data_mamba_fish;
}
else if (shell == "nu")
{
NuActivator a{ context };
auto sh_source_path = a.hook_source_path();
try
{
fs::create_directories(sh_source_path.parent_path());
}
catch (...)
{
}
}
else if (shell == "cmd.exe")
{
init_root_prefix_cmdexe(context, root_prefix);
Expand Down Expand Up @@ -1052,6 +1130,11 @@ namespace mamba
fs::u8path fishrc_path = home / ".config" / "fish" / "config.fish";
modify_rc_file(context, fishrc_path, conda_prefix, shell, mamba_exe);
}
else if (shell == "nu")
{
fs::u8path nu_config_path = home / ".config" / "nushell" / "config.nu";
modify_rc_file(context, nu_config_path, conda_prefix, shell, mamba_exe);
}
else if (shell == "cmd.exe")
{
#ifndef _WIN32
Expand Down Expand Up @@ -1121,6 +1204,11 @@ namespace mamba
fs::u8path fishrc_path = home / ".config" / "fish" / "config.fish";
reset_rc_file(context, fishrc_path, shell, mamba_exe);
}
else if (shell == "nu")
{
fs::u8path nu_config_path = home / ".config" / "nushell" / "config.nu";
reset_rc_file(context, nu_config_path, shell, mamba_exe);
}
else if (shell == "cmd.exe")
{
#ifndef _WIN32
Expand Down Expand Up @@ -1178,6 +1266,10 @@ namespace mamba
{
config_path = home / ".config" / "fish" / "config.fish";
}
else if (shell == "nu")
{
config_path = "";
}
return config_path;
}

Expand All @@ -1186,7 +1278,7 @@ namespace mamba
fs::u8path home = env::home_directory();

std::vector<std::string> result;
std::vector<std::string> supported_shells = { "bash", "zsh", "xonsh", "csh", "fish" };
std::vector<std::string> supported_shells = { "bash", "zsh", "xonsh", "csh", "fish", "nu" };
for (const std::string& shell : supported_shells)
{
fs::u8path config_path = config_path_for_shell(shell);
Expand Down
2 changes: 1 addition & 1 deletion micromamba/src/activate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ set_activate_command(CLI::App* subcom)
"Otherwise, this may be an issue. In the meantime you can run commands. See:\n"
" $ micromamba run --help\n"
"\n"
"Supported shells are {{bash, zsh, csh, xonsh, cmd.exe, powershell, fish}}.\n",
"Supported shells are {{bash, zsh, csh, xonsh, cmd.exe, powershell, fish, nu}}.\n",
get_shell_hook(guessed_shell),
guessed_shell
);
Expand Down
2 changes: 1 addition & 1 deletion micromamba/src/shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace
subcmd
->add_option("-s,--shell", shell_type.get_cli_config<std::string>(), shell_type.description())
->check(CLI::IsMember(std::set<std::string>(
{ "bash", "posix", "powershell", "cmd.exe", "xonsh", "zsh", "fish", "tcsh", "dash" }
{ "bash", "posix", "powershell", "cmd.exe", "xonsh", "zsh", "fish", "tcsh", "dash", "nu" }
)));
}

Expand Down
11 changes: 9 additions & 2 deletions micromamba/tests/test_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"xonsh": ".sh",
"fish": ".fish",
"powershell": ".ps1",
"nu": ".nu",
}


Expand Down Expand Up @@ -62,13 +63,15 @@ def __getitem__(self, shell: str) -> str:
"xonsh": "~/.xonshrc",
"tcsh": "~/.tcshrc",
"fish": "~/.config/fish/config.fish",
"nu": "~/.config/nushell/config.nu",
},
"linux": {
"zsh": "~/.zshrc",
"bash": "~/.bashrc",
"xonsh": "~/.xonshrc",
"tcsh": "~/.tcshrc",
"fish": "~/.config/fish/config.fish",
"nu": "~/.config/nushell/config.nu",
},
}

Expand Down Expand Up @@ -100,8 +103,8 @@ def write_script(interpreter, lines, path):


possible_interpreters = {
"win": {"powershell", "cmd.exe", "bash"},
"unix": {"bash", "zsh", "fish", "xonsh", "tcsh"},
"win": {"powershell", "cmd.exe", "bash", "nu"},
"unix": {"bash", "zsh", "fish", "xonsh", "tcsh", "nu"},
}


Expand Down Expand Up @@ -266,6 +269,8 @@ def shvar(v, interpreter):
return f"$Env:{v}"
elif interpreter == "cmd.exe":
return f"%{v}%"
elif interpreter == "nu":
return f"$env.{v}"


def env_to_dict(out, interpreter="bash"):
Expand Down Expand Up @@ -429,6 +434,8 @@ def test_shell_init_deinit_root_prefix_files(
files = [tmp_root_prefix / "etc" / "profile.d" / "mamba.xsh"]
elif interpreter in ["csh", "tcsh"]:
files = [tmp_root_prefix / "etc" / "profile.d" / "micromamba.csh"]
elif interpreter == "nu":
files = [] # moved to ~/.config/nushell.nu controlled by micromamba activation
else:
raise ValueError(f"Unknown shell {interpreter}")

Expand Down
6 changes: 5 additions & 1 deletion micromamba/tests/test_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def skip_if_shell_incompat(shell_type):

@pytest.mark.parametrize(
"shell_type",
["bash", "posix", "powershell", "cmd.exe", "xonsh", "zsh", "fish", "tcsh"],
["bash", "posix", "powershell", "cmd.exe", "xonsh", "zsh", "fish", "tcsh", "nu"],
)
def test_hook(tmp_home, tmp_root_prefix, shell_type):
res = helpers.shell("hook", "-s", shell_type)
Expand All @@ -52,6 +52,10 @@ def test_hook(tmp_home, tmp_root_prefix, shell_type):
assert res == ""
elif shell_type == "tcsh":
assert res.count(mamba_exe) == 3
elif shell_type == "nu":
# insert dummy test, as the nu scripts contains
# no mention of mamba_exe; path is added in config.nu
assert res.count(mamba_exe) == 0

res = helpers.shell("hook", "-s", shell_type, "--json")
expected_keys = {"success", "operation", "context", "actions"}
Expand Down

0 comments on commit f4cea9b

Please sign in to comment.