Cargo.lock 🔗
@@ -6403,6 +6403,7 @@ dependencies = [
"pet",
"pet-conda",
"pet-core",
+ "pet-fs",
"pet-poetry",
"pet-reporter",
"project",
Stanislav Alekseev created
Reimplements `pet::EnvironmentApi`, trying to access the `project_env`
first
Closes #20177
Release Notes:
- Fixed python toolchain detection when worktree local path is set
Cargo.lock | 1
Cargo.toml | 1
crates/language/src/toolchain.rs | 7 +
crates/languages/Cargo.toml | 1
crates/languages/src/python.rs | 83 +++++++++++++++++++++
crates/project/src/project.rs | 21 ++++-
crates/project/src/toolchain_store.rs | 21 ++++
crates/remote_server/src/headless_project.rs | 10 ++
8 files changed, 132 insertions(+), 13 deletions(-)
@@ -6403,6 +6403,7 @@ dependencies = [
"pet",
"pet-conda",
"pet-core",
+ "pet-fs",
"pet-poetry",
"pet-reporter",
"project",
@@ -381,6 +381,7 @@ palette = { version = "0.7.5", default-features = false, features = ["std"] }
parking_lot = "0.12.1"
pathdiff = "0.2"
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
+pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
@@ -7,6 +7,7 @@
use std::{path::PathBuf, sync::Arc};
use async_trait::async_trait;
+use collections::HashMap;
use gpui::{AsyncAppContext, SharedString};
use settings::WorktreeId;
@@ -23,7 +24,11 @@ pub struct Toolchain {
#[async_trait(?Send)]
pub trait ToolchainLister: Send + Sync {
- async fn list(&self, _: PathBuf) -> ToolchainList;
+ async fn list(
+ &self,
+ worktree_root: PathBuf,
+ project_env: Option<HashMap<String, String>>,
+ ) -> ToolchainList;
}
#[async_trait(?Send)]
@@ -47,6 +47,7 @@ lsp.workspace = true
node_runtime.workspace = true
paths.workspace = true
pet.workspace = true
+pet-fs.workspace = true
pet-core.workspace = true
pet-conda.workspace = true
pet-poetry.workspace = true
@@ -11,11 +11,13 @@ use language::ToolchainLister;
use language::{ContextProvider, LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
+use pet_core::os_environment::Environment;
use pet_core::python_environment::PythonEnvironmentKind;
use pet_core::Configuration;
use project::lsp_store::language_server_settings;
use serde_json::Value;
+use std::sync::Mutex;
use std::{
any::Any,
borrow::Cow,
@@ -380,8 +382,13 @@ fn env_priority(kind: Option<PythonEnvironmentKind>) -> usize {
#[async_trait(?Send)]
impl ToolchainLister for PythonToolchainProvider {
- async fn list(&self, worktree_root: PathBuf) -> ToolchainList {
- let environment = pet_core::os_environment::EnvironmentApi::new();
+ async fn list(
+ &self,
+ worktree_root: PathBuf,
+ project_env: Option<HashMap<String, String>>,
+ ) -> ToolchainList {
+ let env = project_env.unwrap_or_default();
+ let environment = EnvironmentApi::from_env(&env);
let locators = pet::locators::create_locators(
Arc::new(pet_conda::Conda::from(&environment)),
Arc::new(pet_poetry::Poetry::from(&environment)),
@@ -427,6 +434,78 @@ impl ToolchainLister for PythonToolchainProvider {
}
}
+pub struct EnvironmentApi<'a> {
+ global_search_locations: Arc<Mutex<Vec<PathBuf>>>,
+ project_env: &'a HashMap<String, String>,
+ pet_env: pet_core::os_environment::EnvironmentApi,
+}
+
+impl<'a> EnvironmentApi<'a> {
+ pub fn from_env(project_env: &'a HashMap<String, String>) -> Self {
+ let paths = project_env
+ .get("PATH")
+ .map(|p| std::env::split_paths(p).collect())
+ .unwrap_or_default();
+
+ EnvironmentApi {
+ global_search_locations: Arc::new(Mutex::new(paths)),
+ project_env,
+ pet_env: pet_core::os_environment::EnvironmentApi::new(),
+ }
+ }
+
+ fn user_home(&self) -> Option<PathBuf> {
+ self.project_env
+ .get("HOME")
+ .or_else(|| self.project_env.get("USERPROFILE"))
+ .map(|home| pet_fs::path::norm_case(PathBuf::from(home)))
+ .or_else(|| self.pet_env.get_user_home())
+ }
+}
+
+impl<'a> pet_core::os_environment::Environment for EnvironmentApi<'a> {
+ fn get_user_home(&self) -> Option<PathBuf> {
+ self.user_home()
+ }
+
+ fn get_root(&self) -> Option<PathBuf> {
+ None
+ }
+
+ fn get_env_var(&self, key: String) -> Option<String> {
+ self.project_env
+ .get(&key)
+ .cloned()
+ .or_else(|| self.pet_env.get_env_var(key))
+ }
+
+ fn get_know_global_search_locations(&self) -> Vec<PathBuf> {
+ if self.global_search_locations.lock().unwrap().is_empty() {
+ let mut paths =
+ std::env::split_paths(&self.get_env_var("PATH".to_string()).unwrap_or_default())
+ .collect::<Vec<PathBuf>>();
+
+ log::trace!("Env PATH: {:?}", paths);
+ for p in self.pet_env.get_know_global_search_locations() {
+ if !paths.contains(&p) {
+ paths.push(p);
+ }
+ }
+
+ let mut paths = paths
+ .into_iter()
+ .filter(|p| p.exists())
+ .collect::<Vec<PathBuf>>();
+
+ self.global_search_locations
+ .lock()
+ .unwrap()
+ .append(&mut paths);
+ }
+ self.global_search_locations.lock().unwrap().clone()
+ }
+}
+
#[cfg(test)]
mod tests {
use gpui::{BorrowAppContext, Context, ModelContext, TestAppContext};
@@ -639,7 +639,12 @@ impl Project {
cx.subscribe(&settings_observer, Self::on_settings_observer_event)
.detach();
let toolchain_store = cx.new_model(|cx| {
- ToolchainStore::local(languages.clone(), worktree_store.clone(), cx)
+ ToolchainStore::local(
+ languages.clone(),
+ worktree_store.clone(),
+ environment.clone(),
+ cx,
+ )
});
let lsp_store = cx.new_model(|cx| {
LspStore::new_local(
@@ -2369,10 +2374,16 @@ impl Project {
language_name: LanguageName,
cx: &AppContext,
) -> Task<Option<ToolchainList>> {
- if let Some(toolchain_store) = self.toolchain_store.as_ref() {
- toolchain_store
- .read(cx)
- .list_toolchains(worktree_id, language_name, cx)
+ if let Some(toolchain_store) = self.toolchain_store.clone() {
+ cx.spawn(|cx| async move {
+ cx.update(|cx| {
+ toolchain_store
+ .read(cx)
+ .list_toolchains(worktree_id, language_name, cx)
+ })
+ .unwrap_or(Task::Ready(None))
+ .await
+ })
} else {
Task::ready(None)
}
@@ -13,7 +13,7 @@ use rpc::{proto, AnyProtoClient, TypedEnvelope};
use settings::WorktreeId;
use util::ResultExt as _;
-use crate::worktree_store::WorktreeStore;
+use crate::{worktree_store::WorktreeStore, ProjectEnvironment};
pub struct ToolchainStore(ToolchainStoreInner);
enum ToolchainStoreInner {
@@ -32,11 +32,13 @@ impl ToolchainStore {
pub fn local(
languages: Arc<LanguageRegistry>,
worktree_store: Model<WorktreeStore>,
+ project_environment: Model<ProjectEnvironment>,
cx: &mut ModelContext<Self>,
) -> Self {
let model = cx.new_model(|_| LocalToolchainStore {
languages,
worktree_store,
+ project_environment,
active_toolchains: Default::default(),
});
let subscription = cx.subscribe(&model, |_, _, e: &ToolchainStoreEvent, cx| {
@@ -203,6 +205,7 @@ impl ToolchainStore {
struct LocalToolchainStore {
languages: Arc<LanguageRegistry>,
worktree_store: Model<WorktreeStore>,
+ project_environment: Model<ProjectEnvironment>,
active_toolchains: BTreeMap<(WorktreeId, LanguageName), Toolchain>,
}
@@ -296,9 +299,20 @@ impl LocalToolchainStore {
else {
return Task::ready(None);
};
- cx.spawn(|_| async move {
+
+ let environment = self.project_environment.clone();
+ cx.spawn(|mut cx| async move {
+ let project_env = environment
+ .update(&mut cx, |environment, cx| {
+ environment.get_environment(Some(worktree_id), Some(root.clone()), cx)
+ })
+ .ok()?
+ .await;
let language = registry.language_for_name(&language_name.0).await.ok()?;
- let toolchains = language.toolchain_lister()?.list(root.to_path_buf()).await;
+ let toolchains = language
+ .toolchain_lister()?
+ .list(root.to_path_buf(), project_env)
+ .await;
Some(toolchains)
})
}
@@ -345,6 +359,7 @@ impl RemoteToolchainStore {
Some(())
})
}
+
pub(crate) fn list_toolchains(
&self,
worktree_id: WorktreeId,
@@ -108,8 +108,14 @@ impl HeadlessProject {
observer.shared(SSH_PROJECT_ID, session.clone().into(), cx);
observer
});
- let toolchain_store =
- cx.new_model(|cx| ToolchainStore::local(languages.clone(), worktree_store.clone(), cx));
+ let toolchain_store = cx.new_model(|cx| {
+ ToolchainStore::local(
+ languages.clone(),
+ worktree_store.clone(),
+ environment.clone(),
+ cx,
+ )
+ });
let lsp_store = cx.new_model(|cx| {
let mut lsp_store = LspStore::new_local(
buffer_store.clone(),