diff --git a/Cargo.lock b/Cargo.lock index c78d59a479636c40758393356de6c6c089dc1aef..4f4217ef1528ffb2fa7a1a16f589e5aacda3668a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9032,6 +9032,7 @@ dependencies = [ "settings", "smol", "task", + "terminal", "text", "theme", "toml 0.8.23", diff --git a/assets/settings/default.json b/assets/settings/default.json index ac570edccdd42bcc51932e0de49e23717f52e3c8..4b7e8b1533001a550acb658b076eacf45aabe2f0 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -1487,7 +1487,11 @@ // in your project's settings, rather than globally. "directories": [".env", "env", ".venv", "venv"], // Can also be `csh`, `fish`, `nushell` and `power_shell` - "activate_script": "default" + "activate_script": "default", + // Preferred Conda manager to use when activating Conda environments. + // Values: "auto", "conda", "mamba", "micromamba" + // Default: "auto" + "conda_manager": "auto" } }, "toolbar": { diff --git a/crates/language/src/toolchain.rs b/crates/language/src/toolchain.rs index 2896d4827c5e16047a471138122ef0256a24480e..5717ffb5143e38bce736c354b43febc86e321f32 100644 --- a/crates/language/src/toolchain.rs +++ b/crates/language/src/toolchain.rs @@ -9,7 +9,7 @@ use std::{path::PathBuf, sync::Arc}; use async_trait::async_trait; use collections::HashMap; use fs::Fs; -use gpui::{AsyncApp, SharedString}; +use gpui::{App, AsyncApp, SharedString}; use settings::WorktreeId; use task::ShellKind; use util::rel_path::RelPath; @@ -110,7 +110,7 @@ pub trait ToolchainLister: Send + Sync + 'static { fs: &dyn Fs, ) -> anyhow::Result; - fn activation_script(&self, toolchain: &Toolchain, shell: ShellKind) -> Vec; + fn activation_script(&self, toolchain: &Toolchain, shell: ShellKind, cx: &App) -> Vec; /// Returns various "static" bits of information about this toolchain lister. This function should be pure. fn meta(&self) -> ToolchainMetadata; diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index 2f123bb70fc3977784f5137a68fb63c09fb302c7..e78f29a8d6ef49726471fa186a2e9dee095fa0d5 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -69,6 +69,7 @@ settings.workspace = true smol.workspace = true url.workspace = true task.workspace = true +terminal.workspace = true theme.workspace = true toml.workspace = true tree-sitter = { workspace = true, optional = true } diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index f3f571196a2341a47b5e2ddab9f51c83c1ee528e..1ec4618284961928c9e290f77c1ebeeffb9ee70c 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -21,9 +21,11 @@ use project::Fs; use project::lsp_store::language_server_settings; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; +use settings::Settings; use smol::lock::OnceCell; use std::cmp::Ordering; use std::env::consts; +use terminal::terminal_settings::TerminalSettings; use util::command::new_smol_command; use util::fs::{make_file_executable, remove_matching}; use util::rel_path::RelPath; @@ -1171,7 +1173,7 @@ impl ToolchainLister for PythonToolchainProvider { .context("Could not convert a venv into a toolchain") } - fn activation_script(&self, toolchain: &Toolchain, shell: ShellKind) -> Vec { + fn activation_script(&self, toolchain: &Toolchain, shell: ShellKind, cx: &App) -> Vec { let Ok(toolchain) = serde_json::from_value::(toolchain.as_json.clone()) else { @@ -1184,10 +1186,34 @@ impl ToolchainLister for PythonToolchainProvider { match toolchain.environment.kind { Some(PythonEnvironmentKind::Conda) => { + let settings = TerminalSettings::get_global(cx); + let conda_manager = settings + .detect_venv + .as_option() + .map(|venv| venv.conda_manager) + .unwrap_or(settings::CondaManager::Auto); + + let manager = match conda_manager { + settings::CondaManager::Conda => "conda", + settings::CondaManager::Mamba => "mamba", + settings::CondaManager::Micromamba => "micromamba", + settings::CondaManager::Auto => { + // When auto, prefer the detected manager or fall back to conda + toolchain + .environment + .manager + .as_ref() + .and_then(|m| m.executable.file_name()) + .and_then(|name| name.to_str()) + .filter(|name| matches!(*name, "conda" | "mamba" | "micromamba")) + .unwrap_or("conda") + } + }; + if let Some(name) = &toolchain.environment.name { - activation_script.push(format!("conda activate {name}")); + activation_script.push(format!("{manager} activate {name}")); } else { - activation_script.push("conda activate".to_string()); + activation_script.push(format!("{manager} activate base")); } } Some(PythonEnvironmentKind::Venv | PythonEnvironmentKind::VirtualEnv) => { diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index c7e53c652c97224cce4771afb3e90667d5efaa8a..c5062a850b5b98943d9c7b47e37055a46b21e03c 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -10185,7 +10185,7 @@ fn python_lang(fs: Arc) -> Arc { manifest_name: ManifestName::from(SharedString::new_static("pyproject.toml")), } } - fn activation_script(&self, _: &Toolchain, _: ShellKind) -> Vec { + fn activation_script(&self, _: &Toolchain, _: ShellKind, _: &gpui::App) -> Vec { vec![] } } diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index eaa897b53c09d427d53a0df6b01eb079ee147cd0..769e75de3e8a001bf140708dd7443a48c55ed280 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -127,8 +127,10 @@ impl Project { .language_for_name(&toolchain.language_name.0) .await .ok(); - let lister = language?.toolchain_lister(); - return Some(lister?.activation_script(&toolchain, shell_kind)); + let lister = language?.toolchain_lister()?; + return cx + .update(|cx| lister.activation_script(&toolchain, shell_kind, cx)) + .ok(); } None }) @@ -317,7 +319,8 @@ impl Project { .unwrap_or_else(get_default_system_shell), None => settings.shell.program(), }; - let shell_kind = ShellKind::new(&shell, self.path_style(cx).is_windows()); + + let is_windows = self.path_style(cx).is_windows(); // Prepare a task for resolving the environment let env_task = @@ -325,6 +328,7 @@ impl Project { let lang_registry = self.languages.clone(); cx.spawn(async move |project, cx| { + let shell_kind = ShellKind::new(&shell, is_windows); let mut env = env_task.await.unwrap_or_default(); env.extend(settings.env); @@ -337,13 +341,16 @@ impl Project { .language_for_name(&toolchain.language_name.0) .await .ok(); - let lister = language?.toolchain_lister(); - return Some(lister?.activation_script(&toolchain, shell_kind)); + let lister = language?.toolchain_lister()?; + return cx + .update(|cx| lister.activation_script(&toolchain, shell_kind, cx)) + .ok(); } None }) .await .unwrap_or_default(); + let builder = project .update(cx, move |_, cx| { let (shell, env) = { diff --git a/crates/settings/src/settings_content/terminal.rs b/crates/settings/src/settings_content/terminal.rs index d6e82b40b439ba2308a3c9be594e5d895e9c1dad..2413eb96a0d01feb96c8b7322131f1c5f52af91b 100644 --- a/crates/settings/src/settings_content/terminal.rs +++ b/crates/settings/src/settings_content/terminal.rs @@ -348,6 +348,22 @@ pub struct TerminalToolbarContent { pub breadcrumbs: Option, } +#[derive( + Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom, +)] +#[serde(rename_all = "snake_case")] +pub enum CondaManager { + /// Automatically detect the conda manager + #[default] + Auto, + /// Use conda + Conda, + /// Use mamba + Mamba, + /// Use micromamba + Micromamba, +} + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)] #[serde(rename_all = "snake_case")] pub enum VenvSettings { @@ -360,6 +376,10 @@ pub enum VenvSettings { activate_script: Option, venv_name: Option, directories: Option>, + /// Preferred Conda manager to use when activating Conda environments. + /// + /// Default: auto + conda_manager: Option, }, } #[skip_serializing_none] @@ -367,6 +387,7 @@ pub struct VenvSettingsContent<'a> { pub activate_script: ActivateScript, pub venv_name: &'a str, pub directories: &'a [PathBuf], + pub conda_manager: CondaManager, } impl VenvSettings { @@ -377,10 +398,12 @@ impl VenvSettings { activate_script, venv_name, directories, + conda_manager, } => Some(VenvSettingsContent { activate_script: activate_script.unwrap_or(ActivateScript::Default), venv_name: venv_name.as_deref().unwrap_or(""), directories: directories.as_deref().unwrap_or(&[]), + conda_manager: conda_manager.unwrap_or(CondaManager::Auto), }), } }