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:
Piotr Osiewicz 2025-08-18 11:43:52 +02:00 committed by GitHub
parent 42ffa8900a
commit b8a106632f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 1037 additions and 1085 deletions

View file

@ -16022,38 +16022,24 @@ impl Editor {
cx.spawn_in(window, async move |editor, cx| { cx.spawn_in(window, async move |editor, cx| {
let location_task = editor.update(cx, |_, cx| { let location_task = editor.update(cx, |_, cx| {
project.update(cx, |project, cx| { project.update(cx, |project, cx| {
let language_server_name = project project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
.language_server_statuses(cx)
.find(|(id, _)| server_id == *id)
.map(|(_, status)| status.name.clone());
language_server_name.map(|language_server_name| {
project.open_local_buffer_via_lsp(
lsp_location.uri.clone(),
server_id,
language_server_name,
cx,
)
})
}) })
})?; })?;
let location = match location_task { let location = Some({
Some(task) => Some({ let target_buffer_handle = location_task.await.context("open local buffer")?;
let target_buffer_handle = task.await.context("open local buffer")?; let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
let range = target_buffer_handle.read_with(cx, |target_buffer, _| { let target_start = target_buffer
let target_start = target_buffer .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
.clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left); let target_end = target_buffer
let target_end = target_buffer .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
.clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left); target_buffer.anchor_after(target_start)
target_buffer.anchor_after(target_start) ..target_buffer.anchor_before(target_end)
..target_buffer.anchor_before(target_end) })?;
})?; Location {
Location { buffer: target_buffer_handle,
buffer: target_buffer_handle, range,
range, }
} });
}),
None => None,
};
Ok(location) Ok(location)
}) })
} }

View file

@ -1275,6 +1275,7 @@ impl ExtensionStore {
queries, queries,
context_provider, context_provider,
toolchain_provider: None, toolchain_provider: None,
manifest_name: None,
}) })
}), }),
); );

View file

@ -163,6 +163,7 @@ impl HeadlessExtensionStore {
queries: LanguageQueries::default(), queries: LanguageQueries::default(),
context_provider: None, context_provider: None,
toolchain_provider: None, toolchain_provider: None,
manifest_name: None,
}) })
}), }),
); );

View file

@ -938,7 +938,7 @@ impl ExtensionImports for WasmState {
binary: settings.binary.map(|binary| settings::CommandSettings { binary: settings.binary.map(|binary| settings::CommandSettings {
path: binary.path, path: binary.path,
arguments: binary.arguments, arguments: binary.arguments,
env: binary.env, env: binary.env.map(|env| env.into_iter().collect()),
}), }),
settings: settings.settings, settings: settings.settings,
initialization_options: settings.initialization_options, initialization_options: settings.initialization_options,

View file

@ -1571,6 +1571,7 @@ impl Buffer {
diagnostics: diagnostics.iter().cloned().collect(), diagnostics: diagnostics.iter().cloned().collect(),
lamport_timestamp, lamport_timestamp,
}; };
self.apply_diagnostic_update(server_id, diagnostics, lamport_timestamp, cx); self.apply_diagnostic_update(server_id, diagnostics, lamport_timestamp, cx);
self.send_operation(op, true, cx); self.send_operation(op, true, cx);
} }

View file

@ -44,6 +44,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use serde_json::Value; use serde_json::Value;
use settings::WorktreeId; use settings::WorktreeId;
use smol::future::FutureExt as _; use smol::future::FutureExt as _;
use std::num::NonZeroU32;
use std::{ use std::{
any::Any, any::Any,
ffi::OsStr, ffi::OsStr,
@ -59,7 +60,6 @@ use std::{
atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst}, atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},
}, },
}; };
use std::{num::NonZeroU32, sync::OnceLock};
use syntax_map::{QueryCursorHandle, SyntaxSnapshot}; use syntax_map::{QueryCursorHandle, SyntaxSnapshot};
use task::RunnableTag; use task::RunnableTag;
pub use task_context::{ContextLocation, ContextProvider, RunnableRange}; pub use task_context::{ContextLocation, ContextProvider, RunnableRange};
@ -67,7 +67,9 @@ pub use text_diff::{
DiffOptions, apply_diff_patch, line_diff, text_diff, text_diff_with_options, unified_diff, DiffOptions, apply_diff_patch, line_diff, text_diff, text_diff_with_options, unified_diff,
}; };
use theme::SyntaxTheme; use theme::SyntaxTheme;
pub use toolchain::{LanguageToolchainStore, Toolchain, ToolchainList, ToolchainLister}; pub use toolchain::{
LanguageToolchainStore, LocalLanguageToolchainStore, Toolchain, ToolchainList, ToolchainLister,
};
use tree_sitter::{self, Query, QueryCursor, WasmStore, wasmtime}; use tree_sitter::{self, Query, QueryCursor, WasmStore, wasmtime};
use util::serde::default_true; use util::serde::default_true;
@ -165,7 +167,6 @@ pub struct CachedLspAdapter {
pub adapter: Arc<dyn LspAdapter>, pub adapter: Arc<dyn LspAdapter>,
pub reinstall_attempt_count: AtomicU64, pub reinstall_attempt_count: AtomicU64,
cached_binary: futures::lock::Mutex<Option<LanguageServerBinary>>, cached_binary: futures::lock::Mutex<Option<LanguageServerBinary>>,
manifest_name: OnceLock<Option<ManifestName>>,
} }
impl Debug for CachedLspAdapter { impl Debug for CachedLspAdapter {
@ -201,7 +202,6 @@ impl CachedLspAdapter {
adapter, adapter,
cached_binary: Default::default(), cached_binary: Default::default(),
reinstall_attempt_count: AtomicU64::new(0), reinstall_attempt_count: AtomicU64::new(0),
manifest_name: Default::default(),
}) })
} }
@ -212,7 +212,7 @@ impl CachedLspAdapter {
pub async fn get_language_server_command( pub async fn get_language_server_command(
self: Arc<Self>, self: Arc<Self>,
delegate: Arc<dyn LspAdapterDelegate>, delegate: Arc<dyn LspAdapterDelegate>,
toolchains: Arc<dyn LanguageToolchainStore>, toolchains: Option<Toolchain>,
binary_options: LanguageServerBinaryOptions, binary_options: LanguageServerBinaryOptions,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<LanguageServerBinary> { ) -> Result<LanguageServerBinary> {
@ -281,12 +281,6 @@ impl CachedLspAdapter {
.cloned() .cloned()
.unwrap_or_else(|| language_name.lsp_id()) .unwrap_or_else(|| language_name.lsp_id())
} }
pub fn manifest_name(&self) -> Option<ManifestName> {
self.manifest_name
.get_or_init(|| self.adapter.manifest_name())
.clone()
}
} }
/// Determines what gets sent out as a workspace folders content /// Determines what gets sent out as a workspace folders content
@ -327,7 +321,7 @@ pub trait LspAdapter: 'static + Send + Sync {
fn get_language_server_command<'a>( fn get_language_server_command<'a>(
self: Arc<Self>, self: Arc<Self>,
delegate: Arc<dyn LspAdapterDelegate>, delegate: Arc<dyn LspAdapterDelegate>,
toolchains: Arc<dyn LanguageToolchainStore>, toolchains: Option<Toolchain>,
binary_options: LanguageServerBinaryOptions, binary_options: LanguageServerBinaryOptions,
mut cached_binary: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>, mut cached_binary: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
cx: &'a mut AsyncApp, cx: &'a mut AsyncApp,
@ -402,7 +396,7 @@ pub trait LspAdapter: 'static + Send + Sync {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
_: &dyn LspAdapterDelegate, _: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
None None
@ -535,7 +529,7 @@ pub trait LspAdapter: 'static + Send + Sync {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
_: &Arc<dyn LspAdapterDelegate>, _: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_cx: &mut AsyncApp, _cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
Ok(serde_json::json!({})) Ok(serde_json::json!({}))
@ -555,7 +549,6 @@ pub trait LspAdapter: 'static + Send + Sync {
_target_language_server_id: LanguageServerName, _target_language_server_id: LanguageServerName,
_: &dyn Fs, _: &dyn Fs,
_: &Arc<dyn LspAdapterDelegate>, _: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>,
_cx: &mut AsyncApp, _cx: &mut AsyncApp,
) -> Result<Option<Value>> { ) -> Result<Option<Value>> {
Ok(None) Ok(None)
@ -594,10 +587,6 @@ pub trait LspAdapter: 'static + Send + Sync {
WorkspaceFoldersContent::SubprojectRoots WorkspaceFoldersContent::SubprojectRoots
} }
fn manifest_name(&self) -> Option<ManifestName> {
None
}
/// Method only implemented by the default JSON language server adapter. /// Method only implemented by the default JSON language server adapter.
/// Used to provide dynamic reloading of the JSON schemas used to /// Used to provide dynamic reloading of the JSON schemas used to
/// provide autocompletion and diagnostics in Zed setting and keybind /// provide autocompletion and diagnostics in Zed setting and keybind
@ -1108,6 +1097,7 @@ pub struct Language {
pub(crate) grammar: Option<Arc<Grammar>>, pub(crate) grammar: Option<Arc<Grammar>>,
pub(crate) context_provider: Option<Arc<dyn ContextProvider>>, pub(crate) context_provider: Option<Arc<dyn ContextProvider>>,
pub(crate) toolchain: Option<Arc<dyn ToolchainLister>>, pub(crate) toolchain: Option<Arc<dyn ToolchainLister>>,
pub(crate) manifest_name: Option<ManifestName>,
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
@ -1318,6 +1308,7 @@ impl Language {
}), }),
context_provider: None, context_provider: None,
toolchain: None, toolchain: None,
manifest_name: None,
} }
} }
@ -1331,6 +1322,10 @@ impl Language {
self self
} }
pub fn with_manifest(mut self, name: Option<ManifestName>) -> Self {
self.manifest_name = name;
self
}
pub fn with_queries(mut self, queries: LanguageQueries) -> Result<Self> { pub fn with_queries(mut self, queries: LanguageQueries) -> Result<Self> {
if let Some(query) = queries.highlights { if let Some(query) = queries.highlights {
self = self self = self
@ -1764,6 +1759,9 @@ impl Language {
pub fn name(&self) -> LanguageName { pub fn name(&self) -> LanguageName {
self.config.name.clone() self.config.name.clone()
} }
pub fn manifest(&self) -> Option<&ManifestName> {
self.manifest_name.as_ref()
}
pub fn code_fence_block_name(&self) -> Arc<str> { pub fn code_fence_block_name(&self) -> Arc<str> {
self.config self.config
@ -2209,7 +2207,7 @@ impl LspAdapter for FakeLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
_: &dyn LspAdapterDelegate, _: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
Some(self.language_server_binary.clone()) Some(self.language_server_binary.clone())
@ -2218,7 +2216,7 @@ impl LspAdapter for FakeLspAdapter {
fn get_language_server_command<'a>( fn get_language_server_command<'a>(
self: Arc<Self>, self: Arc<Self>,
_: Arc<dyn LspAdapterDelegate>, _: Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: LanguageServerBinaryOptions, _: LanguageServerBinaryOptions,
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>, _: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
_: &'a mut AsyncApp, _: &'a mut AsyncApp,

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher, CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher,
LanguageServerName, LspAdapter, PLAIN_TEXT, ToolchainLister, LanguageServerName, LspAdapter, ManifestName, PLAIN_TEXT, ToolchainLister,
language_settings::{ language_settings::{
AllLanguageSettingsContent, LanguageSettingsContent, all_language_settings, AllLanguageSettingsContent, LanguageSettingsContent, all_language_settings,
}, },
@ -172,6 +172,7 @@ pub struct AvailableLanguage {
hidden: bool, hidden: bool,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>, load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
loaded: bool, loaded: bool,
manifest_name: Option<ManifestName>,
} }
impl AvailableLanguage { impl AvailableLanguage {
@ -259,6 +260,7 @@ pub struct LoadedLanguage {
pub queries: LanguageQueries, pub queries: LanguageQueries,
pub context_provider: Option<Arc<dyn ContextProvider>>, pub context_provider: Option<Arc<dyn ContextProvider>>,
pub toolchain_provider: Option<Arc<dyn ToolchainLister>>, pub toolchain_provider: Option<Arc<dyn ToolchainLister>>,
pub manifest_name: Option<ManifestName>,
} }
impl LanguageRegistry { impl LanguageRegistry {
@ -349,12 +351,14 @@ impl LanguageRegistry {
config.grammar.clone(), config.grammar.clone(),
config.matcher.clone(), config.matcher.clone(),
config.hidden, config.hidden,
None,
Arc::new(move || { Arc::new(move || {
Ok(LoadedLanguage { Ok(LoadedLanguage {
config: config.clone(), config: config.clone(),
queries: Default::default(), queries: Default::default(),
toolchain_provider: None, toolchain_provider: None,
context_provider: None, context_provider: None,
manifest_name: None,
}) })
}), }),
) )
@ -487,6 +491,7 @@ impl LanguageRegistry {
grammar_name: Option<Arc<str>>, grammar_name: Option<Arc<str>>,
matcher: LanguageMatcher, matcher: LanguageMatcher,
hidden: bool, hidden: bool,
manifest_name: Option<ManifestName>,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>, load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
) { ) {
let state = &mut *self.state.write(); let state = &mut *self.state.write();
@ -496,6 +501,7 @@ impl LanguageRegistry {
existing_language.grammar = grammar_name; existing_language.grammar = grammar_name;
existing_language.matcher = matcher; existing_language.matcher = matcher;
existing_language.load = load; existing_language.load = load;
existing_language.manifest_name = manifest_name;
return; return;
} }
} }
@ -508,6 +514,7 @@ impl LanguageRegistry {
load, load,
hidden, hidden,
loaded: false, loaded: false,
manifest_name,
}); });
state.version += 1; state.version += 1;
state.reload_count += 1; state.reload_count += 1;
@ -575,6 +582,7 @@ impl LanguageRegistry {
grammar: language.config.grammar.clone(), grammar: language.config.grammar.clone(),
matcher: language.config.matcher.clone(), matcher: language.config.matcher.clone(),
hidden: language.config.hidden, hidden: language.config.hidden,
manifest_name: None,
load: Arc::new(|| Err(anyhow!("already loaded"))), load: Arc::new(|| Err(anyhow!("already loaded"))),
loaded: true, loaded: true,
}); });
@ -914,10 +922,12 @@ impl LanguageRegistry {
Language::new_with_id(id, loaded_language.config, grammar) Language::new_with_id(id, loaded_language.config, grammar)
.with_context_provider(loaded_language.context_provider) .with_context_provider(loaded_language.context_provider)
.with_toolchain_lister(loaded_language.toolchain_provider) .with_toolchain_lister(loaded_language.toolchain_provider)
.with_manifest(loaded_language.manifest_name)
.with_queries(loaded_language.queries) .with_queries(loaded_language.queries)
} else { } else {
Ok(Language::new_with_id(id, loaded_language.config, None) Ok(Language::new_with_id(id, loaded_language.config, None)
.with_context_provider(loaded_language.context_provider) .with_context_provider(loaded_language.context_provider)
.with_manifest(loaded_language.manifest_name)
.with_toolchain_lister(loaded_language.toolchain_provider)) .with_toolchain_lister(loaded_language.toolchain_provider))
} }
} }

View file

@ -12,6 +12,12 @@ impl Borrow<SharedString> for ManifestName {
} }
} }
impl Borrow<str> for ManifestName {
fn borrow(&self) -> &str {
&self.0
}
}
impl From<SharedString> for ManifestName { impl From<SharedString> for ManifestName {
fn from(value: SharedString) -> Self { fn from(value: SharedString) -> Self {
Self(value) Self(value)

View file

@ -17,7 +17,7 @@ use settings::WorktreeId;
use crate::{LanguageName, ManifestName}; use crate::{LanguageName, ManifestName};
/// Represents a single toolchain. /// Represents a single toolchain.
#[derive(Clone, Debug)] #[derive(Clone, Debug, Eq)]
pub struct Toolchain { pub struct Toolchain {
/// User-facing label /// User-facing label
pub name: SharedString, pub name: SharedString,
@ -27,6 +27,14 @@ pub struct Toolchain {
pub as_json: serde_json::Value, pub as_json: serde_json::Value,
} }
impl std::hash::Hash for Toolchain {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.path.hash(state);
self.language_name.hash(state);
}
}
impl PartialEq for Toolchain { impl PartialEq for Toolchain {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
// Do not use as_json for comparisons; it shouldn't impact equality, as it's not user-surfaced. // Do not use as_json for comparisons; it shouldn't impact equality, as it's not user-surfaced.
@ -64,6 +72,29 @@ pub trait LanguageToolchainStore: Send + Sync + 'static {
) -> Option<Toolchain>; ) -> Option<Toolchain>;
} }
pub trait LocalLanguageToolchainStore: Send + Sync + 'static {
fn active_toolchain(
self: Arc<Self>,
worktree_id: WorktreeId,
relative_path: &Arc<Path>,
language_name: LanguageName,
cx: &mut AsyncApp,
) -> Option<Toolchain>;
}
#[async_trait(?Send )]
impl<T: LocalLanguageToolchainStore> LanguageToolchainStore for T {
async fn active_toolchain(
self: Arc<Self>,
worktree_id: WorktreeId,
relative_path: Arc<Path>,
language_name: LanguageName,
cx: &mut AsyncApp,
) -> Option<Toolchain> {
self.active_toolchain(worktree_id, &relative_path, language_name, cx)
}
}
type DefaultIndex = usize; type DefaultIndex = usize;
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct ToolchainList { pub struct ToolchainList {

View file

@ -12,8 +12,8 @@ use fs::Fs;
use futures::{Future, FutureExt, future::join_all}; use futures::{Future, FutureExt, future::join_all};
use gpui::{App, AppContext, AsyncApp, Task}; use gpui::{App, AppContext, AsyncApp, Task};
use language::{ use language::{
BinaryStatus, CodeLabel, HighlightId, Language, LanguageName, LanguageToolchainStore, BinaryStatus, CodeLabel, HighlightId, Language, LanguageName, LspAdapter, LspAdapterDelegate,
LspAdapter, LspAdapterDelegate, Toolchain,
}; };
use lsp::{ use lsp::{
CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName, CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName,
@ -159,7 +159,7 @@ impl LspAdapter for ExtensionLspAdapter {
fn get_language_server_command<'a>( fn get_language_server_command<'a>(
self: Arc<Self>, self: Arc<Self>,
delegate: Arc<dyn LspAdapterDelegate>, delegate: Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: LanguageServerBinaryOptions, _: LanguageServerBinaryOptions,
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>, _: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
_: &'a mut AsyncApp, _: &'a mut AsyncApp,
@ -288,7 +288,7 @@ impl LspAdapter for ExtensionLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_cx: &mut AsyncApp, _cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _;
@ -336,7 +336,7 @@ impl LspAdapter for ExtensionLspAdapter {
target_language_server_id: LanguageServerName, target_language_server_id: LanguageServerName,
_: &dyn Fs, _: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>,
_cx: &mut AsyncApp, _cx: &mut AsyncApp,
) -> Result<Option<serde_json::Value>> { ) -> Result<Option<serde_json::Value>> {
let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _;

View file

@ -52,7 +52,7 @@ impl ExtensionLanguageProxy for LanguageServerRegistryProxy {
load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>, load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
) { ) {
self.language_registry self.language_registry
.register_language(language, grammar, matcher, hidden, load); .register_language(language, grammar, matcher, hidden, None, load);
} }
fn remove_languages( fn remove_languages(

View file

@ -28,7 +28,7 @@ impl super::LspAdapter for CLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?; let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;

View file

@ -2,7 +2,7 @@ use anyhow::{Context as _, Result};
use async_trait::async_trait; use async_trait::async_trait;
use futures::StreamExt; use futures::StreamExt;
use gpui::AsyncApp; use gpui::AsyncApp;
use language::{LanguageToolchainStore, LspAdapter, LspAdapterDelegate}; use language::{LspAdapter, LspAdapterDelegate, Toolchain};
use lsp::{LanguageServerBinary, LanguageServerName}; use lsp::{LanguageServerBinary, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy}; use node_runtime::{NodeRuntime, VersionStrategy};
use project::{Fs, lsp_store::language_server_settings}; use project::{Fs, lsp_store::language_server_settings};
@ -43,7 +43,7 @@ impl LspAdapter for CssLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let path = delegate let path = delegate
@ -144,7 +144,7 @@ impl LspAdapter for CssLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<serde_json::Value> { ) -> Result<serde_json::Value> {
let mut default_config = json!({ let mut default_config = json!({

View file

@ -75,7 +75,7 @@ impl super::LspAdapter for GoLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?; let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;

View file

@ -8,8 +8,8 @@ use futures::StreamExt;
use gpui::{App, AsyncApp, Task}; use gpui::{App, AsyncApp, Task};
use http_client::github::{GitHubLspBinaryVersion, latest_github_release}; use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
use language::{ use language::{
ContextProvider, LanguageName, LanguageRegistry, LanguageToolchainStore, LocalFile as _, ContextProvider, LanguageName, LanguageRegistry, LocalFile as _, LspAdapter,
LspAdapter, LspAdapterDelegate, LspAdapterDelegate, Toolchain,
}; };
use lsp::{LanguageServerBinary, LanguageServerName}; use lsp::{LanguageServerBinary, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy}; use node_runtime::{NodeRuntime, VersionStrategy};
@ -303,7 +303,7 @@ impl LspAdapter for JsonLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let path = delegate let path = delegate
@ -404,7 +404,7 @@ impl LspAdapter for JsonLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let mut config = self.get_or_init_workspace_config(cx).await?; let mut config = self.get_or_init_workspace_config(cx).await?;
@ -529,7 +529,7 @@ impl LspAdapter for NodeVersionAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?; let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;

View file

@ -1,6 +1,6 @@
use anyhow::Context as _; use anyhow::Context as _;
use feature_flags::{FeatureFlag, FeatureFlagAppExt as _}; use feature_flags::{FeatureFlag, FeatureFlagAppExt as _};
use gpui::{App, UpdateGlobal}; use gpui::{App, SharedString, UpdateGlobal};
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use python::PyprojectTomlManifestProvider; use python::PyprojectTomlManifestProvider;
use rust::CargoManifestProvider; use rust::CargoManifestProvider;
@ -177,11 +177,13 @@ pub fn init(languages: Arc<LanguageRegistry>, node: NodeRuntime, cx: &mut App) {
adapters: vec![python_lsp_adapter.clone(), py_lsp_adapter.clone()], adapters: vec![python_lsp_adapter.clone(), py_lsp_adapter.clone()],
context: Some(python_context_provider), context: Some(python_context_provider),
toolchain: Some(python_toolchain_provider), toolchain: Some(python_toolchain_provider),
manifest_name: Some(SharedString::new_static("pyproject.toml").into()),
}, },
LanguageInfo { LanguageInfo {
name: "rust", name: "rust",
adapters: vec![rust_lsp_adapter], adapters: vec![rust_lsp_adapter],
context: Some(rust_context_provider), context: Some(rust_context_provider),
manifest_name: Some(SharedString::new_static("Cargo.toml").into()),
..Default::default() ..Default::default()
}, },
LanguageInfo { LanguageInfo {
@ -234,6 +236,7 @@ pub fn init(languages: Arc<LanguageRegistry>, node: NodeRuntime, cx: &mut App) {
registration.adapters, registration.adapters,
registration.context, registration.context,
registration.toolchain, registration.toolchain,
registration.manifest_name,
); );
} }
@ -340,7 +343,7 @@ pub fn init(languages: Arc<LanguageRegistry>, node: NodeRuntime, cx: &mut App) {
Arc::from(PyprojectTomlManifestProvider), Arc::from(PyprojectTomlManifestProvider),
]; ];
for provider in manifest_providers { for provider in manifest_providers {
project::ManifestProviders::global(cx).register(provider); project::ManifestProvidersStore::global(cx).register(provider);
} }
} }
@ -350,6 +353,7 @@ struct LanguageInfo {
adapters: Vec<Arc<dyn LspAdapter>>, adapters: Vec<Arc<dyn LspAdapter>>,
context: Option<Arc<dyn ContextProvider>>, context: Option<Arc<dyn ContextProvider>>,
toolchain: Option<Arc<dyn ToolchainLister>>, toolchain: Option<Arc<dyn ToolchainLister>>,
manifest_name: Option<ManifestName>,
} }
fn register_language( fn register_language(
@ -358,6 +362,7 @@ fn register_language(
adapters: Vec<Arc<dyn LspAdapter>>, adapters: Vec<Arc<dyn LspAdapter>>,
context: Option<Arc<dyn ContextProvider>>, context: Option<Arc<dyn ContextProvider>>,
toolchain: Option<Arc<dyn ToolchainLister>>, toolchain: Option<Arc<dyn ToolchainLister>>,
manifest_name: Option<ManifestName>,
) { ) {
let config = load_config(name); let config = load_config(name);
for adapter in adapters { for adapter in adapters {
@ -368,12 +373,14 @@ fn register_language(
config.grammar.clone(), config.grammar.clone(),
config.matcher.clone(), config.matcher.clone(),
config.hidden, config.hidden,
manifest_name.clone(),
Arc::new(move || { Arc::new(move || {
Ok(LoadedLanguage { Ok(LoadedLanguage {
config: config.clone(), config: config.clone(),
queries: load_queries(name), queries: load_queries(name),
context_provider: context.clone(), context_provider: context.clone(),
toolchain_provider: toolchain.clone(), toolchain_provider: toolchain.clone(),
manifest_name: manifest_name.clone(),
}) })
}), }),
); );

View file

@ -127,7 +127,7 @@ impl LspAdapter for PythonLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
if let Some(pyright_bin) = delegate.which("pyright-langserver".as_ref()).await { if let Some(pyright_bin) = delegate.which("pyright-langserver".as_ref()).await {
@ -319,17 +319,9 @@ impl LspAdapter for PythonLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
adapter: &Arc<dyn LspAdapterDelegate>, adapter: &Arc<dyn LspAdapterDelegate>,
toolchains: Arc<dyn LanguageToolchainStore>, toolchain: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let toolchain = toolchains
.active_toolchain(
adapter.worktree_id(),
Arc::from("".as_ref()),
LanguageName::new("Python"),
cx,
)
.await;
cx.update(move |cx| { cx.update(move |cx| {
let mut user_settings = let mut user_settings =
language_server_settings(adapter.as_ref(), &Self::SERVER_NAME, cx) language_server_settings(adapter.as_ref(), &Self::SERVER_NAME, cx)
@ -397,9 +389,7 @@ impl LspAdapter for PythonLspAdapter {
user_settings user_settings
}) })
} }
fn manifest_name(&self) -> Option<ManifestName> {
Some(SharedString::new_static("pyproject.toml").into())
}
fn workspace_folders_content(&self) -> WorkspaceFoldersContent { fn workspace_folders_content(&self) -> WorkspaceFoldersContent {
WorkspaceFoldersContent::WorktreeRoot WorkspaceFoldersContent::WorktreeRoot
} }
@ -1046,8 +1036,8 @@ impl LspAdapter for PyLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
toolchains: Arc<dyn LanguageToolchainStore>, toolchain: Option<Toolchain>,
cx: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
if let Some(pylsp_bin) = delegate.which(Self::SERVER_NAME.as_ref()).await { if let Some(pylsp_bin) = delegate.which(Self::SERVER_NAME.as_ref()).await {
let env = delegate.shell_env().await; let env = delegate.shell_env().await;
@ -1057,14 +1047,7 @@ impl LspAdapter for PyLspAdapter {
arguments: vec![], arguments: vec![],
}) })
} else { } else {
let venv = toolchains let venv = toolchain?;
.active_toolchain(
delegate.worktree_id(),
Arc::from("".as_ref()),
LanguageName::new("Python"),
&mut cx.clone(),
)
.await?;
let pylsp_path = Path::new(venv.path.as_ref()).parent()?.join("pylsp"); let pylsp_path = Path::new(venv.path.as_ref()).parent()?.join("pylsp");
pylsp_path.exists().then(|| LanguageServerBinary { pylsp_path.exists().then(|| LanguageServerBinary {
path: venv.path.to_string().into(), path: venv.path.to_string().into(),
@ -1211,17 +1194,9 @@ impl LspAdapter for PyLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
adapter: &Arc<dyn LspAdapterDelegate>, adapter: &Arc<dyn LspAdapterDelegate>,
toolchains: Arc<dyn LanguageToolchainStore>, toolchain: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let toolchain = toolchains
.active_toolchain(
adapter.worktree_id(),
Arc::from("".as_ref()),
LanguageName::new("Python"),
cx,
)
.await;
cx.update(move |cx| { cx.update(move |cx| {
let mut user_settings = let mut user_settings =
language_server_settings(adapter.as_ref(), &Self::SERVER_NAME, cx) language_server_settings(adapter.as_ref(), &Self::SERVER_NAME, cx)
@ -1282,9 +1257,6 @@ impl LspAdapter for PyLspAdapter {
user_settings user_settings
}) })
} }
fn manifest_name(&self) -> Option<ManifestName> {
Some(SharedString::new_static("pyproject.toml").into())
}
fn workspace_folders_content(&self) -> WorkspaceFoldersContent { fn workspace_folders_content(&self) -> WorkspaceFoldersContent {
WorkspaceFoldersContent::WorktreeRoot WorkspaceFoldersContent::WorktreeRoot
} }
@ -1377,8 +1349,8 @@ impl LspAdapter for BasedPyrightLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
toolchains: Arc<dyn LanguageToolchainStore>, toolchain: Option<Toolchain>,
cx: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
if let Some(bin) = delegate.which(Self::BINARY_NAME.as_ref()).await { if let Some(bin) = delegate.which(Self::BINARY_NAME.as_ref()).await {
let env = delegate.shell_env().await; let env = delegate.shell_env().await;
@ -1388,15 +1360,7 @@ impl LspAdapter for BasedPyrightLspAdapter {
arguments: vec!["--stdio".into()], arguments: vec!["--stdio".into()],
}) })
} else { } else {
let venv = toolchains let path = Path::new(toolchain?.path.as_ref())
.active_toolchain(
delegate.worktree_id(),
Arc::from("".as_ref()),
LanguageName::new("Python"),
&mut cx.clone(),
)
.await?;
let path = Path::new(venv.path.as_ref())
.parent()? .parent()?
.join(Self::BINARY_NAME); .join(Self::BINARY_NAME);
path.exists().then(|| LanguageServerBinary { path.exists().then(|| LanguageServerBinary {
@ -1543,17 +1507,9 @@ impl LspAdapter for BasedPyrightLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
adapter: &Arc<dyn LspAdapterDelegate>, adapter: &Arc<dyn LspAdapterDelegate>,
toolchains: Arc<dyn LanguageToolchainStore>, toolchain: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let toolchain = toolchains
.active_toolchain(
adapter.worktree_id(),
Arc::from("".as_ref()),
LanguageName::new("Python"),
cx,
)
.await;
cx.update(move |cx| { cx.update(move |cx| {
let mut user_settings = let mut user_settings =
language_server_settings(adapter.as_ref(), &Self::SERVER_NAME, cx) language_server_settings(adapter.as_ref(), &Self::SERVER_NAME, cx)
@ -1622,10 +1578,6 @@ impl LspAdapter for BasedPyrightLspAdapter {
}) })
} }
fn manifest_name(&self) -> Option<ManifestName> {
Some(SharedString::new_static("pyproject.toml").into())
}
fn workspace_folders_content(&self) -> WorkspaceFoldersContent { fn workspace_folders_content(&self) -> WorkspaceFoldersContent {
WorkspaceFoldersContent::WorktreeRoot WorkspaceFoldersContent::WorktreeRoot
} }

View file

@ -109,14 +109,10 @@ impl LspAdapter for RustLspAdapter {
SERVER_NAME.clone() SERVER_NAME.clone()
} }
fn manifest_name(&self) -> Option<ManifestName> {
Some(SharedString::new_static("Cargo.toml").into())
}
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let path = delegate.which("rust-analyzer".as_ref()).await?; let path = delegate.which("rust-analyzer".as_ref()).await?;

View file

@ -3,7 +3,7 @@ use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
use futures::StreamExt; use futures::StreamExt;
use gpui::AsyncApp; use gpui::AsyncApp;
use language::{LanguageName, LanguageToolchainStore, LspAdapter, LspAdapterDelegate}; use language::{LanguageName, LspAdapter, LspAdapterDelegate, Toolchain};
use lsp::{LanguageServerBinary, LanguageServerName}; use lsp::{LanguageServerBinary, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy}; use node_runtime::{NodeRuntime, VersionStrategy};
use project::{Fs, lsp_store::language_server_settings}; use project::{Fs, lsp_store::language_server_settings};
@ -50,7 +50,7 @@ impl LspAdapter for TailwindLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?; let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
@ -155,7 +155,7 @@ impl LspAdapter for TailwindLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let mut tailwind_user_settings = cx.update(|cx| { let mut tailwind_user_settings = cx.update(|cx| {

View file

@ -7,7 +7,7 @@ use gpui::{App, AppContext, AsyncApp, Task};
use http_client::github::{AssetKind, GitHubLspBinaryVersion, build_asset_url}; use http_client::github::{AssetKind, GitHubLspBinaryVersion, build_asset_url};
use language::{ use language::{
ContextLocation, ContextProvider, File, LanguageName, LanguageToolchainStore, LspAdapter, ContextLocation, ContextProvider, File, LanguageName, LanguageToolchainStore, LspAdapter,
LspAdapterDelegate, LspAdapterDelegate, Toolchain,
}; };
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName}; use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy}; use node_runtime::{NodeRuntime, VersionStrategy};
@ -722,7 +722,7 @@ impl LspAdapter for TypeScriptLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let override_options = cx.update(|cx| { let override_options = cx.update(|cx| {
@ -822,7 +822,7 @@ impl LspAdapter for EsLintLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let workspace_root = delegate.worktree_root_path(); let workspace_root = delegate.worktree_root_path();

View file

@ -2,7 +2,7 @@ use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
use gpui::AsyncApp; use gpui::AsyncApp;
use language::{LanguageName, LanguageToolchainStore, LspAdapter, LspAdapterDelegate}; use language::{LanguageName, LspAdapter, LspAdapterDelegate, Toolchain};
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName}; use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy}; use node_runtime::{NodeRuntime, VersionStrategy};
use project::{Fs, lsp_store::language_server_settings}; use project::{Fs, lsp_store::language_server_settings};
@ -86,7 +86,7 @@ impl LspAdapter for VtslsLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let env = delegate.shell_env().await; let env = delegate.shell_env().await;
@ -211,7 +211,7 @@ impl LspAdapter for VtslsLspAdapter {
self: Arc<Self>, self: Arc<Self>,
fs: &dyn Fs, fs: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let tsdk_path = Self::tsdk_path(fs, delegate).await; let tsdk_path = Self::tsdk_path(fs, delegate).await;

View file

@ -2,9 +2,7 @@ use anyhow::{Context as _, Result};
use async_trait::async_trait; use async_trait::async_trait;
use futures::StreamExt; use futures::StreamExt;
use gpui::AsyncApp; use gpui::AsyncApp;
use language::{ use language::{LspAdapter, LspAdapterDelegate, Toolchain, language_settings::AllLanguageSettings};
LanguageToolchainStore, LspAdapter, LspAdapterDelegate, language_settings::AllLanguageSettings,
};
use lsp::{LanguageServerBinary, LanguageServerName}; use lsp::{LanguageServerBinary, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy}; use node_runtime::{NodeRuntime, VersionStrategy};
use project::{Fs, lsp_store::language_server_settings}; use project::{Fs, lsp_store::language_server_settings};
@ -57,7 +55,7 @@ impl LspAdapter for YamlLspAdapter {
async fn check_if_user_installed( async fn check_if_user_installed(
&self, &self,
delegate: &dyn LspAdapterDelegate, delegate: &dyn LspAdapterDelegate,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
_: &AsyncApp, _: &AsyncApp,
) -> Option<LanguageServerBinary> { ) -> Option<LanguageServerBinary> {
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?; let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
@ -135,7 +133,7 @@ impl LspAdapter for YamlLspAdapter {
self: Arc<Self>, self: Arc<Self>,
_: &dyn Fs, _: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>, delegate: &Arc<dyn LspAdapterDelegate>,
_: Arc<dyn LanguageToolchainStore>, _: Option<Toolchain>,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let location = SettingsLocation { let location = SettingsLocation {

View file

@ -500,13 +500,12 @@ impl LspCommand for PerformRename {
mut cx: AsyncApp, mut cx: AsyncApp,
) -> Result<ProjectTransaction> { ) -> Result<ProjectTransaction> {
if let Some(edit) = message { if let Some(edit) = message {
let (lsp_adapter, lsp_server) = let (_, lsp_server) =
language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?; language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
LocalLspStore::deserialize_workspace_edit( LocalLspStore::deserialize_workspace_edit(
lsp_store, lsp_store,
edit, edit,
self.push_to_history, self.push_to_history,
lsp_adapter,
lsp_server, lsp_server,
&mut cx, &mut cx,
) )
@ -1116,18 +1115,12 @@ pub async fn location_links_from_lsp(
} }
} }
let (lsp_adapter, language_server) = let (_, language_server) = language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
let mut definitions = Vec::new(); let mut definitions = Vec::new();
for (origin_range, target_uri, target_range) in unresolved_links { for (origin_range, target_uri, target_range) in unresolved_links {
let target_buffer_handle = lsp_store let target_buffer_handle = lsp_store
.update(&mut cx, |this, cx| { .update(&mut cx, |this, cx| {
this.open_local_buffer_via_lsp( this.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx)
target_uri,
language_server.server_id(),
lsp_adapter.name.clone(),
cx,
)
})? })?
.await?; .await?;
@ -1172,8 +1165,7 @@ pub async fn location_link_from_lsp(
server_id: LanguageServerId, server_id: LanguageServerId,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<LocationLink> { ) -> Result<LocationLink> {
let (lsp_adapter, language_server) = let (_, language_server) = language_server_for_buffer(&lsp_store, &buffer, server_id, cx)?;
language_server_for_buffer(&lsp_store, &buffer, server_id, cx)?;
let (origin_range, target_uri, target_range) = ( let (origin_range, target_uri, target_range) = (
link.origin_selection_range, link.origin_selection_range,
@ -1183,12 +1175,7 @@ pub async fn location_link_from_lsp(
let target_buffer_handle = lsp_store let target_buffer_handle = lsp_store
.update(cx, |lsp_store, cx| { .update(cx, |lsp_store, cx| {
lsp_store.open_local_buffer_via_lsp( lsp_store.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx)
target_uri,
language_server.server_id(),
lsp_adapter.name.clone(),
cx,
)
})? })?
.await?; .await?;
@ -1326,7 +1313,7 @@ impl LspCommand for GetReferences {
mut cx: AsyncApp, mut cx: AsyncApp,
) -> Result<Vec<Location>> { ) -> Result<Vec<Location>> {
let mut references = Vec::new(); let mut references = Vec::new();
let (lsp_adapter, language_server) = let (_, language_server) =
language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?; language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
if let Some(locations) = locations { if let Some(locations) = locations {
@ -1336,7 +1323,6 @@ impl LspCommand for GetReferences {
lsp_store.open_local_buffer_via_lsp( lsp_store.open_local_buffer_via_lsp(
lsp_location.uri, lsp_location.uri,
language_server.server_id(), language_server.server_id(),
lsp_adapter.name.clone(),
cx, cx,
) )
})? })?

File diff suppressed because it is too large Load diff

View file

@ -7,18 +7,12 @@ mod manifest_store;
mod path_trie; mod path_trie;
mod server_tree; mod server_tree;
use std::{ use std::{borrow::Borrow, collections::hash_map::Entry, ops::ControlFlow, path::Path, sync::Arc};
borrow::Borrow,
collections::{BTreeMap, hash_map::Entry},
ops::ControlFlow,
path::Path,
sync::Arc,
};
use collections::HashMap; 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}; use language::{ManifestDelegate, ManifestName, ManifestQuery};
pub use manifest_store::ManifestProviders; pub use manifest_store::ManifestProvidersStore;
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, Snapshot, Worktree}; use worktree::{Event as WorktreeEvent, Snapshot, Worktree};
@ -28,9 +22,7 @@ use crate::{
worktree_store::{WorktreeStore, WorktreeStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent},
}; };
pub(crate) use server_tree::{ pub(crate) use server_tree::{LanguageServerTree, LanguageServerTreeNode, LaunchDisposition};
AdapterQuery, LanguageServerTree, LanguageServerTreeNode, LaunchDisposition,
};
struct WorktreeRoots { struct WorktreeRoots {
roots: RootPathTrie<ManifestName>, roots: RootPathTrie<ManifestName>,
@ -81,14 +73,6 @@ pub struct ManifestTree {
_subscriptions: [Subscription; 2], _subscriptions: [Subscription; 2],
} }
#[derive(PartialEq)]
pub(crate) enum ManifestTreeEvent {
WorktreeRemoved(WorktreeId),
Cleared,
}
impl EventEmitter<ManifestTreeEvent> for ManifestTree {}
impl ManifestTree { impl ManifestTree {
pub 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 {
@ -101,30 +85,28 @@ impl ManifestTree {
worktree_roots.roots = RootPathTrie::new(); worktree_roots.roots = RootPathTrie::new();
}) })
} }
cx.emit(ManifestTreeEvent::Cleared);
}), }),
], ],
worktree_store, worktree_store,
}) })
} }
pub(crate) 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>, manifest_name: &ManifestName,
delegate: Arc<dyn ManifestDelegate>, delegate: &Arc<dyn ManifestDelegate>,
cx: &mut App, cx: &mut App,
) -> BTreeMap<ManifestName, ProjectPath> { ) -> Option<ProjectPath> {
debug_assert_eq!(delegate.worktree_id(), worktree_id); debug_assert_eq!(delegate.worktree_id(), *worktree_id);
let mut roots = BTreeMap::from_iter( let (mut marked_path, mut current_presence) = (None, LabelPresence::KnownAbsent);
manifests.map(|manifest| (manifest, (None, LabelPresence::KnownAbsent))), let worktree_roots = match self.root_points.entry(*worktree_id) {
);
let worktree_roots = match self.root_points.entry(worktree_id) {
Entry::Occupied(occupied_entry) => occupied_entry.get().clone(), Entry::Occupied(occupied_entry) => occupied_entry.get().clone(),
Entry::Vacant(vacant_entry) => { Entry::Vacant(vacant_entry) => {
let Some(worktree) = self let Some(worktree) = self
.worktree_store .worktree_store
.read(cx) .read(cx)
.worktree_for_id(worktree_id, cx) .worktree_for_id(*worktree_id, cx)
else { else {
return Default::default(); 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, _| { worktree_roots.read_with(cx, |this, _| {
this.roots.walk(&key, &mut |path, labels| { this.roots.walk(&key, &mut |path, labels| {
for (label, presence) in labels { for (label, presence) in labels {
if let Some((marked_path, current_presence)) = roots.get_mut(label) { if label == manifest_name {
if *current_presence > *presence { if current_presence > *presence {
debug_assert!(false, "RootPathTrie precondition violation; while walking the tree label presence is only allowed to increase"); 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()}); marked_path = Some(ProjectPath {worktree_id: *worktree_id, path: path.clone()});
*current_presence = *presence; current_presence = *presence;
} }
} }
@ -150,12 +132,9 @@ impl ManifestTree {
}); });
}); });
for (manifest_name, (root_path, presence)) in &mut roots { if current_presence == LabelPresence::KnownAbsent {
if *presence == LabelPresence::Present { // Some part of the path is unexplored.
continue; let depth = marked_path
}
let depth = root_path
.as_ref() .as_ref()
.map(|root_path| { .map(|root_path| {
path.strip_prefix(&root_path.path) path.strip_prefix(&root_path.path)
@ -165,13 +144,10 @@ impl ManifestTree {
}) })
.unwrap_or_else(|| path.components().count() + 1); .unwrap_or_else(|| path.components().count() + 1);
if depth > 0 { if depth > 0
let Some(provider) = ManifestProviders::global(cx).get(manifest_name.borrow()) && let Some(provider) =
else { ManifestProvidersStore::global(cx).get(manifest_name.borrow())
log::warn!("Manifest provider `{}` not found", manifest_name.as_ref()); {
continue;
};
let root = provider.search(ManifestQuery { let root = provider.search(ManifestQuery {
path: path.clone(), path: path.clone(),
depth, depth,
@ -182,9 +158,9 @@ impl ManifestTree {
let root = TriePath::from(&*known_root); let root = TriePath::from(&*known_root);
this.roots this.roots
.insert(&root, manifest_name.clone(), LabelPresence::Present); .insert(&root, manifest_name.clone(), LabelPresence::Present);
*presence = LabelPresence::Present; current_presence = LabelPresence::Present;
*root_path = Some(ProjectPath { marked_path = Some(ProjectPath {
worktree_id, worktree_id: *worktree_id,
path: known_root, path: known_root,
}); });
}), }),
@ -195,25 +171,35 @@ impl ManifestTree {
} }
} }
} }
marked_path.filter(|_| current_presence.eq(&LabelPresence::Present))
roots
.into_iter()
.filter_map(|(k, (path, presence))| {
let path = path?;
presence.eq(&LabelPresence::Present).then(|| (k, path))
})
.collect()
} }
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( fn on_worktree_store_event(
&mut self, &mut self,
_: Entity<WorktreeStore>, _: Entity<WorktreeStore>,
evt: &WorktreeStoreEvent, evt: &WorktreeStoreEvent,
cx: &mut Context<Self>, _: &mut Context<Self>,
) { ) {
match evt { match evt {
WorktreeStoreEvent::WorktreeRemoved(_, worktree_id) => { WorktreeStoreEvent::WorktreeRemoved(_, worktree_id) => {
self.root_points.remove(&worktree_id); self.root_points.remove(&worktree_id);
cx.emit(ManifestTreeEvent::WorktreeRemoved(*worktree_id));
} }
_ => {} _ => {}
} }
@ -223,6 +209,7 @@ impl ManifestTree {
pub(crate) struct ManifestQueryDelegate { pub(crate) struct ManifestQueryDelegate {
worktree: Snapshot, worktree: Snapshot,
} }
impl ManifestQueryDelegate { impl ManifestQueryDelegate {
pub fn new(worktree: Snapshot) -> Self { pub fn new(worktree: Snapshot) -> Self {
Self { worktree } Self { worktree }

View file

@ -1,4 +1,4 @@
use collections::HashMap; use collections::{HashMap, HashSet};
use gpui::{App, Global, SharedString}; use gpui::{App, Global, SharedString};
use parking_lot::RwLock; use parking_lot::RwLock;
use std::{ops::Deref, sync::Arc}; use std::{ops::Deref, sync::Arc};
@ -11,13 +11,13 @@ struct ManifestProvidersState {
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct ManifestProviders(Arc<RwLock<ManifestProvidersState>>); pub struct ManifestProvidersStore(Arc<RwLock<ManifestProvidersState>>);
#[derive(Default)] #[derive(Default)]
struct GlobalManifestProvider(ManifestProviders); struct GlobalManifestProvider(ManifestProvidersStore);
impl Deref for GlobalManifestProvider { impl Deref for GlobalManifestProvider {
type Target = ManifestProviders; type Target = ManifestProvidersStore;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
@ -26,7 +26,7 @@ impl Deref for GlobalManifestProvider {
impl Global for GlobalManifestProvider {} impl Global for GlobalManifestProvider {}
impl ManifestProviders { impl ManifestProvidersStore {
/// Returns the global [`ManifestStore`]. /// Returns the global [`ManifestStore`].
/// ///
/// Inserts a default [`ManifestStore`] if one does not yet exist. /// 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>> { pub(super) fn get(&self, name: &SharedString) -> Option<Arc<dyn ManifestProvider>> {
self.0.read().providers.get(name).cloned() self.0.read().providers.get(name).cloned()
} }
pub(crate) fn manifest_file_names(&self) -> HashSet<ManifestName> {
self.0.read().providers.keys().cloned().collect()
}
} }

View file

@ -4,8 +4,7 @@
//! //!
//! ## RPC //! ## 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 //! 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 //! to reuse existing language server.
//! ask about suitable language server for each path it interacts with; it can resolve most of the queries locally.
use std::{ use std::{
collections::{BTreeMap, BTreeSet}, collections::{BTreeMap, BTreeSet},
@ -14,20 +13,23 @@ use std::{
}; };
use collections::IndexMap; use collections::IndexMap;
use gpui::{App, AppContext as _, Entity, Subscription}; use gpui::{App, Entity};
use language::{ use language::{
CachedLspAdapter, LanguageName, LanguageRegistry, ManifestDelegate, CachedLspAdapter, LanguageName, LanguageRegistry, ManifestDelegate, ManifestName, Toolchain,
language_settings::AllLanguageSettings, language_settings::AllLanguageSettings,
}; };
use lsp::LanguageServerName; use lsp::LanguageServerName;
use settings::{Settings, SettingsLocation, WorktreeId}; use settings::{Settings, SettingsLocation, WorktreeId};
use std::sync::OnceLock; 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) struct ServersForWorktree {
pub(crate) roots: BTreeMap< pub(crate) roots: BTreeMap<
Arc<Path>, Arc<Path>,
@ -39,7 +41,7 @@ pub struct LanguageServerTree {
manifest_tree: Entity<ManifestTree>, manifest_tree: Entity<ManifestTree>,
pub(crate) instances: BTreeMap<WorktreeId, ServersForWorktree>, pub(crate) instances: BTreeMap<WorktreeId, ServersForWorktree>,
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
_subscriptions: Subscription, toolchains: Entity<LocalToolchainStore>,
} }
/// A node in language server tree represents either: /// A node in language server tree represents either:
@ -49,22 +51,15 @@ pub struct LanguageServerTree {
pub struct LanguageServerTreeNode(Weak<InnerTreeNode>); pub struct LanguageServerTreeNode(Weak<InnerTreeNode>);
/// Describes a request to launch a language server. /// Describes a request to launch a language server.
#[derive(Debug)] #[derive(Clone, Debug)]
pub(crate) struct LaunchDisposition<'a> { pub(crate) struct LaunchDisposition {
pub(crate) server_name: &'a LanguageServerName, pub(crate) server_name: LanguageServerName,
/// Path to the root directory of a subproject.
pub(crate) path: ProjectPath, pub(crate) path: ProjectPath,
pub(crate) settings: Arc<LspSettings>, 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 { impl LanguageServerTreeNode {
/// Returns a language server ID for this node if there is one. /// 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. /// 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. /// May return None if the node no longer belongs to the server tree it was created in.
pub(crate) fn server_id_or_init( pub(crate) fn server_id_or_init(
&self, &self,
init: impl FnOnce(LaunchDisposition) -> LanguageServerId, init: impl FnOnce(&Arc<LaunchDisposition>) -> LanguageServerId,
) -> Option<LanguageServerId> { ) -> Option<LanguageServerId> {
let this = self.0.upgrade()?; let this = self.0.upgrade()?;
Some( Some(*this.id.get_or_init(|| init(&this.disposition)))
*this
.id
.get_or_init(|| init(LaunchDisposition::from(&*this))),
)
} }
/// Returns a language server name as the language server adapter would return. /// Returns a language server name as the language server adapter would return.
pub fn name(&self) -> Option<LanguageServerName> { 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)] #[derive(Debug)]
pub struct InnerTreeNode { pub struct InnerTreeNode {
id: OnceLock<LanguageServerId>, id: OnceLock<LanguageServerId>,
name: LanguageServerName, disposition: Arc<LaunchDisposition>,
path: ProjectPath,
settings: Arc<LspSettings>,
} }
impl InnerTreeNode { impl InnerTreeNode {
fn new( fn new(
name: LanguageServerName, server_name: LanguageServerName,
path: ProjectPath, path: ProjectPath,
settings: impl Into<Arc<LspSettings>>, settings: LspSettings,
toolchain: Option<Toolchain>,
) -> Self { ) -> Self {
InnerTreeNode { InnerTreeNode {
id: Default::default(), id: Default::default(),
name, disposition: Arc::new(LaunchDisposition {
path, server_name,
settings: settings.into(), 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 { impl LanguageServerTree {
pub(crate) fn new( pub(crate) fn new(
manifest_tree: Entity<ManifestTree>, manifest_tree: Entity<ManifestTree>,
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
cx: &mut App, toolchains: Entity<LocalToolchainStore>,
) -> Entity<Self> { ) -> Self {
cx.new(|cx| Self { Self {
_subscriptions: cx.subscribe(&manifest_tree, |_: &mut Self, _, event, _| {
if event == &ManifestTreeEvent::Cleared {}
}),
manifest_tree, manifest_tree,
instances: Default::default(), instances: Default::default(),
languages, 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. /// 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, &'a mut self,
path: ProjectPath, path: ProjectPath,
query: AdapterQuery<'_>, language_name: LanguageName,
delegate: Arc<dyn ManifestDelegate>, manifest_name: Option<&ManifestName>,
cx: &mut App, delegate: &Arc<dyn ManifestDelegate>,
cx: &'a mut App,
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a { ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
let settings_location = SettingsLocation { let manifest_location = self.manifest_location_for_path(&path, manifest_name, delegate, cx);
worktree_id: path.worktree_id, let adapters = self.adapters_for_language(&manifest_location, &language_name, cx);
path: &path.path, self.init_with_adapters(manifest_location, language_name, adapters, cx)
}; }
let adapters = match query {
AdapterQuery::Language(language_name) => { fn init_with_adapters<'a>(
self.adapters_for_language(settings_location, language_name, cx) &'a mut self,
} root_path: ProjectPath,
AdapterQuery::Adapter(language_server_name) => { language_name: LanguageName,
IndexMap::from_iter(self.adapter_for_name(language_server_name).map(|adapter| { 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(), adapter.name(),
(LspSettings::default(), BTreeSet::new(), adapter), root_path.clone(),
) settings.clone(),
})) toolchain,
} )),
}; Default::default(),
self.get_with_adapters(path, adapters, delegate, cx) )
});
languages.insert(language_name.clone());
Arc::downgrade(&node).into()
})
} }
fn get_with_adapters<'a>( fn get_with_adapters<'a>(
&'a mut self, &'a self,
path: ProjectPath, root_path: ProjectPath,
adapters: IndexMap< adapters: IndexMap<LanguageServerName, (LspSettings, Arc<CachedLspAdapter>)>,
LanguageServerName, ) -> impl Iterator<Item = LanguageServerId> + 'a {
(LspSettings, BTreeSet<LanguageName>, Arc<CachedLspAdapter>), adapters.into_iter().filter_map(move |(_, (_, adapter))| {
>, let root_path = root_path.clone();
delegate: Arc<dyn ManifestDelegate>, let inner_node = self
cx: &mut App, .instances
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a { .get(&root_path.worktree_id)?
let worktree_id = path.worktree_id; .roots
.get(&root_path.path)?
let mut manifest_to_adapters = BTreeMap::default(); .get(&adapter.name())?;
for (_, _, adapter) in adapters.values() { inner_node.0.id.get().copied()
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()
})
} }
fn adapter_for_name(&self, name: &LanguageServerName) -> Option<Arc<CachedLspAdapter>> { fn manifest_location_for_path(
self.languages.adapter_for_name(name) &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( fn adapters_for_language(
&self, &self,
settings_location: SettingsLocation, manifest_location: &ProjectPath,
language_name: &LanguageName, language_name: &LanguageName,
cx: &App, 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( let settings = AllLanguageSettings::get(Some(settings_location), cx).language(
Some(settings_location), Some(settings_location),
Some(language_name), Some(language_name),
@ -295,14 +277,7 @@ impl LanguageServerTree {
) )
.cloned() .cloned()
.unwrap_or_default(); .unwrap_or_default();
Some(( Some((adapter.name(), (adapter_settings, adapter)))
adapter.name(),
(
adapter_settings,
BTreeSet::from_iter([language_name.clone()]),
adapter,
),
))
}) })
.collect::<IndexMap<_, _>>(); .collect::<IndexMap<_, _>>();
// After starting all the language servers, reorder them to reflect the desired order // After starting all the language servers, reorder them to reflect the desired order
@ -315,17 +290,23 @@ impl LanguageServerTree {
&language_name, &language_name,
adapters_with_settings adapters_with_settings
.values() .values()
.map(|(_, _, adapter)| adapter.clone()) .map(|(_, adapter)| adapter.clone())
.collect(), .collect(),
); );
adapters_with_settings adapters_with_settings
} }
// Rebasing a tree: /// Server Tree is built up incrementally via queries for distinct paths of the worktree.
// - Clears it out /// Results of these queries have to be invalidated when data used to build the tree changes.
// - 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<'_> { /// 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) ServerTreeRebase::new(self)
} }
@ -354,16 +335,16 @@ impl LanguageServerTree {
.roots .roots
.entry(Arc::from(Path::new(""))) .entry(Arc::from(Path::new("")))
.or_default() .or_default()
.entry(node.name.clone()) .entry(node.disposition.server_name.clone())
.or_insert_with(|| (node, BTreeSet::new())) .or_insert_with(|| (node, BTreeSet::new()))
.1 .1
.insert(language_name); .insert(language_name);
} }
} }
pub(crate) struct ServerTreeRebase<'a> { pub(crate) struct ServerTreeRebase {
old_contents: BTreeMap<WorktreeId, ServersForWorktree>, old_contents: BTreeMap<WorktreeId, ServersForWorktree>,
new_tree: &'a mut LanguageServerTree, new_tree: LanguageServerTree,
/// All server IDs seen in the old tree. /// All server IDs seen in the old tree.
all_server_ids: BTreeMap<LanguageServerId, LanguageServerName>, 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 /// 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>, rebased_server_ids: BTreeSet<LanguageServerId>,
} }
impl<'tree> ServerTreeRebase<'tree> { impl ServerTreeRebase {
fn new(new_tree: &'tree mut LanguageServerTree) -> Self { fn new(old_tree: &LanguageServerTree) -> Self {
let old_contents = std::mem::take(&mut new_tree.instances); let old_contents = old_tree.instances.clone();
let all_server_ids = old_contents let all_server_ids = old_contents
.values() .values()
.flat_map(|nodes| { .flat_map(|nodes| {
@ -384,69 +365,68 @@ impl<'tree> ServerTreeRebase<'tree> {
.id .id
.get() .get()
.copied() .copied()
.map(|id| (id, server.0.name.clone())) .map(|id| (id, server.0.disposition.server_name.clone()))
}) })
}) })
}) })
.collect(); .collect();
let new_tree = LanguageServerTree::new(
old_tree.manifest_tree.clone(),
old_tree.languages.clone(),
old_tree.toolchains.clone(),
);
Self { Self {
old_contents, old_contents,
new_tree,
all_server_ids, all_server_ids,
new_tree,
rebased_server_ids: BTreeSet::new(), rebased_server_ids: BTreeSet::new(),
} }
} }
pub(crate) fn get<'a>( pub(crate) fn walk<'a>(
&'a mut self, &'a mut self,
path: ProjectPath, path: ProjectPath,
query: AdapterQuery<'_>, language_name: LanguageName,
manifest_name: Option<&ManifestName>,
delegate: Arc<dyn ManifestDelegate>, delegate: Arc<dyn ManifestDelegate>,
cx: &mut App, cx: &'a mut App,
) -> impl Iterator<Item = LanguageServerTreeNode> + 'a { ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
let settings_location = SettingsLocation { let manifest =
worktree_id: path.worktree_id, self.new_tree
path: &path.path, .manifest_location_for_path(&path, manifest_name, &delegate, cx);
}; let adapters = self
let adapters = match query { .new_tree
AdapterQuery::Language(language_name) => { .adapters_for_language(&manifest, &language_name, cx);
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),
)
},
))
}
};
self.new_tree self.new_tree
.get_with_adapters(path, adapters, delegate, cx) .init_with_adapters(manifest, language_name, adapters, cx)
.filter_map(|node| { .filter_map(|node| {
// Inspect result of the query and initialize it ourselves before // Inspect result of the query and initialize it ourselves before
// handing it off to the caller. // 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); return Some(node);
} }
let disposition = &live_node.disposition;
let Some((existing_node, _)) = self let Some((existing_node, _)) = self
.old_contents .old_contents
.get(&disposition.path.worktree_id) .get(&disposition.path.worktree_id)
.and_then(|worktree_nodes| worktree_nodes.roots.get(&disposition.path.path)) .and_then(|worktree_nodes| worktree_nodes.roots.get(&disposition.path.path))
.and_then(|roots| roots.get(&disposition.name)) .and_then(|roots| roots.get(&disposition.server_name))
.filter(|(old_node, _)| disposition.settings == old_node.settings) .filter(|(old_node, _)| {
(&disposition.toolchain, &disposition.settings)
== (
&old_node.disposition.toolchain,
&old_node.disposition.settings,
)
})
else { else {
return Some(node); return Some(node);
}; };
if let Some(existing_id) = existing_node.id.get() { if let Some(existing_id) = existing_node.id.get() {
self.rebased_server_ids.insert(*existing_id); self.rebased_server_ids.insert(*existing_id);
disposition.id.set(*existing_id).ok(); live_node.id.set(*existing_id).ok();
} }
Some(node) Some(node)
@ -454,11 +434,19 @@ impl<'tree> ServerTreeRebase<'tree> {
} }
/// Returns IDs of servers that are no longer referenced (and can be shut down). /// Returns IDs of servers that are no longer referenced (and can be shut down).
pub(crate) fn finish(self) -> BTreeMap<LanguageServerId, LanguageServerName> { pub(crate) fn finish(
self.all_server_ids self,
.into_iter() ) -> (
.filter(|(id, _)| !self.rebased_server_ids.contains(id)) LanguageServerTree,
.collect() 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 { pub(crate) fn server_tree(&mut self) -> &mut LanguageServerTree {

View file

@ -84,7 +84,7 @@ use lsp::{
}; };
use lsp_command::*; use lsp_command::*;
use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle}; use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle};
pub use manifest_tree::ManifestProviders; pub use manifest_tree::ManifestProvidersStore;
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use parking_lot::Mutex; use parking_lot::Mutex;
pub use prettier_store::PrettierStore; pub use prettier_store::PrettierStore;
@ -1115,7 +1115,11 @@ impl Project {
buffer_store.clone(), buffer_store.clone(),
worktree_store.clone(), worktree_store.clone(),
prettier_store.clone(), prettier_store.clone(),
toolchain_store.clone(), toolchain_store
.read(cx)
.as_local_store()
.expect("Toolchain store to be local")
.clone(),
environment.clone(), environment.clone(),
manifest_tree, manifest_tree,
languages.clone(), languages.clone(),
@ -1260,7 +1264,6 @@ impl Project {
LspStore::new_remote( LspStore::new_remote(
buffer_store.clone(), buffer_store.clone(),
worktree_store.clone(), worktree_store.clone(),
Some(toolchain_store.clone()),
languages.clone(), languages.clone(),
ssh_proto.clone(), ssh_proto.clone(),
SSH_PROJECT_ID, SSH_PROJECT_ID,
@ -1485,7 +1488,6 @@ impl Project {
let mut lsp_store = LspStore::new_remote( let mut lsp_store = LspStore::new_remote(
buffer_store.clone(), buffer_store.clone(),
worktree_store.clone(), worktree_store.clone(),
None,
languages.clone(), languages.clone(),
client.clone().into(), client.clone().into(),
remote_id, remote_id,
@ -3596,16 +3598,10 @@ impl Project {
&mut self, &mut self,
abs_path: lsp::Url, abs_path: lsp::Url,
language_server_id: LanguageServerId, language_server_id: LanguageServerId,
language_server_name: LanguageServerName,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Task<Result<Entity<Buffer>>> { ) -> Task<Result<Entity<Buffer>>> {
self.lsp_store.update(cx, |lsp_store, cx| { self.lsp_store.update(cx, |lsp_store, cx| {
lsp_store.open_local_buffer_via_lsp( lsp_store.open_local_buffer_via_lsp(abs_path, language_server_id, cx)
abs_path,
language_server_id,
language_server_name,
cx,
)
}) })
} }

View file

@ -22,6 +22,7 @@ use settings::{
SettingsStore, parse_json_with_comments, watch_config_file, SettingsStore, parse_json_with_comments, watch_config_file,
}; };
use std::{ use std::{
collections::BTreeMap,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
time::Duration, 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 struct BinarySettings {
pub path: Option<String>, pub path: Option<String>,
pub arguments: Option<Vec<String>>, pub arguments: Option<Vec<String>>,
// this can't be an FxHashMap because the extension APIs require the default SipHash pub env: Option<BTreeMap<String, String>>,
pub env: Option<std::collections::HashMap<String, String>>,
pub ignore_system_version: Option<bool>, 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")] #[serde(rename_all = "snake_case")]
pub struct LspSettings { pub struct LspSettings {
pub binary: Option<BinarySettings>, pub binary: Option<BinarySettings>,

View file

@ -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 prev_read_dir_count = fs.read_dir_call_count();
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
let (server_id, server_name) = lsp_store.read_with(cx, |lsp_store, _| { let server_id = lsp_store.read_with(cx, |lsp_store, _| {
let (id, status) = lsp_store.language_server_statuses().next().unwrap(); let (id, _) = lsp_store.language_server_statuses().next().unwrap();
(id, status.name.clone()) id
}); });
// Simulate jumping to a definition in a dependency outside of the worktree. // 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( project.open_local_buffer_via_lsp(
lsp::Url::from_file_path(path!("/the-registry/dep1/src/dep1.rs")).unwrap(), lsp::Url::from_file_path(path!("/the-registry/dep1/src/dep1.rs")).unwrap(),
server_id, server_id,
server_name.clone(),
cx, cx,
) )
}) })

View file

@ -11,7 +11,10 @@ use collections::BTreeMap;
use gpui::{ use gpui::{
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task, WeakEntity, 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::{ use rpc::{
AnyProtoClient, TypedEnvelope, AnyProtoClient, TypedEnvelope,
proto::{self, FromProto, ToProto}, proto::{self, FromProto, ToProto},
@ -104,9 +107,11 @@ impl ToolchainStore {
cx: &App, cx: &App,
) -> Task<Option<Toolchain>> { ) -> Task<Option<Toolchain>> {
match &self.0 { match &self.0 {
ToolchainStoreInner::Local(local, _) => { ToolchainStoreInner::Local(local, _) => Task::ready(local.read(cx).active_toolchain(
local.read(cx).active_toolchain(path, language_name, cx) path.worktree_id,
} &path.path,
language_name,
)),
ToolchainStoreInner::Remote(remote) => { ToolchainStoreInner::Remote(remote) => {
remote.read(cx).active_toolchain(path, language_name, cx) remote.read(cx).active_toolchain(path, language_name, cx)
} }
@ -232,9 +237,15 @@ impl ToolchainStore {
ToolchainStoreInner::Remote(remote) => Arc::new(RemoteStore(remote.downgrade())), 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>, languages: Arc<LanguageRegistry>,
worktree_store: Entity<WorktreeStore>, worktree_store: Entity<WorktreeStore>,
project_environment: Entity<ProjectEnvironment>, project_environment: Entity<ProjectEnvironment>,
@ -243,20 +254,19 @@ struct LocalToolchainStore {
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl language::LanguageToolchainStore for LocalStore { impl language::LocalLanguageToolchainStore for LocalStore {
async fn active_toolchain( fn active_toolchain(
self: Arc<Self>, self: Arc<Self>,
worktree_id: WorktreeId, worktree_id: WorktreeId,
path: Arc<Path>, path: &Arc<Path>,
language_name: LanguageName, language_name: LanguageName,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Option<Toolchain> { ) -> Option<Toolchain> {
self.0 self.0
.update(cx, |this, cx| { .update(cx, |this, _| {
this.active_toolchain(ProjectPath { worktree_id, path }, language_name, cx) this.active_toolchain(worktree_id, path, language_name)
}) })
.ok()? .ok()?
.await
} }
} }
@ -279,19 +289,18 @@ impl language::LanguageToolchainStore for RemoteStore {
} }
pub struct EmptyToolchainStore; pub struct EmptyToolchainStore;
#[async_trait(?Send)] impl language::LocalLanguageToolchainStore for EmptyToolchainStore {
impl language::LanguageToolchainStore for EmptyToolchainStore { fn active_toolchain(
async fn active_toolchain(
self: Arc<Self>, self: Arc<Self>,
_: WorktreeId, _: WorktreeId,
_: Arc<Path>, _: &Arc<Path>,
_: LanguageName, _: LanguageName,
_: &mut AsyncApp, _: &mut AsyncApp,
) -> Option<Toolchain> { ) -> Option<Toolchain> {
None None
} }
} }
struct LocalStore(WeakEntity<LocalToolchainStore>); pub(crate) struct LocalStore(WeakEntity<LocalToolchainStore>);
struct RemoteStore(WeakEntity<RemoteToolchainStore>); struct RemoteStore(WeakEntity<RemoteToolchainStore>);
#[derive(Clone)] #[derive(Clone)]
@ -349,17 +358,13 @@ impl LocalToolchainStore {
.flatten()?; .flatten()?;
let worktree_id = snapshot.id(); let worktree_id = snapshot.id();
let worktree_root = snapshot.abs_path().to_path_buf(); 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 let relative_path = manifest_tree
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.root_for_path( this.root_for_path(&path, &manifest_name, &delegate, cx)
path,
&mut std::iter::once(manifest_name.clone()),
Arc::new(ManifestQueryDelegate::new(snapshot)),
cx,
)
}) })
.ok()? .ok()?
.remove(&manifest_name)
.unwrap_or_else(|| ProjectPath { .unwrap_or_else(|| ProjectPath {
path: Arc::from(Path::new("")), path: Arc::from(Path::new("")),
worktree_id, worktree_id,
@ -394,21 +399,20 @@ impl LocalToolchainStore {
} }
pub(crate) fn active_toolchain( pub(crate) fn active_toolchain(
&self, &self,
path: ProjectPath, worktree_id: WorktreeId,
relative_path: &Arc<Path>,
language_name: LanguageName, language_name: LanguageName,
_: &App, ) -> Option<Toolchain> {
) -> Task<Option<Toolchain>> { let ancestors = relative_path.ancestors();
let ancestors = path.path.ancestors();
Task::ready( self.active_toolchains
self.active_toolchains .get(&(worktree_id, language_name))
.get(&(path.worktree_id, language_name)) .and_then(|paths| {
.and_then(|paths| { ancestors
ancestors .into_iter()
.into_iter() .find_map(|root_path| paths.get(root_path))
.find_map(|root_path| paths.get(root_path)) })
}) .cloned()
.cloned(),
)
} }
} }
struct RemoteToolchainStore { struct RemoteToolchainStore {

View file

@ -171,7 +171,11 @@ impl HeadlessProject {
buffer_store.clone(), buffer_store.clone(),
worktree_store.clone(), worktree_store.clone(),
prettier_store.clone(), prettier_store.clone(),
toolchain_store.clone(), toolchain_store
.read(cx)
.as_local_store()
.expect("Toolchain store to be local")
.clone(),
environment, environment,
manifest_tree, manifest_tree,
languages.clone(), languages.clone(),