Fix toolchain detection for worktree-local paths (#20229)
Reimplements `pet::EnvironmentApi`, trying to access the `project_env` first Closes #20177 Release Notes: - Fixed python toolchain detection when worktree local path is set
This commit is contained in:
parent
f8bd6c66f4
commit
a26c0a8537
8 changed files with 132 additions and 13 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue