python: Show environment name if available (#26741)

Elvis Pranskevichus created

Right now the toolchain popup is a nondescript list of duplicate entries
like `Python 3.10.15 (VirtualEnvWrapper)` and one has to look at the
interpreter path to distinguish one virtualenv from another.

Fix this by including the env name as reported by pet, so the entries
looks like `Python 3.10.15 (myproject; VirtualEnvWrapper)`.

Release Notes:

- Python: Improved display of environments in toolchain selector

Change summary

crates/languages/src/python.rs | 47 +++++++++++++++++++++++++++++++----
1 file changed, 41 insertions(+), 6 deletions(-)

Detailed changes

crates/languages/src/python.rs 🔗

@@ -29,6 +29,7 @@ use std::{
     any::Any,
     borrow::Cow,
     ffi::OsString,
+    fmt::Write,
     path::{Path, PathBuf},
     sync::Arc,
 };
@@ -588,6 +589,28 @@ fn python_module_name_from_relative_path(relative_path: &str) -> String {
         .to_string()
 }
 
+fn python_env_kind_display(k: &PythonEnvironmentKind) -> &'static str {
+    match k {
+        PythonEnvironmentKind::Conda => "Conda",
+        PythonEnvironmentKind::Pixi => "pixi",
+        PythonEnvironmentKind::Homebrew => "Homebrew",
+        PythonEnvironmentKind::Pyenv => "global (Pyenv)",
+        PythonEnvironmentKind::GlobalPaths => "global",
+        PythonEnvironmentKind::PyenvVirtualEnv => "Pyenv",
+        PythonEnvironmentKind::Pipenv => "Pipenv",
+        PythonEnvironmentKind::Poetry => "Poetry",
+        PythonEnvironmentKind::MacPythonOrg => "global (Python.org)",
+        PythonEnvironmentKind::MacCommandLineTools => "global (Command Line Tools for Xcode)",
+        PythonEnvironmentKind::LinuxGlobal => "global",
+        PythonEnvironmentKind::MacXCode => "global (Xcode)",
+        PythonEnvironmentKind::Venv => "venv",
+        PythonEnvironmentKind::VirtualEnv => "virtualenv",
+        PythonEnvironmentKind::VirtualEnvWrapper => "virtualenvwrapper",
+        PythonEnvironmentKind::WindowsStore => "global (Windows Store)",
+        PythonEnvironmentKind::WindowsRegistry => "global (Windows Registry)",
+    }
+}
+
 pub(crate) struct PythonToolchainProvider {
     term: SharedString,
 }
@@ -683,14 +706,26 @@ impl ToolchainLister for PythonToolchainProvider {
         let mut toolchains: Vec<_> = toolchains
             .into_iter()
             .filter_map(|toolchain| {
-                let name = if let Some(version) = &toolchain.version {
-                    format!("Python {version} ({:?})", toolchain.kind?)
-                } else {
-                    format!("{:?}", toolchain.kind?)
+                let mut name = String::from("Python");
+                if let Some(ref version) = toolchain.version {
+                    _ = write!(name, " {version}");
+                }
+
+                let name_and_kind = match (&toolchain.name, &toolchain.kind) {
+                    (Some(name), Some(kind)) => {
+                        Some(format!("({name}; {})", python_env_kind_display(kind)))
+                    }
+                    (Some(name), None) => Some(format!("({name})")),
+                    (None, Some(kind)) => Some(format!("({})", python_env_kind_display(kind))),
+                    (None, None) => None,
+                };
+
+                if let Some(nk) = name_and_kind {
+                    _ = write!(name, " {nk}");
                 }
-                .into();
+
                 Some(Toolchain {
-                    name,
+                    name: name.into(),
                     path: toolchain.executable.as_ref()?.to_str()?.to_owned().into(),
                     language_name: LanguageName::new("Python"),
                     as_json: serde_json::to_value(toolchain).ok()?,