@@ -12,6 +12,7 @@ use std::{
use async_trait::async_trait;
use collections::HashMap;
use fs::Fs;
+use futures::future::BoxFuture;
use gpui::{App, AsyncApp, SharedString};
use settings::WorktreeId;
use task::ShellKind;
@@ -113,7 +114,12 @@ pub trait ToolchainLister: Send + Sync + 'static {
fs: &dyn Fs,
) -> anyhow::Result<Toolchain>;
- fn activation_script(&self, toolchain: &Toolchain, shell: ShellKind, cx: &App) -> Vec<String>;
+ fn activation_script(
+ &self,
+ toolchain: &Toolchain,
+ shell: ShellKind,
+ cx: &App,
+ ) -> BoxFuture<'static, Vec<String>>;
/// Returns various "static" bits of information about this toolchain lister. This function should be pure.
fn meta(&self) -> ToolchainMetadata;
@@ -2,6 +2,7 @@ use anyhow::{Context as _, ensure};
use anyhow::{Result, anyhow};
use async_trait::async_trait;
use collections::HashMap;
+use futures::future::BoxFuture;
use futures::lock::OwnedMutexGuard;
use futures::{AsyncBufReadExt, StreamExt as _};
use gpui::{App, AsyncApp, SharedString, Task};
@@ -24,11 +25,13 @@ use semver::Version;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use settings::Settings;
+use terminal::terminal_settings::TerminalSettings;
+
use smol::lock::OnceCell;
use std::cmp::{Ordering, Reverse};
use std::env::consts;
use std::process::Stdio;
-use terminal::terminal_settings::TerminalSettings;
+
use util::command::new_smol_command;
use util::fs::{make_file_executable, remove_matching};
use util::paths::PathStyle;
@@ -1333,94 +1336,110 @@ impl ToolchainLister for PythonToolchainProvider {
.context("Could not convert a venv into a toolchain")
}
- fn activation_script(&self, toolchain: &Toolchain, shell: ShellKind, cx: &App) -> Vec<String> {
- let Ok(toolchain) =
- serde_json::from_value::<PythonToolchainData>(toolchain.as_json.clone())
- else {
- return vec![];
- };
+ fn activation_script(
+ &self,
+ toolchain: &Toolchain,
+ shell: ShellKind,
+ cx: &App,
+ ) -> BoxFuture<'static, Vec<String>> {
+ 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 toolchain_clone = toolchain.clone();
+ Box::pin(async move {
+ let Ok(toolchain) =
+ serde_json::from_value::<PythonToolchainData>(toolchain_clone.as_json.clone())
+ else {
+ return vec![];
+ };
- log::debug!("(Python) Composing activation script for toolchain {toolchain:?}");
-
- let mut activation_script = vec![];
-
- 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 => 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"),
- };
+ log::debug!("(Python) Composing activation script for toolchain {toolchain:?}");
- // Activate micromamba shell in the child shell
- // [required for micromamba]
- if manager == "micromamba" {
- let shell = micromamba_shell_name(shell);
- activation_script
- .push(format!(r#"eval "$({manager} shell hook --shell {shell})""#));
- }
+ let mut activation_script = vec![];
- if let Some(name) = &toolchain.environment.name {
- activation_script.push(format!("{manager} activate {name}"));
- } else {
- activation_script.push(format!("{manager} activate base"));
+ match toolchain.environment.kind {
+ Some(PythonEnvironmentKind::Conda) => {
+ let Some(manager_info) = &toolchain.environment.manager else {
+ return vec![];
+ };
+ if smol::fs::metadata(&manager_info.executable).await.is_err() {
+ return vec![];
+ }
+
+ let manager = match conda_manager {
+ settings::CondaManager::Conda => "conda",
+ settings::CondaManager::Mamba => "mamba",
+ settings::CondaManager::Micromamba => "micromamba",
+ settings::CondaManager::Auto => 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"),
+ };
+
+ // Activate micromamba shell in the child shell
+ // [required for micromamba]
+ if manager == "micromamba" {
+ let shell = micromamba_shell_name(shell);
+ activation_script
+ .push(format!(r#"eval "$({manager} shell hook --shell {shell})""#));
+ }
+
+ if let Some(name) = &toolchain.environment.name {
+ activation_script.push(format!("{manager} activate {name}"));
+ } else {
+ activation_script.push(format!("{manager} activate base"));
+ }
}
- }
- Some(
- PythonEnvironmentKind::Venv
- | PythonEnvironmentKind::VirtualEnv
- | PythonEnvironmentKind::Uv
- | PythonEnvironmentKind::UvWorkspace
- | PythonEnvironmentKind::Poetry,
- ) => {
- if let Some(activation_scripts) = &toolchain.activation_scripts {
- if let Some(activate_script_path) = activation_scripts.get(&shell) {
- let activate_keyword = shell.activate_keyword();
- if let Some(quoted) =
- shell.try_quote(&activate_script_path.to_string_lossy())
- {
- activation_script.push(format!("{activate_keyword} {quoted}"));
+ Some(
+ PythonEnvironmentKind::Venv
+ | PythonEnvironmentKind::VirtualEnv
+ | PythonEnvironmentKind::Uv
+ | PythonEnvironmentKind::UvWorkspace
+ | PythonEnvironmentKind::Poetry,
+ ) => {
+ if let Some(activation_scripts) = &toolchain.activation_scripts {
+ if let Some(activate_script_path) = activation_scripts.get(&shell) {
+ let activate_keyword = shell.activate_keyword();
+ if let Some(quoted) =
+ shell.try_quote(&activate_script_path.to_string_lossy())
+ {
+ activation_script.push(format!("{activate_keyword} {quoted}"));
+ }
}
}
}
+ Some(PythonEnvironmentKind::Pyenv) => {
+ let Some(manager) = &toolchain.environment.manager else {
+ return vec![];
+ };
+ let version = toolchain.environment.version.as_deref().unwrap_or("system");
+ let pyenv = &manager.executable;
+ let pyenv = pyenv.display();
+ activation_script.extend(match shell {
+ ShellKind::Fish => Some(format!("\"{pyenv}\" shell - fish {version}")),
+ ShellKind::Posix => Some(format!("\"{pyenv}\" shell - sh {version}")),
+ ShellKind::Nushell => Some(format!("^\"{pyenv}\" shell - nu {version}")),
+ ShellKind::PowerShell | ShellKind::Pwsh => None,
+ ShellKind::Csh => None,
+ ShellKind::Tcsh => None,
+ ShellKind::Cmd => None,
+ ShellKind::Rc => None,
+ ShellKind::Xonsh => None,
+ ShellKind::Elvish => None,
+ })
+ }
+ _ => {}
}
- Some(PythonEnvironmentKind::Pyenv) => {
- let Some(manager) = &toolchain.environment.manager else {
- return vec![];
- };
- let version = toolchain.environment.version.as_deref().unwrap_or("system");
- let pyenv = &manager.executable;
- let pyenv = pyenv.display();
- activation_script.extend(match shell {
- ShellKind::Fish => Some(format!("\"{pyenv}\" shell - fish {version}")),
- ShellKind::Posix => Some(format!("\"{pyenv}\" shell - sh {version}")),
- ShellKind::Nushell => Some(format!("^\"{pyenv}\" shell - nu {version}")),
- ShellKind::PowerShell | ShellKind::Pwsh => None,
- ShellKind::Csh => None,
- ShellKind::Tcsh => None,
- ShellKind::Cmd => None,
- ShellKind::Rc => None,
- ShellKind::Xonsh => None,
- ShellKind::Elvish => None,
- })
- }
- _ => {}
- }
- activation_script
+ activation_script
+ })
}
}
@@ -11110,8 +11110,13 @@ fn python_lang(fs: Arc<FakeFs>) -> Arc<Language> {
manifest_name: ManifestName::from(SharedString::new_static("pyproject.toml")),
}
}
- fn activation_script(&self, _: &Toolchain, _: ShellKind, _: &gpui::App) -> Vec<String> {
- vec![]
+ fn activation_script(
+ &self,
+ _: &Toolchain,
+ _: ShellKind,
+ _: &gpui::App,
+ ) -> futures::future::BoxFuture<'static, Vec<String>> {
+ Box::pin(async { vec![] })
}
}
Arc::new(
@@ -129,9 +129,9 @@ impl Project {
.await
.ok();
let lister = language?.toolchain_lister()?;
- return Some(
- cx.update(|cx| lister.activation_script(&toolchain, shell_kind, cx)),
- );
+ let future =
+ cx.update(|cx| lister.activation_script(&toolchain, shell_kind, cx));
+ return Some(future.await);
}
None
})
@@ -379,9 +379,9 @@ impl Project {
.await
.ok();
let lister = language?.toolchain_lister()?;
- return Some(
- cx.update(|cx| lister.activation_script(&toolchain, shell_kind, cx)),
- );
+ let future =
+ cx.update(|cx| lister.activation_script(&toolchain, shell_kind, cx));
+ return Some(future.await);
}
None
})