python: Re-land usage of source file path in toolchain picker (#31893)
This reverts commit 1e55e88c18
.
Closes #ISSUE
Release Notes:
- Python toolchain selector now uses path to the closest pyproject.toml
as a basis for picking a toolchain. All files under the same
pyproject.toml (in filesystem hierarchy) will share a single virtual
environment. It is possible to have multiple Python virtual environments
selected for disjoint parts of the same project.
This commit is contained in:
parent
2ebe16a52f
commit
9dd18e5ee1
14 changed files with 195 additions and 92 deletions
|
@ -34,7 +34,7 @@ pub use highlight_map::HighlightMap;
|
||||||
use http_client::HttpClient;
|
use http_client::HttpClient;
|
||||||
pub use language_registry::{LanguageName, LoadedLanguage};
|
pub use language_registry::{LanguageName, LoadedLanguage};
|
||||||
use lsp::{CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions};
|
use lsp::{CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions};
|
||||||
pub use manifest::{ManifestName, ManifestProvider, ManifestQuery};
|
pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use schemars::{
|
use schemars::{
|
||||||
|
@ -323,7 +323,6 @@ pub trait LspAdapterDelegate: Send + Sync {
|
||||||
fn http_client(&self) -> Arc<dyn HttpClient>;
|
fn http_client(&self) -> Arc<dyn HttpClient>;
|
||||||
fn worktree_id(&self) -> WorktreeId;
|
fn worktree_id(&self) -> WorktreeId;
|
||||||
fn worktree_root_path(&self) -> &Path;
|
fn worktree_root_path(&self) -> &Path;
|
||||||
fn exists(&self, path: &Path, is_dir: Option<bool>) -> bool;
|
|
||||||
fn update_status(&self, language: LanguageServerName, status: BinaryStatus);
|
fn update_status(&self, language: LanguageServerName, status: BinaryStatus);
|
||||||
fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>>;
|
fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>>;
|
||||||
async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>;
|
async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use std::{borrow::Borrow, path::Path, sync::Arc};
|
use std::{borrow::Borrow, path::Path, sync::Arc};
|
||||||
|
|
||||||
use gpui::SharedString;
|
use gpui::SharedString;
|
||||||
|
use settings::WorktreeId;
|
||||||
use crate::LspAdapterDelegate;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct ManifestName(SharedString);
|
pub struct ManifestName(SharedString);
|
||||||
|
@ -39,10 +38,15 @@ pub struct ManifestQuery {
|
||||||
/// Path to the file, relative to worktree root.
|
/// Path to the file, relative to worktree root.
|
||||||
pub path: Arc<Path>,
|
pub path: Arc<Path>,
|
||||||
pub depth: usize,
|
pub depth: usize,
|
||||||
pub delegate: Arc<dyn LspAdapterDelegate>,
|
pub delegate: Arc<dyn ManifestDelegate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ManifestProvider {
|
pub trait ManifestProvider {
|
||||||
fn name(&self) -> ManifestName;
|
fn name(&self) -> ManifestName;
|
||||||
fn search(&self, query: ManifestQuery) -> Option<Arc<Path>>;
|
fn search(&self, query: ManifestQuery) -> Option<Arc<Path>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ManifestDelegate: Send + Sync {
|
||||||
|
fn worktree_id(&self) -> WorktreeId;
|
||||||
|
fn exists(&self, path: &Path, is_dir: Option<bool>) -> bool;
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use collections::HashMap;
|
||||||
use gpui::{AsyncApp, SharedString};
|
use gpui::{AsyncApp, SharedString};
|
||||||
use settings::WorktreeId;
|
use settings::WorktreeId;
|
||||||
|
|
||||||
use crate::LanguageName;
|
use crate::{LanguageName, ManifestName};
|
||||||
|
|
||||||
/// Represents a single toolchain.
|
/// Represents a single toolchain.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -44,10 +44,13 @@ pub trait ToolchainLister: Send + Sync {
|
||||||
async fn list(
|
async fn list(
|
||||||
&self,
|
&self,
|
||||||
worktree_root: PathBuf,
|
worktree_root: PathBuf,
|
||||||
|
subroot_relative_path: Option<Arc<Path>>,
|
||||||
project_env: Option<HashMap<String, String>>,
|
project_env: Option<HashMap<String, String>>,
|
||||||
) -> ToolchainList;
|
) -> ToolchainList;
|
||||||
// Returns a term which we should use in UI to refer to a toolchain.
|
// Returns a term which we should use in UI to refer to a toolchain.
|
||||||
fn term(&self) -> SharedString;
|
fn term(&self) -> SharedString;
|
||||||
|
/// Returns the name of the manifest file for this toolchain.
|
||||||
|
fn manifest_name(&self) -> ManifestName;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
|
|
|
@ -693,9 +693,13 @@ fn get_worktree_venv_declaration(worktree_root: &Path) -> Option<String> {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ToolchainLister for PythonToolchainProvider {
|
impl ToolchainLister for PythonToolchainProvider {
|
||||||
|
fn manifest_name(&self) -> language::ManifestName {
|
||||||
|
ManifestName::from(SharedString::new_static("pyproject.toml"))
|
||||||
|
}
|
||||||
async fn list(
|
async fn list(
|
||||||
&self,
|
&self,
|
||||||
worktree_root: PathBuf,
|
worktree_root: PathBuf,
|
||||||
|
subroot_relative_path: Option<Arc<Path>>,
|
||||||
project_env: Option<HashMap<String, String>>,
|
project_env: Option<HashMap<String, String>>,
|
||||||
) -> ToolchainList {
|
) -> ToolchainList {
|
||||||
let env = project_env.unwrap_or_default();
|
let env = project_env.unwrap_or_default();
|
||||||
|
@ -706,7 +710,14 @@ impl ToolchainLister for PythonToolchainProvider {
|
||||||
&environment,
|
&environment,
|
||||||
);
|
);
|
||||||
let mut config = Configuration::default();
|
let mut config = Configuration::default();
|
||||||
config.workspace_directories = Some(vec![worktree_root.clone()]);
|
|
||||||
|
let mut directories = vec![worktree_root.clone()];
|
||||||
|
if let Some(subroot_relative_path) = subroot_relative_path {
|
||||||
|
debug_assert!(subroot_relative_path.is_relative());
|
||||||
|
directories.push(worktree_root.join(subroot_relative_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
config.workspace_directories = Some(directories);
|
||||||
for locator in locators.iter() {
|
for locator in locators.iter() {
|
||||||
locator.configure(&config);
|
locator.configure(&config);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ use crate::{
|
||||||
lsp_command::{self, *},
|
lsp_command::{self, *},
|
||||||
lsp_store,
|
lsp_store,
|
||||||
manifest_tree::{
|
manifest_tree::{
|
||||||
AdapterQuery, LanguageServerTree, LanguageServerTreeNode, LaunchDisposition, ManifestTree,
|
AdapterQuery, LanguageServerTree, LanguageServerTreeNode, LaunchDisposition,
|
||||||
|
ManifestQueryDelegate, ManifestTree,
|
||||||
},
|
},
|
||||||
prettier_store::{self, PrettierStore, PrettierStoreEvent},
|
prettier_store::{self, PrettierStore, PrettierStoreEvent},
|
||||||
project_settings::{LspSettings, ProjectSettings},
|
project_settings::{LspSettings, ProjectSettings},
|
||||||
|
@ -1036,7 +1037,7 @@ impl LocalLspStore {
|
||||||
else {
|
else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
let delegate = LocalLspAdapterDelegate::from_local_lsp(self, &worktree, cx);
|
let delegate = Arc::new(ManifestQueryDelegate::new(worktree.read(cx).snapshot()));
|
||||||
let root = self.lsp_tree.update(cx, |this, cx| {
|
let root = self.lsp_tree.update(cx, |this, cx| {
|
||||||
this.get(
|
this.get(
|
||||||
project_path,
|
project_path,
|
||||||
|
@ -2290,7 +2291,8 @@ impl LocalLspStore {
|
||||||
})
|
})
|
||||||
.map(|(delegate, servers)| (true, delegate, servers))
|
.map(|(delegate, servers)| (true, delegate, servers))
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let delegate = LocalLspAdapterDelegate::from_local_lsp(self, &worktree, cx);
|
let lsp_delegate = LocalLspAdapterDelegate::from_local_lsp(self, &worktree, cx);
|
||||||
|
let delegate = Arc::new(ManifestQueryDelegate::new(worktree.read(cx).snapshot()));
|
||||||
let servers = self
|
let servers = self
|
||||||
.lsp_tree
|
.lsp_tree
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -2304,7 +2306,7 @@ impl LocalLspStore {
|
||||||
)
|
)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
(false, delegate, servers)
|
(false, lsp_delegate, servers)
|
||||||
});
|
});
|
||||||
let servers = servers
|
let servers = servers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -3585,6 +3587,7 @@ impl LspStore {
|
||||||
prettier_store: Entity<PrettierStore>,
|
prettier_store: Entity<PrettierStore>,
|
||||||
toolchain_store: Entity<ToolchainStore>,
|
toolchain_store: Entity<ToolchainStore>,
|
||||||
environment: Entity<ProjectEnvironment>,
|
environment: Entity<ProjectEnvironment>,
|
||||||
|
manifest_tree: Entity<ManifestTree>,
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
http_client: Arc<dyn HttpClient>,
|
http_client: Arc<dyn HttpClient>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
|
@ -3618,7 +3621,7 @@ impl LspStore {
|
||||||
sender,
|
sender,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let manifest_tree = ManifestTree::new(worktree_store.clone(), cx);
|
|
||||||
Self {
|
Self {
|
||||||
mode: LspStoreMode::Local(LocalLspStore {
|
mode: LspStoreMode::Local(LocalLspStore {
|
||||||
weak: cx.weak_entity(),
|
weak: cx.weak_entity(),
|
||||||
|
@ -4465,10 +4468,13 @@ impl LspStore {
|
||||||
)
|
)
|
||||||
.map(|(delegate, servers)| (true, delegate, servers))
|
.map(|(delegate, servers)| (true, delegate, servers))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let delegate = adapters
|
let lsp_delegate = adapters
|
||||||
.entry(worktree_id)
|
.entry(worktree_id)
|
||||||
.or_insert_with(|| get_adapter(worktree_id, cx))
|
.or_insert_with(|| get_adapter(worktree_id, cx))
|
||||||
.clone()?;
|
.clone()?;
|
||||||
|
let delegate = Arc::new(ManifestQueryDelegate::new(
|
||||||
|
worktree.read(cx).snapshot(),
|
||||||
|
));
|
||||||
let path = file
|
let path = file
|
||||||
.path()
|
.path()
|
||||||
.parent()
|
.parent()
|
||||||
|
@ -4483,7 +4489,7 @@ impl LspStore {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
Some((false, delegate, nodes.collect()))
|
Some((false, lsp_delegate, nodes.collect()))
|
||||||
})
|
})
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -6476,7 +6482,7 @@ impl LspStore {
|
||||||
worktree_id,
|
worktree_id,
|
||||||
path: Arc::from("".as_ref()),
|
path: Arc::from("".as_ref()),
|
||||||
};
|
};
|
||||||
let delegate = LocalLspAdapterDelegate::from_local_lsp(local, &worktree, cx);
|
let delegate = Arc::new(ManifestQueryDelegate::new(worktree.read(cx).snapshot()));
|
||||||
local.lsp_tree.update(cx, |language_server_tree, cx| {
|
local.lsp_tree.update(cx, |language_server_tree, cx| {
|
||||||
for node in language_server_tree.get(
|
for node in language_server_tree.get(
|
||||||
path,
|
path,
|
||||||
|
@ -10204,14 +10210,6 @@ impl LspAdapterDelegate for LocalLspAdapterDelegate {
|
||||||
self.worktree.id()
|
self.worktree.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists(&self, path: &Path, is_dir: Option<bool>) -> bool {
|
|
||||||
self.worktree.entry_for_path(path).map_or(false, |entry| {
|
|
||||||
is_dir.map_or(true, |is_required_to_be_dir| {
|
|
||||||
is_required_to_be_dir == entry.is_dir()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn worktree_root_path(&self) -> &Path {
|
fn worktree_root_path(&self) -> &Path {
|
||||||
self.worktree.abs_path().as_ref()
|
self.worktree.abs_path().as_ref()
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,16 +11,17 @@ use std::{
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
collections::{BTreeMap, hash_map::Entry},
|
collections::{BTreeMap, hash_map::Entry},
|
||||||
ops::ControlFlow,
|
ops::ControlFlow,
|
||||||
|
path::Path,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription};
|
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription};
|
||||||
use language::{LspAdapterDelegate, ManifestName, ManifestQuery};
|
use language::{ManifestDelegate, ManifestName, ManifestQuery};
|
||||||
pub use manifest_store::ManifestProviders;
|
pub use manifest_store::ManifestProviders;
|
||||||
use path_trie::{LabelPresence, RootPathTrie, TriePath};
|
use path_trie::{LabelPresence, RootPathTrie, TriePath};
|
||||||
use settings::{SettingsStore, WorktreeId};
|
use settings::{SettingsStore, WorktreeId};
|
||||||
use worktree::{Event as WorktreeEvent, Worktree};
|
use worktree::{Event as WorktreeEvent, Snapshot, Worktree};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ProjectPath,
|
ProjectPath,
|
||||||
|
@ -89,7 +90,7 @@ pub(crate) enum ManifestTreeEvent {
|
||||||
impl EventEmitter<ManifestTreeEvent> for ManifestTree {}
|
impl EventEmitter<ManifestTreeEvent> for ManifestTree {}
|
||||||
|
|
||||||
impl ManifestTree {
|
impl ManifestTree {
|
||||||
pub(crate) fn new(worktree_store: Entity<WorktreeStore>, cx: &mut App) -> Entity<Self> {
|
pub fn new(worktree_store: Entity<WorktreeStore>, cx: &mut App) -> Entity<Self> {
|
||||||
cx.new(|cx| Self {
|
cx.new(|cx| Self {
|
||||||
root_points: Default::default(),
|
root_points: Default::default(),
|
||||||
_subscriptions: [
|
_subscriptions: [
|
||||||
|
@ -106,11 +107,11 @@ impl ManifestTree {
|
||||||
worktree_store,
|
worktree_store,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn root_for_path(
|
pub(crate) fn root_for_path(
|
||||||
&mut self,
|
&mut self,
|
||||||
ProjectPath { worktree_id, path }: ProjectPath,
|
ProjectPath { worktree_id, path }: ProjectPath,
|
||||||
manifests: &mut dyn Iterator<Item = ManifestName>,
|
manifests: &mut dyn Iterator<Item = ManifestName>,
|
||||||
delegate: Arc<dyn LspAdapterDelegate>,
|
delegate: Arc<dyn ManifestDelegate>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> BTreeMap<ManifestName, ProjectPath> {
|
) -> BTreeMap<ManifestName, ProjectPath> {
|
||||||
debug_assert_eq!(delegate.worktree_id(), worktree_id);
|
debug_assert_eq!(delegate.worktree_id(), worktree_id);
|
||||||
|
@ -218,3 +219,26 @@ impl ManifestTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ManifestQueryDelegate {
|
||||||
|
worktree: Snapshot,
|
||||||
|
}
|
||||||
|
impl ManifestQueryDelegate {
|
||||||
|
pub fn new(worktree: Snapshot) -> Self {
|
||||||
|
Self { worktree }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManifestDelegate for ManifestQueryDelegate {
|
||||||
|
fn exists(&self, path: &Path, is_dir: Option<bool>) -> bool {
|
||||||
|
self.worktree.entry_for_path(path).map_or(false, |entry| {
|
||||||
|
is_dir.map_or(true, |is_required_to_be_dir| {
|
||||||
|
is_required_to_be_dir == entry.is_dir()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn worktree_id(&self) -> WorktreeId {
|
||||||
|
self.worktree.id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use std::{
|
||||||
use collections::{HashMap, IndexMap};
|
use collections::{HashMap, IndexMap};
|
||||||
use gpui::{App, AppContext as _, Entity, Subscription};
|
use gpui::{App, AppContext as _, Entity, Subscription};
|
||||||
use language::{
|
use language::{
|
||||||
Attach, CachedLspAdapter, LanguageName, LanguageRegistry, LspAdapterDelegate,
|
Attach, CachedLspAdapter, LanguageName, LanguageRegistry, ManifestDelegate,
|
||||||
language_settings::AllLanguageSettings,
|
language_settings::AllLanguageSettings,
|
||||||
};
|
};
|
||||||
use lsp::LanguageServerName;
|
use lsp::LanguageServerName;
|
||||||
|
@ -151,7 +151,7 @@ impl LanguageServerTree {
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
query: AdapterQuery<'_>,
|
query: AdapterQuery<'_>,
|
||||||
delegate: Arc<dyn LspAdapterDelegate>,
|
delegate: Arc<dyn ManifestDelegate>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
||||||
let settings_location = SettingsLocation {
|
let settings_location = SettingsLocation {
|
||||||
|
@ -181,7 +181,7 @@ impl LanguageServerTree {
|
||||||
LanguageServerName,
|
LanguageServerName,
|
||||||
(LspSettings, BTreeSet<LanguageName>, Arc<CachedLspAdapter>),
|
(LspSettings, BTreeSet<LanguageName>, Arc<CachedLspAdapter>),
|
||||||
>,
|
>,
|
||||||
delegate: Arc<dyn LspAdapterDelegate>,
|
delegate: Arc<dyn ManifestDelegate>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
||||||
let worktree_id = path.worktree_id;
|
let worktree_id = path.worktree_id;
|
||||||
|
@ -401,7 +401,7 @@ impl<'tree> ServerTreeRebase<'tree> {
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
query: AdapterQuery<'_>,
|
query: AdapterQuery<'_>,
|
||||||
delegate: Arc<dyn LspAdapterDelegate>,
|
delegate: Arc<dyn ManifestDelegate>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
||||||
let settings_location = SettingsLocation {
|
let settings_location = SettingsLocation {
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub use git_store::{
|
||||||
ConflictRegion, ConflictSet, ConflictSetSnapshot, ConflictSetUpdate,
|
ConflictRegion, ConflictSet, ConflictSetSnapshot, ConflictSetUpdate,
|
||||||
git_traversal::{ChildEntriesGitIter, GitEntry, GitEntryRef, GitTraversal},
|
git_traversal::{ChildEntriesGitIter, GitEntry, GitEntryRef, GitTraversal},
|
||||||
};
|
};
|
||||||
|
pub use manifest_tree::ManifestTree;
|
||||||
|
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use buffer_store::{BufferStore, BufferStoreEvent};
|
use buffer_store::{BufferStore, BufferStoreEvent};
|
||||||
|
@ -874,11 +875,13 @@ impl Project {
|
||||||
cx.new(|cx| ContextServerStore::new(worktree_store.clone(), cx));
|
cx.new(|cx| ContextServerStore::new(worktree_store.clone(), cx));
|
||||||
|
|
||||||
let environment = cx.new(|_| ProjectEnvironment::new(env));
|
let environment = cx.new(|_| ProjectEnvironment::new(env));
|
||||||
|
let manifest_tree = ManifestTree::new(worktree_store.clone(), cx);
|
||||||
let toolchain_store = cx.new(|cx| {
|
let toolchain_store = cx.new(|cx| {
|
||||||
ToolchainStore::local(
|
ToolchainStore::local(
|
||||||
languages.clone(),
|
languages.clone(),
|
||||||
worktree_store.clone(),
|
worktree_store.clone(),
|
||||||
environment.clone(),
|
environment.clone(),
|
||||||
|
manifest_tree.clone(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -946,6 +949,7 @@ impl Project {
|
||||||
prettier_store.clone(),
|
prettier_store.clone(),
|
||||||
toolchain_store.clone(),
|
toolchain_store.clone(),
|
||||||
environment.clone(),
|
environment.clone(),
|
||||||
|
manifest_tree,
|
||||||
languages.clone(),
|
languages.clone(),
|
||||||
client.http_client(),
|
client.http_client(),
|
||||||
fs.clone(),
|
fs.clone(),
|
||||||
|
@ -3084,16 +3088,13 @@ impl Project {
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
language_name: LanguageName,
|
language_name: LanguageName,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Task<Option<ToolchainList>> {
|
) -> Task<Option<(ToolchainList, Arc<Path>)>> {
|
||||||
if let Some(toolchain_store) = self.toolchain_store.clone() {
|
if let Some(toolchain_store) = self.toolchain_store.as_ref().map(Entity::downgrade) {
|
||||||
cx.spawn(async move |cx| {
|
cx.spawn(async move |cx| {
|
||||||
cx.update(|cx| {
|
toolchain_store
|
||||||
toolchain_store
|
.update(cx, |this, cx| this.list_toolchains(path, language_name, cx))
|
||||||
.read(cx)
|
.ok()?
|
||||||
.list_toolchains(path, language_name, cx)
|
.await
|
||||||
})
|
|
||||||
.ok()?
|
|
||||||
.await
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Task::ready(None)
|
Task::ready(None)
|
||||||
|
|
|
@ -19,7 +19,11 @@ use rpc::{
|
||||||
use settings::WorktreeId;
|
use settings::WorktreeId;
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
|
|
||||||
use crate::{ProjectEnvironment, ProjectPath, worktree_store::WorktreeStore};
|
use crate::{
|
||||||
|
ProjectEnvironment, ProjectPath,
|
||||||
|
manifest_tree::{ManifestQueryDelegate, ManifestTree},
|
||||||
|
worktree_store::WorktreeStore,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct ToolchainStore(ToolchainStoreInner);
|
pub struct ToolchainStore(ToolchainStoreInner);
|
||||||
enum ToolchainStoreInner {
|
enum ToolchainStoreInner {
|
||||||
|
@ -42,6 +46,7 @@ impl ToolchainStore {
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
worktree_store: Entity<WorktreeStore>,
|
worktree_store: Entity<WorktreeStore>,
|
||||||
project_environment: Entity<ProjectEnvironment>,
|
project_environment: Entity<ProjectEnvironment>,
|
||||||
|
manifest_tree: Entity<ManifestTree>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let entity = cx.new(|_| LocalToolchainStore {
|
let entity = cx.new(|_| LocalToolchainStore {
|
||||||
|
@ -49,6 +54,7 @@ impl ToolchainStore {
|
||||||
worktree_store,
|
worktree_store,
|
||||||
project_environment,
|
project_environment,
|
||||||
active_toolchains: Default::default(),
|
active_toolchains: Default::default(),
|
||||||
|
manifest_tree,
|
||||||
});
|
});
|
||||||
let subscription = cx.subscribe(&entity, |_, _, e: &ToolchainStoreEvent, cx| {
|
let subscription = cx.subscribe(&entity, |_, _, e: &ToolchainStoreEvent, cx| {
|
||||||
cx.emit(e.clone())
|
cx.emit(e.clone())
|
||||||
|
@ -80,11 +86,11 @@ impl ToolchainStore {
|
||||||
&self,
|
&self,
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
language_name: LanguageName,
|
language_name: LanguageName,
|
||||||
cx: &App,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Option<ToolchainList>> {
|
) -> Task<Option<(ToolchainList, Arc<Path>)>> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
ToolchainStoreInner::Local(local, _) => {
|
ToolchainStoreInner::Local(local, _) => {
|
||||||
local.read(cx).list_toolchains(path, language_name, cx)
|
local.update(cx, |this, cx| this.list_toolchains(path, language_name, cx))
|
||||||
}
|
}
|
||||||
ToolchainStoreInner::Remote(remote) => {
|
ToolchainStoreInner::Remote(remote) => {
|
||||||
remote.read(cx).list_toolchains(path, language_name, cx)
|
remote.read(cx).list_toolchains(path, language_name, cx)
|
||||||
|
@ -181,7 +187,7 @@ impl ToolchainStore {
|
||||||
})?
|
})?
|
||||||
.await;
|
.await;
|
||||||
let has_values = toolchains.is_some();
|
let has_values = toolchains.is_some();
|
||||||
let groups = if let Some(toolchains) = &toolchains {
|
let groups = if let Some((toolchains, _)) = &toolchains {
|
||||||
toolchains
|
toolchains
|
||||||
.groups
|
.groups
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -195,8 +201,8 @@ impl ToolchainStore {
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
let toolchains = if let Some(toolchains) = toolchains {
|
let (toolchains, relative_path) = if let Some((toolchains, relative_path)) = toolchains {
|
||||||
toolchains
|
let toolchains = toolchains
|
||||||
.toolchains
|
.toolchains
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|toolchain| {
|
.map(|toolchain| {
|
||||||
|
@ -207,15 +213,17 @@ impl ToolchainStore {
|
||||||
raw_json: toolchain.as_json.to_string(),
|
raw_json: toolchain.as_json.to_string(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>();
|
||||||
|
(toolchains, relative_path)
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
(vec![], Arc::from(Path::new("")))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(proto::ListToolchainsResponse {
|
Ok(proto::ListToolchainsResponse {
|
||||||
has_values,
|
has_values,
|
||||||
toolchains,
|
toolchains,
|
||||||
groups,
|
groups,
|
||||||
|
relative_worktree_path: Some(relative_path.to_string_lossy().into_owned()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn as_language_toolchain_store(&self) -> Arc<dyn LanguageToolchainStore> {
|
pub fn as_language_toolchain_store(&self) -> Arc<dyn LanguageToolchainStore> {
|
||||||
|
@ -231,6 +239,7 @@ struct LocalToolchainStore {
|
||||||
worktree_store: Entity<WorktreeStore>,
|
worktree_store: Entity<WorktreeStore>,
|
||||||
project_environment: Entity<ProjectEnvironment>,
|
project_environment: Entity<ProjectEnvironment>,
|
||||||
active_toolchains: BTreeMap<(WorktreeId, LanguageName), BTreeMap<Arc<Path>, Toolchain>>,
|
active_toolchains: BTreeMap<(WorktreeId, LanguageName), BTreeMap<Arc<Path>, Toolchain>>,
|
||||||
|
manifest_tree: Entity<ManifestTree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
|
@ -312,36 +321,73 @@ impl LocalToolchainStore {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub(crate) fn list_toolchains(
|
pub(crate) fn list_toolchains(
|
||||||
&self,
|
&mut self,
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
language_name: LanguageName,
|
language_name: LanguageName,
|
||||||
cx: &App,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Option<ToolchainList>> {
|
) -> Task<Option<(ToolchainList, Arc<Path>)>> {
|
||||||
let registry = self.languages.clone();
|
let registry = self.languages.clone();
|
||||||
let Some(abs_path) = self
|
|
||||||
.worktree_store
|
let manifest_tree = self.manifest_tree.downgrade();
|
||||||
.read(cx)
|
|
||||||
.worktree_for_id(path.worktree_id, cx)
|
|
||||||
.map(|worktree| worktree.read(cx).abs_path())
|
|
||||||
else {
|
|
||||||
return Task::ready(None);
|
|
||||||
};
|
|
||||||
let environment = self.project_environment.clone();
|
let environment = self.project_environment.clone();
|
||||||
cx.spawn(async move |cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
|
let language = cx
|
||||||
|
.background_spawn(registry.language_for_name(language_name.as_ref()))
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
let toolchains = language.toolchain_lister()?;
|
||||||
|
let manifest_name = toolchains.manifest_name();
|
||||||
|
let (snapshot, worktree) = this
|
||||||
|
.update(cx, |this, cx| {
|
||||||
|
this.worktree_store
|
||||||
|
.read(cx)
|
||||||
|
.worktree_for_id(path.worktree_id, cx)
|
||||||
|
.map(|worktree| (worktree.read(cx).snapshot(), worktree))
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
.flatten()?;
|
||||||
|
let worktree_id = snapshot.id();
|
||||||
|
let worktree_root = snapshot.abs_path().to_path_buf();
|
||||||
|
let relative_path = manifest_tree
|
||||||
|
.update(cx, |this, cx| {
|
||||||
|
this.root_for_path(
|
||||||
|
path,
|
||||||
|
&mut std::iter::once(manifest_name.clone()),
|
||||||
|
Arc::new(ManifestQueryDelegate::new(snapshot)),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok()?
|
||||||
|
.remove(&manifest_name)
|
||||||
|
.unwrap_or_else(|| ProjectPath {
|
||||||
|
path: Arc::from(Path::new("")),
|
||||||
|
worktree_id,
|
||||||
|
});
|
||||||
|
let abs_path = worktree
|
||||||
|
.update(cx, |this, _| this.absolutize(&relative_path.path).ok())
|
||||||
|
.ok()
|
||||||
|
.flatten()?;
|
||||||
|
|
||||||
let project_env = environment
|
let project_env = environment
|
||||||
.update(cx, |environment, cx| {
|
.update(cx, |environment, cx| {
|
||||||
environment.get_directory_environment(abs_path.clone(), cx)
|
environment.get_directory_environment(abs_path.as_path().into(), cx)
|
||||||
})
|
})
|
||||||
.ok()?
|
.ok()?
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
let language = registry
|
Some((
|
||||||
.language_for_name(language_name.as_ref())
|
toolchains
|
||||||
.await
|
.list(
|
||||||
.ok()?;
|
worktree_root,
|
||||||
let toolchains = language.toolchain_lister()?;
|
Some(relative_path.path.clone())
|
||||||
Some(toolchains.list(abs_path.to_path_buf(), project_env).await)
|
.filter(|_| *relative_path.path != *Path::new("")),
|
||||||
|
project_env,
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
relative_path.path,
|
||||||
|
))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
|
@ -404,7 +450,7 @@ impl RemoteToolchainStore {
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
language_name: LanguageName,
|
language_name: LanguageName,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Task<Option<ToolchainList>> {
|
) -> Task<Option<(ToolchainList, Arc<Path>)>> {
|
||||||
let project_id = self.project_id;
|
let project_id = self.project_id;
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
|
@ -444,11 +490,20 @@ impl RemoteToolchainStore {
|
||||||
Some((usize::try_from(group.start_index).ok()?, group.name.into()))
|
Some((usize::try_from(group.start_index).ok()?, group.name.into()))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Some(ToolchainList {
|
let relative_path = Arc::from(Path::new(
|
||||||
toolchains,
|
response
|
||||||
default: None,
|
.relative_worktree_path
|
||||||
groups,
|
.as_deref()
|
||||||
})
|
.unwrap_or_default(),
|
||||||
|
));
|
||||||
|
Some((
|
||||||
|
ToolchainList {
|
||||||
|
toolchains,
|
||||||
|
default: None,
|
||||||
|
groups,
|
||||||
|
},
|
||||||
|
relative_path,
|
||||||
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub(crate) fn active_toolchain(
|
pub(crate) fn active_toolchain(
|
||||||
|
|
|
@ -23,6 +23,7 @@ message ListToolchainsResponse {
|
||||||
repeated Toolchain toolchains = 1;
|
repeated Toolchain toolchains = 1;
|
||||||
bool has_values = 2;
|
bool has_values = 2;
|
||||||
repeated ToolchainGroup groups = 3;
|
repeated ToolchainGroup groups = 3;
|
||||||
|
optional string relative_worktree_path = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ActivateToolchain {
|
message ActivateToolchain {
|
||||||
|
|
|
@ -9,8 +9,8 @@ use http_client::HttpClient;
|
||||||
use language::{Buffer, BufferEvent, LanguageRegistry, proto::serialize_operation};
|
use language::{Buffer, BufferEvent, LanguageRegistry, proto::serialize_operation};
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
use project::{
|
use project::{
|
||||||
LspStore, LspStoreEvent, PrettierStore, ProjectEnvironment, ProjectPath, ToolchainStore,
|
LspStore, LspStoreEvent, ManifestTree, PrettierStore, ProjectEnvironment, ProjectPath,
|
||||||
WorktreeId,
|
ToolchainStore, WorktreeId,
|
||||||
buffer_store::{BufferStore, BufferStoreEvent},
|
buffer_store::{BufferStore, BufferStoreEvent},
|
||||||
debugger::{breakpoint_store::BreakpointStore, dap_store::DapStore},
|
debugger::{breakpoint_store::BreakpointStore, dap_store::DapStore},
|
||||||
git_store::GitStore,
|
git_store::GitStore,
|
||||||
|
@ -87,12 +87,13 @@ impl HeadlessProject {
|
||||||
});
|
});
|
||||||
|
|
||||||
let environment = cx.new(|_| ProjectEnvironment::new(None));
|
let environment = cx.new(|_| ProjectEnvironment::new(None));
|
||||||
|
let manifest_tree = ManifestTree::new(worktree_store.clone(), cx);
|
||||||
let toolchain_store = cx.new(|cx| {
|
let toolchain_store = cx.new(|cx| {
|
||||||
ToolchainStore::local(
|
ToolchainStore::local(
|
||||||
languages.clone(),
|
languages.clone(),
|
||||||
worktree_store.clone(),
|
worktree_store.clone(),
|
||||||
environment.clone(),
|
environment.clone(),
|
||||||
|
manifest_tree.clone(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -172,6 +173,7 @@ impl HeadlessProject {
|
||||||
prettier_store.clone(),
|
prettier_store.clone(),
|
||||||
toolchain_store.clone(),
|
toolchain_store.clone(),
|
||||||
environment,
|
environment,
|
||||||
|
manifest_tree,
|
||||||
languages.clone(),
|
languages.clone(),
|
||||||
http_client.clone(),
|
http_client.clone(),
|
||||||
fs.clone(),
|
fs.clone(),
|
||||||
|
|
|
@ -92,7 +92,7 @@ pub fn python_env_kernel_specifications(
|
||||||
let background_executor = cx.background_executor().clone();
|
let background_executor = cx.background_executor().clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let toolchains = if let Some(toolchains) = toolchains.await {
|
let toolchains = if let Some((toolchains, _)) = toolchains.await {
|
||||||
toolchains
|
toolchains
|
||||||
} else {
|
} else {
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
|
|
|
@ -158,7 +158,7 @@ impl ActiveToolchain {
|
||||||
let project = workspace
|
let project = workspace
|
||||||
.read_with(cx, |this, _| this.project().clone())
|
.read_with(cx, |this, _| this.project().clone())
|
||||||
.ok()?;
|
.ok()?;
|
||||||
let toolchains = cx
|
let (toolchains, relative_path) = cx
|
||||||
.update(|_, cx| {
|
.update(|_, cx| {
|
||||||
project.read(cx).available_toolchains(
|
project.read(cx).available_toolchains(
|
||||||
ProjectPath {
|
ProjectPath {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use gpui::{
|
||||||
use language::{LanguageName, Toolchain, ToolchainList};
|
use language::{LanguageName, Toolchain, ToolchainList};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use project::{Project, ProjectPath, WorktreeId};
|
use project::{Project, ProjectPath, WorktreeId};
|
||||||
use std::{path::Path, sync::Arc};
|
use std::{borrow::Cow, path::Path, sync::Arc};
|
||||||
use ui::{HighlightedLabel, ListItem, ListItemSpacing, prelude::*};
|
use ui::{HighlightedLabel, ListItem, ListItemSpacing, prelude::*};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{ModalView, Workspace};
|
use workspace::{ModalView, Workspace};
|
||||||
|
@ -172,18 +172,8 @@ impl ToolchainSelectorDelegate {
|
||||||
let relative_path = this
|
let relative_path = this
|
||||||
.read_with(cx, |this, _| this.delegate.relative_path.clone())
|
.read_with(cx, |this, _| this.delegate.relative_path.clone())
|
||||||
.ok()?;
|
.ok()?;
|
||||||
let placeholder_text = format!(
|
|
||||||
"Select a {} for `{}`…",
|
|
||||||
term.to_lowercase(),
|
|
||||||
relative_path.to_string_lossy()
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
let _ = this.update_in(cx, move |this, window, cx| {
|
|
||||||
this.delegate.placeholder_text = placeholder_text;
|
|
||||||
this.refresh_placeholder(window, cx);
|
|
||||||
});
|
|
||||||
|
|
||||||
let available_toolchains = project
|
let (available_toolchains, relative_path) = project
|
||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
this.available_toolchains(
|
this.available_toolchains(
|
||||||
ProjectPath {
|
ProjectPath {
|
||||||
|
@ -196,6 +186,21 @@ impl ToolchainSelectorDelegate {
|
||||||
})
|
})
|
||||||
.ok()?
|
.ok()?
|
||||||
.await?;
|
.await?;
|
||||||
|
let pretty_path = {
|
||||||
|
let path = relative_path.to_string_lossy();
|
||||||
|
if path.is_empty() {
|
||||||
|
Cow::Borrowed("worktree root")
|
||||||
|
} else {
|
||||||
|
Cow::Owned(format!("`{}`", path))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let placeholder_text =
|
||||||
|
format!("Select a {} for {pretty_path}…", term.to_lowercase(),).into();
|
||||||
|
let _ = this.update_in(cx, move |this, window, cx| {
|
||||||
|
this.delegate.relative_path = relative_path;
|
||||||
|
this.delegate.placeholder_text = placeholder_text;
|
||||||
|
this.refresh_placeholder(window, cx);
|
||||||
|
});
|
||||||
|
|
||||||
let _ = this.update_in(cx, move |this, window, cx| {
|
let _ = this.update_in(cx, move |this, window, cx| {
|
||||||
this.delegate.candidates = available_toolchains;
|
this.delegate.candidates = available_toolchains;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue