lsp: Identify language servers by their configuration (#35270)
- **WIP: reorganize dispositions** - **Introduce a LocalToolchainStore trait and use it for LspAdapter methods** Closes #35782 Closes #27331 Release Notes: - Python: Improved propagation of a selected virtual environment into the LSP configuration. This should the make all language-related features such as Go to definition or Find all references more reliable. --------- Co-authored-by: Cole Miller <cole@zed.dev> Co-authored-by: Lukas Wirth <lukas@zed.dev>
This commit is contained in:
parent
42ffa8900a
commit
b8a106632f
32 changed files with 1037 additions and 1085 deletions
|
@ -500,13 +500,12 @@ impl LspCommand for PerformRename {
|
|||
mut cx: AsyncApp,
|
||||
) -> Result<ProjectTransaction> {
|
||||
if let Some(edit) = message {
|
||||
let (lsp_adapter, lsp_server) =
|
||||
let (_, lsp_server) =
|
||||
language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
|
||||
LocalLspStore::deserialize_workspace_edit(
|
||||
lsp_store,
|
||||
edit,
|
||||
self.push_to_history,
|
||||
lsp_adapter,
|
||||
lsp_server,
|
||||
&mut cx,
|
||||
)
|
||||
|
@ -1116,18 +1115,12 @@ pub async fn location_links_from_lsp(
|
|||
}
|
||||
}
|
||||
|
||||
let (lsp_adapter, language_server) =
|
||||
language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
|
||||
let (_, language_server) = language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
|
||||
let mut definitions = Vec::new();
|
||||
for (origin_range, target_uri, target_range) in unresolved_links {
|
||||
let target_buffer_handle = lsp_store
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.open_local_buffer_via_lsp(
|
||||
target_uri,
|
||||
language_server.server_id(),
|
||||
lsp_adapter.name.clone(),
|
||||
cx,
|
||||
)
|
||||
this.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
|
@ -1172,8 +1165,7 @@ pub async fn location_link_from_lsp(
|
|||
server_id: LanguageServerId,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<LocationLink> {
|
||||
let (lsp_adapter, language_server) =
|
||||
language_server_for_buffer(&lsp_store, &buffer, server_id, cx)?;
|
||||
let (_, language_server) = language_server_for_buffer(&lsp_store, &buffer, server_id, cx)?;
|
||||
|
||||
let (origin_range, target_uri, target_range) = (
|
||||
link.origin_selection_range,
|
||||
|
@ -1183,12 +1175,7 @@ pub async fn location_link_from_lsp(
|
|||
|
||||
let target_buffer_handle = lsp_store
|
||||
.update(cx, |lsp_store, cx| {
|
||||
lsp_store.open_local_buffer_via_lsp(
|
||||
target_uri,
|
||||
language_server.server_id(),
|
||||
lsp_adapter.name.clone(),
|
||||
cx,
|
||||
)
|
||||
lsp_store.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
|
@ -1326,7 +1313,7 @@ impl LspCommand for GetReferences {
|
|||
mut cx: AsyncApp,
|
||||
) -> Result<Vec<Location>> {
|
||||
let mut references = Vec::new();
|
||||
let (lsp_adapter, language_server) =
|
||||
let (_, language_server) =
|
||||
language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
|
||||
|
||||
if let Some(locations) = locations {
|
||||
|
@ -1336,7 +1323,6 @@ impl LspCommand for GetReferences {
|
|||
lsp_store.open_local_buffer_via_lsp(
|
||||
lsp_location.uri,
|
||||
language_server.server_id(),
|
||||
lsp_adapter.name.clone(),
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,18 +7,12 @@ mod manifest_store;
|
|||
mod path_trie;
|
||||
mod server_tree;
|
||||
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::{BTreeMap, hash_map::Entry},
|
||||
ops::ControlFlow,
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{borrow::Borrow, collections::hash_map::Entry, ops::ControlFlow, path::Path, sync::Arc};
|
||||
|
||||
use collections::HashMap;
|
||||
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription};
|
||||
use gpui::{App, AppContext as _, Context, Entity, Subscription};
|
||||
use language::{ManifestDelegate, ManifestName, ManifestQuery};
|
||||
pub use manifest_store::ManifestProviders;
|
||||
pub use manifest_store::ManifestProvidersStore;
|
||||
use path_trie::{LabelPresence, RootPathTrie, TriePath};
|
||||
use settings::{SettingsStore, WorktreeId};
|
||||
use worktree::{Event as WorktreeEvent, Snapshot, Worktree};
|
||||
|
@ -28,9 +22,7 @@ use crate::{
|
|||
worktree_store::{WorktreeStore, WorktreeStoreEvent},
|
||||
};
|
||||
|
||||
pub(crate) use server_tree::{
|
||||
AdapterQuery, LanguageServerTree, LanguageServerTreeNode, LaunchDisposition,
|
||||
};
|
||||
pub(crate) use server_tree::{LanguageServerTree, LanguageServerTreeNode, LaunchDisposition};
|
||||
|
||||
struct WorktreeRoots {
|
||||
roots: RootPathTrie<ManifestName>,
|
||||
|
@ -81,14 +73,6 @@ pub struct ManifestTree {
|
|||
_subscriptions: [Subscription; 2],
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub(crate) enum ManifestTreeEvent {
|
||||
WorktreeRemoved(WorktreeId),
|
||||
Cleared,
|
||||
}
|
||||
|
||||
impl EventEmitter<ManifestTreeEvent> for ManifestTree {}
|
||||
|
||||
impl ManifestTree {
|
||||
pub fn new(worktree_store: Entity<WorktreeStore>, cx: &mut App) -> Entity<Self> {
|
||||
cx.new(|cx| Self {
|
||||
|
@ -101,30 +85,28 @@ impl ManifestTree {
|
|||
worktree_roots.roots = RootPathTrie::new();
|
||||
})
|
||||
}
|
||||
cx.emit(ManifestTreeEvent::Cleared);
|
||||
}),
|
||||
],
|
||||
worktree_store,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn root_for_path(
|
||||
&mut self,
|
||||
ProjectPath { worktree_id, path }: ProjectPath,
|
||||
manifests: &mut dyn Iterator<Item = ManifestName>,
|
||||
delegate: Arc<dyn ManifestDelegate>,
|
||||
ProjectPath { worktree_id, path }: &ProjectPath,
|
||||
manifest_name: &ManifestName,
|
||||
delegate: &Arc<dyn ManifestDelegate>,
|
||||
cx: &mut App,
|
||||
) -> BTreeMap<ManifestName, ProjectPath> {
|
||||
debug_assert_eq!(delegate.worktree_id(), worktree_id);
|
||||
let mut roots = BTreeMap::from_iter(
|
||||
manifests.map(|manifest| (manifest, (None, LabelPresence::KnownAbsent))),
|
||||
);
|
||||
let worktree_roots = match self.root_points.entry(worktree_id) {
|
||||
) -> Option<ProjectPath> {
|
||||
debug_assert_eq!(delegate.worktree_id(), *worktree_id);
|
||||
let (mut marked_path, mut current_presence) = (None, LabelPresence::KnownAbsent);
|
||||
let worktree_roots = match self.root_points.entry(*worktree_id) {
|
||||
Entry::Occupied(occupied_entry) => occupied_entry.get().clone(),
|
||||
Entry::Vacant(vacant_entry) => {
|
||||
let Some(worktree) = self
|
||||
.worktree_store
|
||||
.read(cx)
|
||||
.worktree_for_id(worktree_id, cx)
|
||||
.worktree_for_id(*worktree_id, cx)
|
||||
else {
|
||||
return Default::default();
|
||||
};
|
||||
|
@ -133,16 +115,16 @@ impl ManifestTree {
|
|||
}
|
||||
};
|
||||
|
||||
let key = TriePath::from(&*path);
|
||||
let key = TriePath::from(&**path);
|
||||
worktree_roots.read_with(cx, |this, _| {
|
||||
this.roots.walk(&key, &mut |path, labels| {
|
||||
for (label, presence) in labels {
|
||||
if let Some((marked_path, current_presence)) = roots.get_mut(label) {
|
||||
if *current_presence > *presence {
|
||||
if label == manifest_name {
|
||||
if current_presence > *presence {
|
||||
debug_assert!(false, "RootPathTrie precondition violation; while walking the tree label presence is only allowed to increase");
|
||||
}
|
||||
*marked_path = Some(ProjectPath {worktree_id, path: path.clone()});
|
||||
*current_presence = *presence;
|
||||
marked_path = Some(ProjectPath {worktree_id: *worktree_id, path: path.clone()});
|
||||
current_presence = *presence;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -150,12 +132,9 @@ impl ManifestTree {
|
|||
});
|
||||
});
|
||||
|
||||
for (manifest_name, (root_path, presence)) in &mut roots {
|
||||
if *presence == LabelPresence::Present {
|
||||
continue;
|
||||
}
|
||||
|
||||
let depth = root_path
|
||||
if current_presence == LabelPresence::KnownAbsent {
|
||||
// Some part of the path is unexplored.
|
||||
let depth = marked_path
|
||||
.as_ref()
|
||||
.map(|root_path| {
|
||||
path.strip_prefix(&root_path.path)
|
||||
|
@ -165,13 +144,10 @@ impl ManifestTree {
|
|||
})
|
||||
.unwrap_or_else(|| path.components().count() + 1);
|
||||
|
||||
if depth > 0 {
|
||||
let Some(provider) = ManifestProviders::global(cx).get(manifest_name.borrow())
|
||||
else {
|
||||
log::warn!("Manifest provider `{}` not found", manifest_name.as_ref());
|
||||
continue;
|
||||
};
|
||||
|
||||
if depth > 0
|
||||
&& let Some(provider) =
|
||||
ManifestProvidersStore::global(cx).get(manifest_name.borrow())
|
||||
{
|
||||
let root = provider.search(ManifestQuery {
|
||||
path: path.clone(),
|
||||
depth,
|
||||
|
@ -182,9 +158,9 @@ impl ManifestTree {
|
|||
let root = TriePath::from(&*known_root);
|
||||
this.roots
|
||||
.insert(&root, manifest_name.clone(), LabelPresence::Present);
|
||||
*presence = LabelPresence::Present;
|
||||
*root_path = Some(ProjectPath {
|
||||
worktree_id,
|
||||
current_presence = LabelPresence::Present;
|
||||
marked_path = Some(ProjectPath {
|
||||
worktree_id: *worktree_id,
|
||||
path: known_root,
|
||||
});
|
||||
}),
|
||||
|
@ -195,25 +171,35 @@ impl ManifestTree {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
roots
|
||||
.into_iter()
|
||||
.filter_map(|(k, (path, presence))| {
|
||||
let path = path?;
|
||||
presence.eq(&LabelPresence::Present).then(|| (k, path))
|
||||
})
|
||||
.collect()
|
||||
marked_path.filter(|_| current_presence.eq(&LabelPresence::Present))
|
||||
}
|
||||
|
||||
pub(crate) fn root_for_path_or_worktree_root(
|
||||
&mut self,
|
||||
project_path: &ProjectPath,
|
||||
manifest_name: Option<&ManifestName>,
|
||||
delegate: &Arc<dyn ManifestDelegate>,
|
||||
cx: &mut App,
|
||||
) -> ProjectPath {
|
||||
let worktree_id = project_path.worktree_id;
|
||||
// Backwards-compat: Fill in any adapters for which we did not detect the root as having the project root at the root of a worktree.
|
||||
manifest_name
|
||||
.and_then(|manifest_name| self.root_for_path(project_path, manifest_name, delegate, cx))
|
||||
.unwrap_or_else(|| ProjectPath {
|
||||
worktree_id,
|
||||
path: Arc::from(Path::new("")),
|
||||
})
|
||||
}
|
||||
|
||||
fn on_worktree_store_event(
|
||||
&mut self,
|
||||
_: Entity<WorktreeStore>,
|
||||
evt: &WorktreeStoreEvent,
|
||||
cx: &mut Context<Self>,
|
||||
_: &mut Context<Self>,
|
||||
) {
|
||||
match evt {
|
||||
WorktreeStoreEvent::WorktreeRemoved(_, worktree_id) => {
|
||||
self.root_points.remove(&worktree_id);
|
||||
cx.emit(ManifestTreeEvent::WorktreeRemoved(*worktree_id));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -223,6 +209,7 @@ impl ManifestTree {
|
|||
pub(crate) struct ManifestQueryDelegate {
|
||||
worktree: Snapshot,
|
||||
}
|
||||
|
||||
impl ManifestQueryDelegate {
|
||||
pub fn new(worktree: Snapshot) -> Self {
|
||||
Self { worktree }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use collections::HashMap;
|
||||
use collections::{HashMap, HashSet};
|
||||
use gpui::{App, Global, SharedString};
|
||||
use parking_lot::RwLock;
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
@ -11,13 +11,13 @@ struct ManifestProvidersState {
|
|||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ManifestProviders(Arc<RwLock<ManifestProvidersState>>);
|
||||
pub struct ManifestProvidersStore(Arc<RwLock<ManifestProvidersState>>);
|
||||
|
||||
#[derive(Default)]
|
||||
struct GlobalManifestProvider(ManifestProviders);
|
||||
struct GlobalManifestProvider(ManifestProvidersStore);
|
||||
|
||||
impl Deref for GlobalManifestProvider {
|
||||
type Target = ManifestProviders;
|
||||
type Target = ManifestProvidersStore;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
|
@ -26,7 +26,7 @@ impl Deref for GlobalManifestProvider {
|
|||
|
||||
impl Global for GlobalManifestProvider {}
|
||||
|
||||
impl ManifestProviders {
|
||||
impl ManifestProvidersStore {
|
||||
/// Returns the global [`ManifestStore`].
|
||||
///
|
||||
/// Inserts a default [`ManifestStore`] if one does not yet exist.
|
||||
|
@ -45,4 +45,7 @@ impl ManifestProviders {
|
|||
pub(super) fn get(&self, name: &SharedString) -> Option<Arc<dyn ManifestProvider>> {
|
||||
self.0.read().providers.get(name).cloned()
|
||||
}
|
||||
pub(crate) fn manifest_file_names(&self) -> HashSet<ManifestName> {
|
||||
self.0.read().providers.keys().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
//!
|
||||
//! ## RPC
|
||||
//! LSP Tree is transparent to RPC peers; when clients ask host to spawn a new language server, the host will perform LSP Tree lookup for provided path; it may decide
|
||||
//! to reuse existing language server. The client maintains it's own LSP Tree that is a subset of host LSP Tree. Done this way, the client does not need to
|
||||
//! ask about suitable language server for each path it interacts with; it can resolve most of the queries locally.
|
||||
//! to reuse existing language server.
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
|
@ -14,20 +13,23 @@ use std::{
|
|||
};
|
||||
|
||||
use collections::IndexMap;
|
||||
use gpui::{App, AppContext as _, Entity, Subscription};
|
||||
use gpui::{App, Entity};
|
||||
use language::{
|
||||
CachedLspAdapter, LanguageName, LanguageRegistry, ManifestDelegate,
|
||||
CachedLspAdapter, LanguageName, LanguageRegistry, ManifestDelegate, ManifestName, Toolchain,
|
||||
language_settings::AllLanguageSettings,
|
||||
};
|
||||
use lsp::LanguageServerName;
|
||||
use settings::{Settings, SettingsLocation, WorktreeId};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::{LanguageServerId, ProjectPath, project_settings::LspSettings};
|
||||
use crate::{
|
||||
LanguageServerId, ProjectPath, project_settings::LspSettings,
|
||||
toolchain_store::LocalToolchainStore,
|
||||
};
|
||||
|
||||
use super::{ManifestTree, ManifestTreeEvent};
|
||||
use super::ManifestTree;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct ServersForWorktree {
|
||||
pub(crate) roots: BTreeMap<
|
||||
Arc<Path>,
|
||||
|
@ -39,7 +41,7 @@ pub struct LanguageServerTree {
|
|||
manifest_tree: Entity<ManifestTree>,
|
||||
pub(crate) instances: BTreeMap<WorktreeId, ServersForWorktree>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
_subscriptions: Subscription,
|
||||
toolchains: Entity<LocalToolchainStore>,
|
||||
}
|
||||
|
||||
/// A node in language server tree represents either:
|
||||
|
@ -49,22 +51,15 @@ pub struct LanguageServerTree {
|
|||
pub struct LanguageServerTreeNode(Weak<InnerTreeNode>);
|
||||
|
||||
/// Describes a request to launch a language server.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LaunchDisposition<'a> {
|
||||
pub(crate) server_name: &'a LanguageServerName,
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct LaunchDisposition {
|
||||
pub(crate) server_name: LanguageServerName,
|
||||
/// Path to the root directory of a subproject.
|
||||
pub(crate) path: ProjectPath,
|
||||
pub(crate) settings: Arc<LspSettings>,
|
||||
pub(crate) toolchain: Option<Toolchain>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a InnerTreeNode> for LaunchDisposition<'a> {
|
||||
fn from(value: &'a InnerTreeNode) -> Self {
|
||||
LaunchDisposition {
|
||||
server_name: &value.name,
|
||||
path: value.path.clone(),
|
||||
settings: value.settings.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl LanguageServerTreeNode {
|
||||
/// Returns a language server ID for this node if there is one.
|
||||
/// Returns None if this node has not been initialized yet or it is no longer in the tree.
|
||||
|
@ -76,19 +71,17 @@ impl LanguageServerTreeNode {
|
|||
/// May return None if the node no longer belongs to the server tree it was created in.
|
||||
pub(crate) fn server_id_or_init(
|
||||
&self,
|
||||
init: impl FnOnce(LaunchDisposition) -> LanguageServerId,
|
||||
init: impl FnOnce(&Arc<LaunchDisposition>) -> LanguageServerId,
|
||||
) -> Option<LanguageServerId> {
|
||||
let this = self.0.upgrade()?;
|
||||
Some(
|
||||
*this
|
||||
.id
|
||||
.get_or_init(|| init(LaunchDisposition::from(&*this))),
|
||||
)
|
||||
Some(*this.id.get_or_init(|| init(&this.disposition)))
|
||||
}
|
||||
|
||||
/// Returns a language server name as the language server adapter would return.
|
||||
pub fn name(&self) -> Option<LanguageServerName> {
|
||||
self.0.upgrade().map(|node| node.name.clone())
|
||||
self.0
|
||||
.upgrade()
|
||||
.map(|node| node.disposition.server_name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,160 +94,149 @@ impl From<Weak<InnerTreeNode>> for LanguageServerTreeNode {
|
|||
#[derive(Debug)]
|
||||
pub struct InnerTreeNode {
|
||||
id: OnceLock<LanguageServerId>,
|
||||
name: LanguageServerName,
|
||||
path: ProjectPath,
|
||||
settings: Arc<LspSettings>,
|
||||
disposition: Arc<LaunchDisposition>,
|
||||
}
|
||||
|
||||
impl InnerTreeNode {
|
||||
fn new(
|
||||
name: LanguageServerName,
|
||||
server_name: LanguageServerName,
|
||||
path: ProjectPath,
|
||||
settings: impl Into<Arc<LspSettings>>,
|
||||
settings: LspSettings,
|
||||
toolchain: Option<Toolchain>,
|
||||
) -> Self {
|
||||
InnerTreeNode {
|
||||
id: Default::default(),
|
||||
name,
|
||||
path,
|
||||
settings: settings.into(),
|
||||
disposition: Arc::new(LaunchDisposition {
|
||||
server_name,
|
||||
path,
|
||||
settings: settings.into(),
|
||||
toolchain,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines how the list of adapters to query should be constructed.
|
||||
pub(crate) enum AdapterQuery<'a> {
|
||||
/// Search for roots of all adapters associated with a given language name.
|
||||
/// Layman: Look for all project roots along the queried path that have any
|
||||
/// language server associated with this language running.
|
||||
Language(&'a LanguageName),
|
||||
/// Search for roots of adapter with a given name.
|
||||
/// Layman: Look for all project roots along the queried path that have this server running.
|
||||
Adapter(&'a LanguageServerName),
|
||||
}
|
||||
|
||||
impl LanguageServerTree {
|
||||
pub(crate) fn new(
|
||||
manifest_tree: Entity<ManifestTree>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
cx: &mut App,
|
||||
) -> Entity<Self> {
|
||||
cx.new(|cx| Self {
|
||||
_subscriptions: cx.subscribe(&manifest_tree, |_: &mut Self, _, event, _| {
|
||||
if event == &ManifestTreeEvent::Cleared {}
|
||||
}),
|
||||
toolchains: Entity<LocalToolchainStore>,
|
||||
) -> Self {
|
||||
Self {
|
||||
manifest_tree,
|
||||
instances: Default::default(),
|
||||
|
||||
languages,
|
||||
})
|
||||
toolchains,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all initialized language server IDs for a given path.
|
||||
pub(crate) fn get<'a>(
|
||||
&'a self,
|
||||
path: ProjectPath,
|
||||
language_name: LanguageName,
|
||||
manifest_name: Option<&ManifestName>,
|
||||
delegate: &Arc<dyn ManifestDelegate>,
|
||||
cx: &mut App,
|
||||
) -> impl Iterator<Item = LanguageServerId> + 'a {
|
||||
let manifest_location = self.manifest_location_for_path(&path, manifest_name, delegate, cx);
|
||||
let adapters = self.adapters_for_language(&manifest_location, &language_name, cx);
|
||||
self.get_with_adapters(manifest_location, adapters)
|
||||
}
|
||||
|
||||
/// Get all language server root points for a given path and language; the language servers might already be initialized at a given path.
|
||||
pub(crate) fn get<'a>(
|
||||
pub(crate) fn walk<'a>(
|
||||
&'a mut self,
|
||||
path: ProjectPath,
|
||||
query: AdapterQuery<'_>,
|
||||
delegate: Arc<dyn ManifestDelegate>,
|
||||
cx: &mut App,
|
||||
language_name: LanguageName,
|
||||
manifest_name: Option<&ManifestName>,
|
||||
delegate: &Arc<dyn ManifestDelegate>,
|
||||
cx: &'a mut App,
|
||||
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
||||
let settings_location = SettingsLocation {
|
||||
worktree_id: path.worktree_id,
|
||||
path: &path.path,
|
||||
};
|
||||
let adapters = match query {
|
||||
AdapterQuery::Language(language_name) => {
|
||||
self.adapters_for_language(settings_location, language_name, cx)
|
||||
}
|
||||
AdapterQuery::Adapter(language_server_name) => {
|
||||
IndexMap::from_iter(self.adapter_for_name(language_server_name).map(|adapter| {
|
||||
(
|
||||
let manifest_location = self.manifest_location_for_path(&path, manifest_name, delegate, cx);
|
||||
let adapters = self.adapters_for_language(&manifest_location, &language_name, cx);
|
||||
self.init_with_adapters(manifest_location, language_name, adapters, cx)
|
||||
}
|
||||
|
||||
fn init_with_adapters<'a>(
|
||||
&'a mut self,
|
||||
root_path: ProjectPath,
|
||||
language_name: LanguageName,
|
||||
adapters: IndexMap<LanguageServerName, (LspSettings, Arc<CachedLspAdapter>)>,
|
||||
cx: &'a App,
|
||||
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
||||
adapters.into_iter().map(move |(_, (settings, adapter))| {
|
||||
let root_path = root_path.clone();
|
||||
let inner_node = self
|
||||
.instances
|
||||
.entry(root_path.worktree_id)
|
||||
.or_default()
|
||||
.roots
|
||||
.entry(root_path.path.clone())
|
||||
.or_default()
|
||||
.entry(adapter.name());
|
||||
let (node, languages) = inner_node.or_insert_with(|| {
|
||||
let toolchain = self.toolchains.read(cx).active_toolchain(
|
||||
root_path.worktree_id,
|
||||
&root_path.path,
|
||||
language_name.clone(),
|
||||
);
|
||||
(
|
||||
Arc::new(InnerTreeNode::new(
|
||||
adapter.name(),
|
||||
(LspSettings::default(), BTreeSet::new(), adapter),
|
||||
)
|
||||
}))
|
||||
}
|
||||
};
|
||||
self.get_with_adapters(path, adapters, delegate, cx)
|
||||
root_path.clone(),
|
||||
settings.clone(),
|
||||
toolchain,
|
||||
)),
|
||||
Default::default(),
|
||||
)
|
||||
});
|
||||
languages.insert(language_name.clone());
|
||||
Arc::downgrade(&node).into()
|
||||
})
|
||||
}
|
||||
|
||||
fn get_with_adapters<'a>(
|
||||
&'a mut self,
|
||||
path: ProjectPath,
|
||||
adapters: IndexMap<
|
||||
LanguageServerName,
|
||||
(LspSettings, BTreeSet<LanguageName>, Arc<CachedLspAdapter>),
|
||||
>,
|
||||
delegate: Arc<dyn ManifestDelegate>,
|
||||
cx: &mut App,
|
||||
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
||||
let worktree_id = path.worktree_id;
|
||||
|
||||
let mut manifest_to_adapters = BTreeMap::default();
|
||||
for (_, _, adapter) in adapters.values() {
|
||||
if let Some(manifest_name) = adapter.manifest_name() {
|
||||
manifest_to_adapters
|
||||
.entry(manifest_name)
|
||||
.or_insert_with(Vec::default)
|
||||
.push(adapter.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let roots = self.manifest_tree.update(cx, |this, cx| {
|
||||
this.root_for_path(
|
||||
path,
|
||||
&mut manifest_to_adapters.keys().cloned(),
|
||||
delegate,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let root_path = std::cell::LazyCell::new(move || ProjectPath {
|
||||
worktree_id,
|
||||
path: Arc::from("".as_ref()),
|
||||
});
|
||||
adapters
|
||||
.into_iter()
|
||||
.map(move |(_, (settings, new_languages, adapter))| {
|
||||
// Backwards-compat: Fill in any adapters for which we did not detect the root as having the project root at the root of a worktree.
|
||||
let root_path = adapter
|
||||
.manifest_name()
|
||||
.and_then(|name| roots.get(&name))
|
||||
.cloned()
|
||||
.unwrap_or_else(|| root_path.clone());
|
||||
|
||||
let inner_node = self
|
||||
.instances
|
||||
.entry(root_path.worktree_id)
|
||||
.or_default()
|
||||
.roots
|
||||
.entry(root_path.path.clone())
|
||||
.or_default()
|
||||
.entry(adapter.name());
|
||||
let (node, languages) = inner_node.or_insert_with(|| {
|
||||
(
|
||||
Arc::new(InnerTreeNode::new(
|
||||
adapter.name(),
|
||||
root_path.clone(),
|
||||
settings.clone(),
|
||||
)),
|
||||
Default::default(),
|
||||
)
|
||||
});
|
||||
languages.extend(new_languages.iter().cloned());
|
||||
Arc::downgrade(&node).into()
|
||||
})
|
||||
&'a self,
|
||||
root_path: ProjectPath,
|
||||
adapters: IndexMap<LanguageServerName, (LspSettings, Arc<CachedLspAdapter>)>,
|
||||
) -> impl Iterator<Item = LanguageServerId> + 'a {
|
||||
adapters.into_iter().filter_map(move |(_, (_, adapter))| {
|
||||
let root_path = root_path.clone();
|
||||
let inner_node = self
|
||||
.instances
|
||||
.get(&root_path.worktree_id)?
|
||||
.roots
|
||||
.get(&root_path.path)?
|
||||
.get(&adapter.name())?;
|
||||
inner_node.0.id.get().copied()
|
||||
})
|
||||
}
|
||||
|
||||
fn adapter_for_name(&self, name: &LanguageServerName) -> Option<Arc<CachedLspAdapter>> {
|
||||
self.languages.adapter_for_name(name)
|
||||
fn manifest_location_for_path(
|
||||
&self,
|
||||
path: &ProjectPath,
|
||||
manifest_name: Option<&ManifestName>,
|
||||
delegate: &Arc<dyn ManifestDelegate>,
|
||||
cx: &mut App,
|
||||
) -> ProjectPath {
|
||||
// Find out what the root location of our subproject is.
|
||||
// That's where we'll look for language settings (that include a set of language servers).
|
||||
self.manifest_tree.update(cx, |this, cx| {
|
||||
this.root_for_path_or_worktree_root(path, manifest_name, delegate, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn adapters_for_language(
|
||||
&self,
|
||||
settings_location: SettingsLocation,
|
||||
manifest_location: &ProjectPath,
|
||||
language_name: &LanguageName,
|
||||
cx: &App,
|
||||
) -> IndexMap<LanguageServerName, (LspSettings, BTreeSet<LanguageName>, Arc<CachedLspAdapter>)>
|
||||
{
|
||||
) -> IndexMap<LanguageServerName, (LspSettings, Arc<CachedLspAdapter>)> {
|
||||
let settings_location = SettingsLocation {
|
||||
worktree_id: manifest_location.worktree_id,
|
||||
path: &manifest_location.path,
|
||||
};
|
||||
let settings = AllLanguageSettings::get(Some(settings_location), cx).language(
|
||||
Some(settings_location),
|
||||
Some(language_name),
|
||||
|
@ -295,14 +277,7 @@ impl LanguageServerTree {
|
|||
)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
Some((
|
||||
adapter.name(),
|
||||
(
|
||||
adapter_settings,
|
||||
BTreeSet::from_iter([language_name.clone()]),
|
||||
adapter,
|
||||
),
|
||||
))
|
||||
Some((adapter.name(), (adapter_settings, adapter)))
|
||||
})
|
||||
.collect::<IndexMap<_, _>>();
|
||||
// After starting all the language servers, reorder them to reflect the desired order
|
||||
|
@ -315,17 +290,23 @@ impl LanguageServerTree {
|
|||
&language_name,
|
||||
adapters_with_settings
|
||||
.values()
|
||||
.map(|(_, _, adapter)| adapter.clone())
|
||||
.map(|(_, adapter)| adapter.clone())
|
||||
.collect(),
|
||||
);
|
||||
|
||||
adapters_with_settings
|
||||
}
|
||||
|
||||
// Rebasing a tree:
|
||||
// - Clears it out
|
||||
// - Provides you with the indirect access to the old tree while you're reinitializing a new one (by querying it).
|
||||
pub(crate) fn rebase(&mut self) -> ServerTreeRebase<'_> {
|
||||
/// Server Tree is built up incrementally via queries for distinct paths of the worktree.
|
||||
/// Results of these queries have to be invalidated when data used to build the tree changes.
|
||||
///
|
||||
/// The environment of a server tree is a set of all user settings.
|
||||
/// Rebasing a tree means invalidating it and building up a new one while reusing the old tree where applicable.
|
||||
/// We want to reuse the old tree in order to preserve as many of the running language servers as possible.
|
||||
/// E.g. if the user disables one of their language servers for Python, we don't want to shut down any language servers unaffected by this settings change.
|
||||
///
|
||||
/// Thus, [`ServerTreeRebase`] mimics the interface of a [`ServerTree`], except that it tries to find a matching language server in the old tree before handing out an uninitialized node.
|
||||
pub(crate) fn rebase(&mut self) -> ServerTreeRebase {
|
||||
ServerTreeRebase::new(self)
|
||||
}
|
||||
|
||||
|
@ -354,16 +335,16 @@ impl LanguageServerTree {
|
|||
.roots
|
||||
.entry(Arc::from(Path::new("")))
|
||||
.or_default()
|
||||
.entry(node.name.clone())
|
||||
.entry(node.disposition.server_name.clone())
|
||||
.or_insert_with(|| (node, BTreeSet::new()))
|
||||
.1
|
||||
.insert(language_name);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ServerTreeRebase<'a> {
|
||||
pub(crate) struct ServerTreeRebase {
|
||||
old_contents: BTreeMap<WorktreeId, ServersForWorktree>,
|
||||
new_tree: &'a mut LanguageServerTree,
|
||||
new_tree: LanguageServerTree,
|
||||
/// All server IDs seen in the old tree.
|
||||
all_server_ids: BTreeMap<LanguageServerId, LanguageServerName>,
|
||||
/// Server IDs we've preserved for a new iteration of the tree. `all_server_ids - rebased_server_ids` is the
|
||||
|
@ -371,9 +352,9 @@ pub(crate) struct ServerTreeRebase<'a> {
|
|||
rebased_server_ids: BTreeSet<LanguageServerId>,
|
||||
}
|
||||
|
||||
impl<'tree> ServerTreeRebase<'tree> {
|
||||
fn new(new_tree: &'tree mut LanguageServerTree) -> Self {
|
||||
let old_contents = std::mem::take(&mut new_tree.instances);
|
||||
impl ServerTreeRebase {
|
||||
fn new(old_tree: &LanguageServerTree) -> Self {
|
||||
let old_contents = old_tree.instances.clone();
|
||||
let all_server_ids = old_contents
|
||||
.values()
|
||||
.flat_map(|nodes| {
|
||||
|
@ -384,69 +365,68 @@ impl<'tree> ServerTreeRebase<'tree> {
|
|||
.id
|
||||
.get()
|
||||
.copied()
|
||||
.map(|id| (id, server.0.name.clone()))
|
||||
.map(|id| (id, server.0.disposition.server_name.clone()))
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let new_tree = LanguageServerTree::new(
|
||||
old_tree.manifest_tree.clone(),
|
||||
old_tree.languages.clone(),
|
||||
old_tree.toolchains.clone(),
|
||||
);
|
||||
Self {
|
||||
old_contents,
|
||||
new_tree,
|
||||
all_server_ids,
|
||||
new_tree,
|
||||
rebased_server_ids: BTreeSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get<'a>(
|
||||
pub(crate) fn walk<'a>(
|
||||
&'a mut self,
|
||||
path: ProjectPath,
|
||||
query: AdapterQuery<'_>,
|
||||
language_name: LanguageName,
|
||||
manifest_name: Option<&ManifestName>,
|
||||
delegate: Arc<dyn ManifestDelegate>,
|
||||
cx: &mut App,
|
||||
cx: &'a mut App,
|
||||
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
|
||||
let settings_location = SettingsLocation {
|
||||
worktree_id: path.worktree_id,
|
||||
path: &path.path,
|
||||
};
|
||||
let adapters = match query {
|
||||
AdapterQuery::Language(language_name) => {
|
||||
self.new_tree
|
||||
.adapters_for_language(settings_location, language_name, cx)
|
||||
}
|
||||
AdapterQuery::Adapter(language_server_name) => {
|
||||
IndexMap::from_iter(self.new_tree.adapter_for_name(language_server_name).map(
|
||||
|adapter| {
|
||||
(
|
||||
adapter.name(),
|
||||
(LspSettings::default(), BTreeSet::new(), adapter),
|
||||
)
|
||||
},
|
||||
))
|
||||
}
|
||||
};
|
||||
let manifest =
|
||||
self.new_tree
|
||||
.manifest_location_for_path(&path, manifest_name, &delegate, cx);
|
||||
let adapters = self
|
||||
.new_tree
|
||||
.adapters_for_language(&manifest, &language_name, cx);
|
||||
|
||||
self.new_tree
|
||||
.get_with_adapters(path, adapters, delegate, cx)
|
||||
.init_with_adapters(manifest, language_name, adapters, cx)
|
||||
.filter_map(|node| {
|
||||
// Inspect result of the query and initialize it ourselves before
|
||||
// handing it off to the caller.
|
||||
let disposition = node.0.upgrade()?;
|
||||
let live_node = node.0.upgrade()?;
|
||||
|
||||
if disposition.id.get().is_some() {
|
||||
if live_node.id.get().is_some() {
|
||||
return Some(node);
|
||||
}
|
||||
let disposition = &live_node.disposition;
|
||||
let Some((existing_node, _)) = self
|
||||
.old_contents
|
||||
.get(&disposition.path.worktree_id)
|
||||
.and_then(|worktree_nodes| worktree_nodes.roots.get(&disposition.path.path))
|
||||
.and_then(|roots| roots.get(&disposition.name))
|
||||
.filter(|(old_node, _)| disposition.settings == old_node.settings)
|
||||
.and_then(|roots| roots.get(&disposition.server_name))
|
||||
.filter(|(old_node, _)| {
|
||||
(&disposition.toolchain, &disposition.settings)
|
||||
== (
|
||||
&old_node.disposition.toolchain,
|
||||
&old_node.disposition.settings,
|
||||
)
|
||||
})
|
||||
else {
|
||||
return Some(node);
|
||||
};
|
||||
if let Some(existing_id) = existing_node.id.get() {
|
||||
self.rebased_server_ids.insert(*existing_id);
|
||||
disposition.id.set(*existing_id).ok();
|
||||
live_node.id.set(*existing_id).ok();
|
||||
}
|
||||
|
||||
Some(node)
|
||||
|
@ -454,11 +434,19 @@ impl<'tree> ServerTreeRebase<'tree> {
|
|||
}
|
||||
|
||||
/// Returns IDs of servers that are no longer referenced (and can be shut down).
|
||||
pub(crate) fn finish(self) -> BTreeMap<LanguageServerId, LanguageServerName> {
|
||||
self.all_server_ids
|
||||
.into_iter()
|
||||
.filter(|(id, _)| !self.rebased_server_ids.contains(id))
|
||||
.collect()
|
||||
pub(crate) fn finish(
|
||||
self,
|
||||
) -> (
|
||||
LanguageServerTree,
|
||||
BTreeMap<LanguageServerId, LanguageServerName>,
|
||||
) {
|
||||
(
|
||||
self.new_tree,
|
||||
self.all_server_ids
|
||||
.into_iter()
|
||||
.filter(|(id, _)| !self.rebased_server_ids.contains(id))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn server_tree(&mut self) -> &mut LanguageServerTree {
|
||||
|
|
|
@ -84,7 +84,7 @@ use lsp::{
|
|||
};
|
||||
use lsp_command::*;
|
||||
use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle};
|
||||
pub use manifest_tree::ManifestProviders;
|
||||
pub use manifest_tree::ManifestProvidersStore;
|
||||
use node_runtime::NodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
pub use prettier_store::PrettierStore;
|
||||
|
@ -1115,7 +1115,11 @@ impl Project {
|
|||
buffer_store.clone(),
|
||||
worktree_store.clone(),
|
||||
prettier_store.clone(),
|
||||
toolchain_store.clone(),
|
||||
toolchain_store
|
||||
.read(cx)
|
||||
.as_local_store()
|
||||
.expect("Toolchain store to be local")
|
||||
.clone(),
|
||||
environment.clone(),
|
||||
manifest_tree,
|
||||
languages.clone(),
|
||||
|
@ -1260,7 +1264,6 @@ impl Project {
|
|||
LspStore::new_remote(
|
||||
buffer_store.clone(),
|
||||
worktree_store.clone(),
|
||||
Some(toolchain_store.clone()),
|
||||
languages.clone(),
|
||||
ssh_proto.clone(),
|
||||
SSH_PROJECT_ID,
|
||||
|
@ -1485,7 +1488,6 @@ impl Project {
|
|||
let mut lsp_store = LspStore::new_remote(
|
||||
buffer_store.clone(),
|
||||
worktree_store.clone(),
|
||||
None,
|
||||
languages.clone(),
|
||||
client.clone().into(),
|
||||
remote_id,
|
||||
|
@ -3596,16 +3598,10 @@ impl Project {
|
|||
&mut self,
|
||||
abs_path: lsp::Url,
|
||||
language_server_id: LanguageServerId,
|
||||
language_server_name: LanguageServerName,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Entity<Buffer>>> {
|
||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
||||
lsp_store.open_local_buffer_via_lsp(
|
||||
abs_path,
|
||||
language_server_id,
|
||||
language_server_name,
|
||||
cx,
|
||||
)
|
||||
lsp_store.open_local_buffer_via_lsp(abs_path, language_server_id, cx)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ use settings::{
|
|||
SettingsStore, parse_json_with_comments, watch_config_file,
|
||||
};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
|
@ -518,16 +519,15 @@ impl Default for InlineBlameSettings {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)]
|
||||
pub struct BinarySettings {
|
||||
pub path: Option<String>,
|
||||
pub arguments: Option<Vec<String>>,
|
||||
// this can't be an FxHashMap because the extension APIs require the default SipHash
|
||||
pub env: Option<std::collections::HashMap<String, String>>,
|
||||
pub env: Option<BTreeMap<String, String>>,
|
||||
pub ignore_system_version: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct LspSettings {
|
||||
pub binary: Option<BinarySettings>,
|
||||
|
|
|
@ -1099,9 +1099,9 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
|||
let prev_read_dir_count = fs.read_dir_call_count();
|
||||
|
||||
let fake_server = fake_servers.next().await.unwrap();
|
||||
let (server_id, server_name) = lsp_store.read_with(cx, |lsp_store, _| {
|
||||
let (id, status) = lsp_store.language_server_statuses().next().unwrap();
|
||||
(id, status.name.clone())
|
||||
let server_id = lsp_store.read_with(cx, |lsp_store, _| {
|
||||
let (id, _) = lsp_store.language_server_statuses().next().unwrap();
|
||||
id
|
||||
});
|
||||
|
||||
// Simulate jumping to a definition in a dependency outside of the worktree.
|
||||
|
@ -1110,7 +1110,6 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
|||
project.open_local_buffer_via_lsp(
|
||||
lsp::Url::from_file_path(path!("/the-registry/dep1/src/dep1.rs")).unwrap(),
|
||||
server_id,
|
||||
server_name.clone(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
|
|
@ -11,7 +11,10 @@ use collections::BTreeMap;
|
|||
use gpui::{
|
||||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task, WeakEntity,
|
||||
};
|
||||
use language::{LanguageName, LanguageRegistry, LanguageToolchainStore, Toolchain, ToolchainList};
|
||||
use language::{
|
||||
LanguageName, LanguageRegistry, LanguageToolchainStore, ManifestDelegate, Toolchain,
|
||||
ToolchainList,
|
||||
};
|
||||
use rpc::{
|
||||
AnyProtoClient, TypedEnvelope,
|
||||
proto::{self, FromProto, ToProto},
|
||||
|
@ -104,9 +107,11 @@ impl ToolchainStore {
|
|||
cx: &App,
|
||||
) -> Task<Option<Toolchain>> {
|
||||
match &self.0 {
|
||||
ToolchainStoreInner::Local(local, _) => {
|
||||
local.read(cx).active_toolchain(path, language_name, cx)
|
||||
}
|
||||
ToolchainStoreInner::Local(local, _) => Task::ready(local.read(cx).active_toolchain(
|
||||
path.worktree_id,
|
||||
&path.path,
|
||||
language_name,
|
||||
)),
|
||||
ToolchainStoreInner::Remote(remote) => {
|
||||
remote.read(cx).active_toolchain(path, language_name, cx)
|
||||
}
|
||||
|
@ -232,9 +237,15 @@ impl ToolchainStore {
|
|||
ToolchainStoreInner::Remote(remote) => Arc::new(RemoteStore(remote.downgrade())),
|
||||
}
|
||||
}
|
||||
pub fn as_local_store(&self) -> Option<&Entity<LocalToolchainStore>> {
|
||||
match &self.0 {
|
||||
ToolchainStoreInner::Local(local, _) => Some(local),
|
||||
ToolchainStoreInner::Remote(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalToolchainStore {
|
||||
pub struct LocalToolchainStore {
|
||||
languages: Arc<LanguageRegistry>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
project_environment: Entity<ProjectEnvironment>,
|
||||
|
@ -243,20 +254,19 @@ struct LocalToolchainStore {
|
|||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl language::LanguageToolchainStore for LocalStore {
|
||||
async fn active_toolchain(
|
||||
impl language::LocalLanguageToolchainStore for LocalStore {
|
||||
fn active_toolchain(
|
||||
self: Arc<Self>,
|
||||
worktree_id: WorktreeId,
|
||||
path: Arc<Path>,
|
||||
path: &Arc<Path>,
|
||||
language_name: LanguageName,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Option<Toolchain> {
|
||||
self.0
|
||||
.update(cx, |this, cx| {
|
||||
this.active_toolchain(ProjectPath { worktree_id, path }, language_name, cx)
|
||||
.update(cx, |this, _| {
|
||||
this.active_toolchain(worktree_id, path, language_name)
|
||||
})
|
||||
.ok()?
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,19 +289,18 @@ impl language::LanguageToolchainStore for RemoteStore {
|
|||
}
|
||||
|
||||
pub struct EmptyToolchainStore;
|
||||
#[async_trait(?Send)]
|
||||
impl language::LanguageToolchainStore for EmptyToolchainStore {
|
||||
async fn active_toolchain(
|
||||
impl language::LocalLanguageToolchainStore for EmptyToolchainStore {
|
||||
fn active_toolchain(
|
||||
self: Arc<Self>,
|
||||
_: WorktreeId,
|
||||
_: Arc<Path>,
|
||||
_: &Arc<Path>,
|
||||
_: LanguageName,
|
||||
_: &mut AsyncApp,
|
||||
) -> Option<Toolchain> {
|
||||
None
|
||||
}
|
||||
}
|
||||
struct LocalStore(WeakEntity<LocalToolchainStore>);
|
||||
pub(crate) struct LocalStore(WeakEntity<LocalToolchainStore>);
|
||||
struct RemoteStore(WeakEntity<RemoteToolchainStore>);
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -349,17 +358,13 @@ impl LocalToolchainStore {
|
|||
.flatten()?;
|
||||
let worktree_id = snapshot.id();
|
||||
let worktree_root = snapshot.abs_path().to_path_buf();
|
||||
let delegate =
|
||||
Arc::from(ManifestQueryDelegate::new(snapshot)) as Arc<dyn ManifestDelegate>;
|
||||
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,
|
||||
)
|
||||
this.root_for_path(&path, &manifest_name, &delegate, cx)
|
||||
})
|
||||
.ok()?
|
||||
.remove(&manifest_name)
|
||||
.unwrap_or_else(|| ProjectPath {
|
||||
path: Arc::from(Path::new("")),
|
||||
worktree_id,
|
||||
|
@ -394,21 +399,20 @@ impl LocalToolchainStore {
|
|||
}
|
||||
pub(crate) fn active_toolchain(
|
||||
&self,
|
||||
path: ProjectPath,
|
||||
worktree_id: WorktreeId,
|
||||
relative_path: &Arc<Path>,
|
||||
language_name: LanguageName,
|
||||
_: &App,
|
||||
) -> Task<Option<Toolchain>> {
|
||||
let ancestors = path.path.ancestors();
|
||||
Task::ready(
|
||||
self.active_toolchains
|
||||
.get(&(path.worktree_id, language_name))
|
||||
.and_then(|paths| {
|
||||
ancestors
|
||||
.into_iter()
|
||||
.find_map(|root_path| paths.get(root_path))
|
||||
})
|
||||
.cloned(),
|
||||
)
|
||||
) -> Option<Toolchain> {
|
||||
let ancestors = relative_path.ancestors();
|
||||
|
||||
self.active_toolchains
|
||||
.get(&(worktree_id, language_name))
|
||||
.and_then(|paths| {
|
||||
ancestors
|
||||
.into_iter()
|
||||
.find_map(|root_path| paths.get(root_path))
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
struct RemoteToolchainStore {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue