From 05aa8880a478db07ef9478e844ff2a783c024b08 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:22:36 +0100 Subject: [PATCH] project: Track manifest locations per unique manifest locator (#27194) This pull request paves way for exposing manifest tracking to extensions. - Project tree was renamed to manifest tree to better reflect it's intent (and avoid confusion). - Language server adapters now provide a name of their *manifest locator*. If multiple language servers refer to the same locator, the locating code will run just once for a given path. Release Notes: - N/A *or* Added/Fixed/Improved ... --------- Co-authored-by: Anthony --- crates/language/src/language.rs | 27 ++-- crates/language/src/manifest.rs | 48 ++++++ crates/languages/src/lib.rs | 2 + crates/languages/src/rust.rs | 34 +++-- crates/project/src/lsp_store.rs | 6 +- .../src/{project_tree.rs => manifest_tree.rs} | 87 ++++------- .../src/manifest_tree/manifest_store.rs | 48 ++++++ .../path_trie.rs | 2 +- .../server_tree.rs | 138 +++++++++--------- crates/project/src/project.rs | 3 +- 10 files changed, 241 insertions(+), 154 deletions(-) create mode 100644 crates/language/src/manifest.rs rename crates/project/src/{project_tree.rs => manifest_tree.rs} (76%) create mode 100644 crates/project/src/manifest_tree/manifest_store.rs rename crates/project/src/{project_tree => manifest_tree}/path_trie.rs (98%) rename crates/project/src/{project_tree => manifest_tree}/server_tree.rs (81%) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index d4f5ce616d..0a5b24626a 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -11,6 +11,7 @@ mod diagnostic_set; mod highlight_map; mod language_registry; pub mod language_settings; +mod manifest; mod outline; pub mod proto; mod syntax_map; @@ -33,6 +34,7 @@ pub use highlight_map::HighlightMap; use http_client::HttpClient; pub use language_registry::{LanguageName, LoadedLanguage}; use lsp::{CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions}; +pub use manifest::{ManifestName, ManifestProvider, ManifestQuery}; use parking_lot::Mutex; use regex::Regex; use schemars::{ @@ -163,6 +165,7 @@ pub struct CachedLspAdapter { pub adapter: Arc, pub reinstall_attempt_count: AtomicU64, cached_binary: futures::lock::Mutex>, + manifest_name: OnceLock>, attach_kind: OnceLock, } @@ -200,6 +203,7 @@ impl CachedLspAdapter { cached_binary: Default::default(), reinstall_attempt_count: AtomicU64::new(0), attach_kind: Default::default(), + manifest_name: Default::default(), }) } @@ -261,14 +265,10 @@ impl CachedLspAdapter { .cloned() .unwrap_or_else(|| language_name.lsp_id()) } - pub fn find_project_root( - &self, - path: &Path, - ancestor_depth: usize, - delegate: &Arc, - ) -> Option> { - self.adapter - .find_project_root(path, ancestor_depth, delegate) + pub fn manifest_name(&self) -> Option { + self.manifest_name + .get_or_init(|| self.adapter.manifest_name()) + .clone() } pub fn attach_kind(&self) -> Attach { *self.attach_kind.get_or_init(|| self.adapter.attach_kind()) @@ -542,18 +542,13 @@ pub trait LspAdapter: 'static + Send + Sync { fn prepare_initialize_params(&self, original: InitializeParams) -> Result { Ok(original) } + fn attach_kind(&self) -> Attach { Attach::Shared } - fn find_project_root( - &self, - _path: &Path, - _ancestor_depth: usize, - _: &Arc, - ) -> Option> { - // By default all language servers are rooted at the root of the worktree. - Some(Arc::from("".as_ref())) + fn manifest_name(&self) -> Option { + None } /// Method only implemented by the default JSON language server adapter. diff --git a/crates/language/src/manifest.rs b/crates/language/src/manifest.rs new file mode 100644 index 0000000000..f55c875070 --- /dev/null +++ b/crates/language/src/manifest.rs @@ -0,0 +1,48 @@ +use std::{borrow::Borrow, path::Path, sync::Arc}; + +use gpui::SharedString; + +use crate::LspAdapterDelegate; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct ManifestName(SharedString); + +impl Borrow for ManifestName { + fn borrow(&self) -> &SharedString { + &self.0 + } +} + +impl From for ManifestName { + fn from(value: SharedString) -> Self { + Self(value) + } +} + +impl From for SharedString { + fn from(value: ManifestName) -> Self { + value.0 + } +} + +impl AsRef for ManifestName { + fn as_ref(&self) -> &SharedString { + &self.0 + } +} + +/// Represents a manifest query; given a path to a file, [ManifestSearcher] is tasked with finding a path to the directory containing the manifest for that file. +/// +/// Since parts of the path might have already been explored, there's an additional `depth` parameter that indicates to what ancestry level a given path should be explored. +/// For example, given a path like `foo/bar/baz`, a depth of 2 would explore `foo/bar/baz` and `foo/bar`, but not `foo`. +pub struct ManifestQuery { + /// Path to the file, relative to worktree root. + pub path: Arc, + pub depth: usize, + pub delegate: Arc, +} + +pub trait ManifestProvider { + fn name(&self) -> ManifestName; + fn search(&self, query: ManifestQuery) -> Option>; +} diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index a926354a9e..d80ad2bf36 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -2,6 +2,7 @@ use anyhow::Context as _; use gpui::{App, UpdateGlobal}; use json::json_task_context; use node_runtime::NodeRuntime; +use rust::CargoManifestProvider; use rust_embed::RustEmbed; use settings::SettingsStore; use smol::stream::StreamExt; @@ -301,6 +302,7 @@ pub fn init(languages: Arc, node: NodeRuntime, cx: &mut App) { anyhow::Ok(()) }) .detach(); + project::ManifestProviders::global(cx).register(Arc::from(CargoManifestProvider)); } #[derive(Default)] diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 6602cd8bad..a0803c0c42 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -3,7 +3,7 @@ use async_compression::futures::bufread::GzipDecoder; use async_trait::async_trait; use collections::HashMap; use futures::{io::BufReader, StreamExt}; -use gpui::{App, AsyncApp, Task}; +use gpui::{App, AsyncApp, SharedString, Task}; use http_client::github::AssetKind; use http_client::github::{latest_github_release, GitHubLspBinaryVersion}; pub use language::*; @@ -68,20 +68,23 @@ impl RustLspAdapter { } } -#[async_trait(?Send)] -impl LspAdapter for RustLspAdapter { - fn name(&self) -> LanguageServerName { - Self::SERVER_NAME.clone() +pub(crate) struct CargoManifestProvider; + +impl ManifestProvider for CargoManifestProvider { + fn name(&self) -> ManifestName { + SharedString::new_static("Cargo.toml").into() } - fn find_project_root( + fn search( &self, - path: &Path, - ancestor_depth: usize, - delegate: &Arc, + ManifestQuery { + path, + depth, + delegate, + }: ManifestQuery, ) -> Option> { let mut outermost_cargo_toml = None; - for path in path.ancestors().take(ancestor_depth) { + for path in path.ancestors().take(depth) { let p = path.join("Cargo.toml"); if delegate.exists(&p, Some(false)) { outermost_cargo_toml = Some(Arc::from(path)); @@ -90,6 +93,17 @@ impl LspAdapter for RustLspAdapter { outermost_cargo_toml } +} + +#[async_trait(?Send)] +impl LspAdapter for RustLspAdapter { + fn name(&self) -> LanguageServerName { + Self::SERVER_NAME.clone() + } + + fn manifest_name(&self) -> Option { + Some(SharedString::new_static("Cargo.toml").into()) + } async fn check_if_user_installed( &self, diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 61870b464b..68ebf94347 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -6,9 +6,9 @@ use crate::{ buffer_store::{BufferStore, BufferStoreEvent}, environment::ProjectEnvironment, lsp_command::{self, *}, + manifest_tree::{AdapterQuery, LanguageServerTree, LaunchDisposition, ManifestTree}, prettier_store::{self, PrettierStore, PrettierStoreEvent}, project_settings::{LspSettings, ProjectSettings}, - project_tree::{AdapterQuery, LanguageServerTree, LaunchDisposition, ProjectTree}, relativize_path, resolve_path, toolchain_store::{EmptyToolchainStore, ToolchainStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent}, @@ -3349,7 +3349,7 @@ impl LspStore { sender, ) }; - let project_tree = ProjectTree::new(worktree_store.clone(), cx); + let manifest_tree = ManifestTree::new(worktree_store.clone(), cx); Self { mode: LspStoreMode::Local(LocalLspStore { weak: cx.weak_entity(), @@ -3375,7 +3375,7 @@ impl LspStore { _subscription: cx.on_app_quit(|this, cx| { this.as_local_mut().unwrap().shutdown_language_servers(cx) }), - lsp_tree: LanguageServerTree::new(project_tree, languages.clone(), cx), + lsp_tree: LanguageServerTree::new(manifest_tree, languages.clone(), cx), registered_buffers: Default::default(), }), last_formatting_failure: None, diff --git a/crates/project/src/project_tree.rs b/crates/project/src/manifest_tree.rs similarity index 76% rename from crates/project/src/project_tree.rs rename to crates/project/src/manifest_tree.rs index 8e73c8c7d9..fb501980b4 100644 --- a/crates/project/src/project_tree.rs +++ b/crates/project/src/manifest_tree.rs @@ -1,7 +1,9 @@ -//! This module defines a Project Tree. +//! This module defines a Manifest Tree. //! -//! A Project Tree is responsible for determining where the roots of subprojects are located in a project. +//! A Manifest Tree is responsible for determining where the manifests for subprojects are located in a project. +//! This then is used to provide those locations to language servers & determine locations eligible for toolchain selection. +mod manifest_store; mod path_trie; mod server_tree; @@ -14,8 +16,8 @@ use std::{ use collections::HashMap; use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription}; -use language::{CachedLspAdapter, LspAdapterDelegate}; -use lsp::LanguageServerName; +use language::{LspAdapterDelegate, ManifestName, ManifestQuery}; +pub use manifest_store::ManifestProviders; use path_trie::{LabelPresence, RootPathTrie, TriePath}; use settings::{SettingsStore, WorktreeId}; use worktree::{Event as WorktreeEvent, Worktree}; @@ -28,7 +30,7 @@ use crate::{ pub(crate) use server_tree::{AdapterQuery, LanguageServerTree, LaunchDisposition}; struct WorktreeRoots { - roots: RootPathTrie, + roots: RootPathTrie, worktree_store: Entity, _worktree_subscription: Subscription, } @@ -70,55 +72,21 @@ impl WorktreeRoots { } } -pub struct ProjectTree { +pub struct ManifestTree { root_points: HashMap>, worktree_store: Entity, _subscriptions: [Subscription; 2], } -#[derive(Debug, Clone)] -struct AdapterWrapper(Arc); -impl PartialEq for AdapterWrapper { - fn eq(&self, other: &Self) -> bool { - self.0.name.eq(&other.0.name) - } -} - -impl Eq for AdapterWrapper {} - -impl std::hash::Hash for AdapterWrapper { - fn hash(&self, state: &mut H) { - self.0.name.hash(state); - } -} - -impl PartialOrd for AdapterWrapper { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.0.name.cmp(&other.0.name)) - } -} - -impl Ord for AdapterWrapper { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.name.cmp(&other.0.name) - } -} - -impl Borrow for AdapterWrapper { - fn borrow(&self) -> &LanguageServerName { - &self.0.name - } -} - #[derive(PartialEq)] -pub(crate) enum ProjectTreeEvent { +pub(crate) enum ManifestTreeEvent { WorktreeRemoved(WorktreeId), Cleared, } -impl EventEmitter for ProjectTree {} +impl EventEmitter for ManifestTree {} -impl ProjectTree { +impl ManifestTree { pub(crate) fn new(worktree_store: Entity, cx: &mut App) -> Entity { cx.new(|cx| Self { root_points: Default::default(), @@ -130,26 +98,22 @@ impl ProjectTree { worktree_roots.roots = RootPathTrie::new(); }) } - cx.emit(ProjectTreeEvent::Cleared); + cx.emit(ManifestTreeEvent::Cleared); }), ], worktree_store, }) } - #[allow(clippy::mutable_key_type)] fn root_for_path( &mut self, ProjectPath { worktree_id, path }: ProjectPath, - adapters: Vec>, + manifests: &mut dyn Iterator, delegate: Arc, cx: &mut App, - ) -> BTreeMap { + ) -> BTreeMap { debug_assert_eq!(delegate.worktree_id(), worktree_id); - #[allow(clippy::mutable_key_type)] let mut roots = BTreeMap::from_iter( - adapters - .into_iter() - .map(|adapter| (AdapterWrapper(adapter), (None, LabelPresence::KnownAbsent))), + manifests.map(|manifest| (manifest, (None, LabelPresence::KnownAbsent))), ); let worktree_roots = match self.root_points.entry(worktree_id) { Entry::Occupied(occupied_entry) => occupied_entry.get().clone(), @@ -182,7 +146,8 @@ impl ProjectTree { ControlFlow::Continue(()) }); }); - for (adapter, (root_path, presence)) in &mut roots { + + for (manifest_name, (root_path, presence)) in &mut roots { if *presence == LabelPresence::Present { continue; } @@ -198,12 +163,22 @@ impl ProjectTree { .unwrap_or_else(|| path.components().count() + 1); if depth > 0 { - let root = adapter.0.find_project_root(&path, depth, &delegate); + let Some(provider) = ManifestProviders::global(cx).get(manifest_name.borrow()) + else { + log::warn!("Manifest provider `{}` not found", manifest_name.as_ref()); + continue; + }; + + let root = provider.search(ManifestQuery { + path: path.clone(), + depth, + delegate: delegate.clone(), + }); match root { Some(known_root) => worktree_roots.update(cx, |this, _| { let root = TriePath::from(&*known_root); this.roots - .insert(&root, adapter.0.name(), LabelPresence::Present); + .insert(&root, manifest_name.clone(), LabelPresence::Present); *presence = LabelPresence::Present; *root_path = Some(ProjectPath { worktree_id, @@ -212,7 +187,7 @@ impl ProjectTree { }), None => worktree_roots.update(cx, |this, _| { this.roots - .insert(&key, adapter.0.name(), LabelPresence::KnownAbsent); + .insert(&key, manifest_name.clone(), LabelPresence::KnownAbsent); }), } } @@ -235,7 +210,7 @@ impl ProjectTree { match evt { WorktreeStoreEvent::WorktreeRemoved(_, worktree_id) => { self.root_points.remove(&worktree_id); - cx.emit(ProjectTreeEvent::WorktreeRemoved(*worktree_id)); + cx.emit(ManifestTreeEvent::WorktreeRemoved(*worktree_id)); } _ => {} } diff --git a/crates/project/src/manifest_tree/manifest_store.rs b/crates/project/src/manifest_tree/manifest_store.rs new file mode 100644 index 0000000000..0462b25798 --- /dev/null +++ b/crates/project/src/manifest_tree/manifest_store.rs @@ -0,0 +1,48 @@ +use collections::HashMap; +use gpui::{App, Global, SharedString}; +use parking_lot::RwLock; +use std::{ops::Deref, sync::Arc}; + +use language::{ManifestName, ManifestProvider}; + +#[derive(Default)] +struct ManifestProvidersState { + providers: HashMap>, +} + +#[derive(Clone, Default)] +pub struct ManifestProviders(Arc>); + +#[derive(Default)] +struct GlobalManifestProvider(ManifestProviders); + +impl Deref for GlobalManifestProvider { + type Target = ManifestProviders; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Global for GlobalManifestProvider {} + +impl ManifestProviders { + /// Returns the global [`ManifestStore`]. + /// + /// Inserts a default [`ManifestStore`] if one does not yet exist. + pub fn global(cx: &mut App) -> Self { + cx.default_global::().0.clone() + } + + pub fn register(&self, provider: Arc) { + self.0.write().providers.insert(provider.name(), provider); + } + + pub fn unregister(&self, name: &SharedString) { + self.0.write().providers.remove(name); + } + + pub(super) fn get(&self, name: &SharedString) -> Option> { + self.0.read().providers.get(name).cloned() + } +} diff --git a/crates/project/src/project_tree/path_trie.rs b/crates/project/src/manifest_tree/path_trie.rs similarity index 98% rename from crates/project/src/project_tree/path_trie.rs rename to crates/project/src/manifest_tree/path_trie.rs index 1c70efe7b5..8151b39e4d 100644 --- a/crates/project/src/project_tree/path_trie.rs +++ b/crates/project/src/manifest_tree/path_trie.rs @@ -6,7 +6,7 @@ use std::{ sync::Arc, }; -/// [RootPathTrie] is a workhorse of [super::ProjectTree]. It is responsible for determining the closest known project root for a given path. +/// [RootPathTrie] is a workhorse of [super::ManifestTree]. It is responsible for determining the closest known project root for a given path. /// It also determines how much of a given path is unexplored, thus letting callers fill in that gap if needed. /// Conceptually, it allows one to annotate Worktree entries with arbitrary extra metadata and run closest-ancestor searches. /// diff --git a/crates/project/src/project_tree/server_tree.rs b/crates/project/src/manifest_tree/server_tree.rs similarity index 81% rename from crates/project/src/project_tree/server_tree.rs rename to crates/project/src/manifest_tree/server_tree.rs index fa1221637e..8aba35a03e 100644 --- a/crates/project/src/project_tree/server_tree.rs +++ b/crates/project/src/manifest_tree/server_tree.rs @@ -6,7 +6,6 @@ //! 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. -//! This module defines a Project Tree. use std::{ collections::{BTreeMap, BTreeSet}, @@ -16,10 +15,9 @@ use std::{ use collections::{HashMap, IndexMap}; use gpui::{App, AppContext as _, Entity, Subscription}; -use itertools::Itertools; use language::{ - language_settings::AllLanguageSettings, Attach, LanguageName, LanguageRegistry, - LspAdapterDelegate, + language_settings::AllLanguageSettings, Attach, CachedLspAdapter, LanguageName, + LanguageRegistry, LspAdapterDelegate, }; use lsp::LanguageServerName; use settings::{Settings, SettingsLocation, WorktreeId}; @@ -27,7 +25,7 @@ use std::sync::OnceLock; use crate::{project_settings::LspSettings, LanguageServerId, ProjectPath}; -use super::{AdapterWrapper, ProjectTree, ProjectTreeEvent}; +use super::{ManifestTree, ManifestTreeEvent}; #[derive(Debug, Default)] struct ServersForWorktree { @@ -38,7 +36,7 @@ struct ServersForWorktree { } pub struct LanguageServerTree { - project_tree: Entity, + manifest_tree: Entity, instances: BTreeMap, attach_kind_cache: HashMap, languages: Arc, @@ -133,30 +131,20 @@ pub(crate) enum AdapterQuery<'a> { impl LanguageServerTree { pub(crate) fn new( - project_tree: Entity, + manifest_tree: Entity, languages: Arc, cx: &mut App, ) -> Entity { cx.new(|cx| Self { - _subscriptions: cx.subscribe( - &project_tree, - |_: &mut Self, _, event, _| { - if event == &ProjectTreeEvent::Cleared {} - }, - ), - project_tree, + _subscriptions: cx.subscribe(&manifest_tree, |_: &mut Self, _, event, _| { + if event == &ManifestTreeEvent::Cleared {} + }), + manifest_tree, instances: Default::default(), attach_kind_cache: Default::default(), languages, }) } - /// Memoize calls to attach_kind on LspAdapter (which might be a WASM extension, thus ~expensive to call). - fn attach_kind(&mut self, adapter: &AdapterWrapper) -> Attach { - *self - .attach_kind_cache - .entry(adapter.0.name.clone()) - .or_insert_with(|| adapter.0.attach_kind()) - } /// 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>( @@ -174,10 +162,14 @@ impl LanguageServerTree { 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| (adapter, (LspSettings::default(), BTreeSet::new()))), - ), + AdapterQuery::Adapter(language_server_name) => { + IndexMap::from_iter(self.adapter_for_name(language_server_name).map(|adapter| { + ( + adapter.name(), + (LspSettings::default(), BTreeSet::new(), adapter), + ) + })) + } }; self.get_with_adapters(path, adapters, delegate, cx) } @@ -185,41 +177,48 @@ impl LanguageServerTree { fn get_with_adapters<'a>( &'a mut self, path: ProjectPath, - adapters: IndexMap)>, + adapters: IndexMap< + LanguageServerName, + (LspSettings, BTreeSet, Arc), + >, delegate: Arc, cx: &mut App, ) -> impl Iterator + 'a { let worktree_id = path.worktree_id; - #[allow(clippy::mutable_key_type)] - let mut roots = self.project_tree.update(cx, |this, cx| { + + 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, - adapters - .iter() - .map(|(adapter, _)| adapter.0.clone()) - .collect(), + &mut manifest_to_adapters.keys().cloned(), delegate, cx, ) }); - let mut root_path = None; - // 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. - for (adapter, _) in adapters.iter() { - roots.entry(adapter.clone()).or_insert_with(|| { - root_path - .get_or_insert_with(|| ProjectPath { - worktree_id, - path: Arc::from("".as_ref()), - }) - .clone() - }); - } - - roots + let root_path = std::cell::LazyCell::new(move || ProjectPath { + worktree_id, + path: Arc::from("".as_ref()), + }); + adapters .into_iter() - .filter_map(move |(adapter, root_path)| { - let attach = self.attach_kind(&adapter); - let (index, _, (settings, new_languages)) = adapters.get_full(&adapter)?; + .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 attach = adapter.attach_kind(); + let inner_node = self .instances .entry(root_path.worktree_id) @@ -227,27 +226,25 @@ impl LanguageServerTree { .roots .entry(root_path.path.clone()) .or_default() - .entry(adapter.0.name.clone()); - let (node, languages) = inner_node.or_insert_with(move || { + .entry(adapter.name()); + let (node, languages) = inner_node.or_insert_with(|| { ( Arc::new(InnerTreeNode::new( - adapter.0.name(), + adapter.name(), attach, - root_path, + root_path.clone(), settings.clone(), )), Default::default(), ) }); languages.extend(new_languages.iter().cloned()); - Some((index, Arc::downgrade(&node).into())) + Arc::downgrade(&node).into() }) - .sorted_by_key(|(index, _)| *index) - .map(|(_, node)| node) } - fn adapter_for_name(&self, name: &LanguageServerName) -> Option { - self.languages.adapter_for_name(name).map(AdapterWrapper) + fn adapter_for_name(&self, name: &LanguageServerName) -> Option> { + self.languages.adapter_for_name(name) } fn adapters_for_language( @@ -255,7 +252,8 @@ impl LanguageServerTree { settings_location: SettingsLocation, language_name: &LanguageName, cx: &App, - ) -> IndexMap)> { + ) -> IndexMap, Arc)> + { let settings = AllLanguageSettings::get(Some(settings_location), cx).language( Some(settings_location), Some(language_name), @@ -297,10 +295,11 @@ impl LanguageServerTree { .cloned() .unwrap_or_default(); Some(( - AdapterWrapper(adapter), + adapter.name(), ( adapter_settings, BTreeSet::from_iter([language_name.clone()]), + adapter, ), )) }) @@ -314,8 +313,8 @@ impl LanguageServerTree { self.languages.reorder_language_servers( &language_name, adapters_with_settings - .keys() - .map(|wrapper| wrapper.0.clone()) + .values() + .map(|(_, _, adapter)| adapter.clone()) .collect(), ); @@ -392,11 +391,16 @@ impl<'tree> ServerTreeRebase<'tree> { 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, (LspSettings::default(), BTreeSet::new()))), - ), + 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), + ) + }, + )) + } }; self.new_tree diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index caea2eeffa..f727413fc8 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -7,9 +7,9 @@ pub mod git_store; pub mod image_store; pub mod lsp_command; pub mod lsp_store; +mod manifest_tree; pub mod prettier_store; pub mod project_settings; -mod project_tree; pub mod search; mod task_inventory; pub mod task_store; @@ -73,6 +73,7 @@ use lsp::{ }; use lsp_command::*; use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle}; +pub use manifest_tree::ManifestProviders; use node_runtime::NodeRuntime; use parking_lot::Mutex; pub use prettier_store::PrettierStore;