1//! This module defines an LSP Tree.
2//!
3//! An LSP Tree is responsible for determining which language servers apply to a given project path.
4//!
5//! ## RPC
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
7//! 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
8//! ask about suitable language server for each path it interacts with; it can resolve most of the queries locally.
9
10use std::{
11 collections::{BTreeMap, BTreeSet},
12 path::Path,
13 sync::{Arc, Weak},
14};
15
16use collections::{HashMap, IndexMap};
17use gpui::{App, AppContext as _, Entity, Subscription};
18use language::{
19 Attach, CachedLspAdapter, LanguageName, LanguageRegistry, LspAdapterDelegate,
20 language_settings::AllLanguageSettings,
21};
22use lsp::LanguageServerName;
23use settings::{Settings, SettingsLocation, WorktreeId};
24use std::sync::OnceLock;
25
26use crate::{LanguageServerId, ProjectPath, project_settings::LspSettings};
27
28use super::{ManifestTree, ManifestTreeEvent};
29
30#[derive(Debug, Default)]
31struct ServersForWorktree {
32 roots: BTreeMap<
33 Arc<Path>,
34 BTreeMap<LanguageServerName, (Arc<InnerTreeNode>, BTreeSet<LanguageName>)>,
35 >,
36}
37
38pub struct LanguageServerTree {
39 manifest_tree: Entity<ManifestTree>,
40 instances: BTreeMap<WorktreeId, ServersForWorktree>,
41 attach_kind_cache: HashMap<LanguageServerName, Attach>,
42 languages: Arc<LanguageRegistry>,
43 _subscriptions: Subscription,
44}
45
46/// A node in language server tree represents either:
47/// - A language server that has already been initialized/updated for a given project
48/// - A soon-to-be-initialized language server.
49#[derive(Clone)]
50pub(crate) struct LanguageServerTreeNode(Weak<InnerTreeNode>);
51
52/// Describes a request to launch a language server.
53#[derive(Debug)]
54pub(crate) struct LaunchDisposition<'a> {
55 pub(crate) server_name: &'a LanguageServerName,
56 pub(crate) attach: Attach,
57 pub(crate) path: ProjectPath,
58 pub(crate) settings: Arc<LspSettings>,
59}
60
61impl<'a> From<&'a InnerTreeNode> for LaunchDisposition<'a> {
62 fn from(value: &'a InnerTreeNode) -> Self {
63 LaunchDisposition {
64 server_name: &value.name,
65 attach: value.attach,
66 path: value.path.clone(),
67 settings: value.settings.clone(),
68 }
69 }
70}
71impl LanguageServerTreeNode {
72 /// Returns a language server ID for this node if there is one.
73 /// Returns None if this node has not been initialized yet or it is no longer in the tree.
74 pub(crate) fn server_id(&self) -> Option<LanguageServerId> {
75 self.0.upgrade()?.id.get().copied()
76 }
77 /// Returns a language server ID for this node if it has already been initialized; otherwise runs the provided closure to initialize the language server node in a tree.
78 /// May return None if the node no longer belongs to the server tree it was created in.
79 pub(crate) fn server_id_or_init(
80 &self,
81 init: impl FnOnce(LaunchDisposition) -> LanguageServerId,
82 ) -> Option<LanguageServerId> {
83 let this = self.0.upgrade()?;
84 Some(
85 *this
86 .id
87 .get_or_init(|| init(LaunchDisposition::from(&*this))),
88 )
89 }
90}
91
92impl From<Weak<InnerTreeNode>> for LanguageServerTreeNode {
93 fn from(weak: Weak<InnerTreeNode>) -> Self {
94 LanguageServerTreeNode(weak)
95 }
96}
97
98#[derive(Debug)]
99struct InnerTreeNode {
100 id: OnceLock<LanguageServerId>,
101 name: LanguageServerName,
102 attach: Attach,
103 path: ProjectPath,
104 settings: Arc<LspSettings>,
105}
106
107impl InnerTreeNode {
108 fn new(
109 name: LanguageServerName,
110 attach: Attach,
111 path: ProjectPath,
112 settings: impl Into<Arc<LspSettings>>,
113 ) -> Self {
114 InnerTreeNode {
115 id: Default::default(),
116 name,
117 attach,
118 path,
119 settings: settings.into(),
120 }
121 }
122}
123
124/// Determines how the list of adapters to query should be constructed.
125pub(crate) enum AdapterQuery<'a> {
126 /// Search for roots of all adapters associated with a given language name.
127 Language(&'a LanguageName),
128 /// Search for roots of adapter with a given name.
129 Adapter(&'a LanguageServerName),
130}
131
132impl LanguageServerTree {
133 pub(crate) fn new(
134 manifest_tree: Entity<ManifestTree>,
135 languages: Arc<LanguageRegistry>,
136 cx: &mut App,
137 ) -> Entity<Self> {
138 cx.new(|cx| Self {
139 _subscriptions: cx.subscribe(&manifest_tree, |_: &mut Self, _, event, _| {
140 if event == &ManifestTreeEvent::Cleared {}
141 }),
142 manifest_tree,
143 instances: Default::default(),
144 attach_kind_cache: Default::default(),
145 languages,
146 })
147 }
148
149 /// Get all language server root points for a given path and language; the language servers might already be initialized at a given path.
150 pub(crate) fn get<'a>(
151 &'a mut self,
152 path: ProjectPath,
153 query: AdapterQuery<'_>,
154 delegate: Arc<dyn LspAdapterDelegate>,
155 cx: &mut App,
156 ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
157 let settings_location = SettingsLocation {
158 worktree_id: path.worktree_id,
159 path: &path.path,
160 };
161 let adapters = match query {
162 AdapterQuery::Language(language_name) => {
163 self.adapters_for_language(settings_location, language_name, cx)
164 }
165 AdapterQuery::Adapter(language_server_name) => {
166 IndexMap::from_iter(self.adapter_for_name(language_server_name).map(|adapter| {
167 (
168 adapter.name(),
169 (LspSettings::default(), BTreeSet::new(), adapter),
170 )
171 }))
172 }
173 };
174 self.get_with_adapters(path, adapters, delegate, cx)
175 }
176
177 fn get_with_adapters<'a>(
178 &'a mut self,
179 path: ProjectPath,
180 adapters: IndexMap<
181 LanguageServerName,
182 (LspSettings, BTreeSet<LanguageName>, Arc<CachedLspAdapter>),
183 >,
184 delegate: Arc<dyn LspAdapterDelegate>,
185 cx: &mut App,
186 ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
187 let worktree_id = path.worktree_id;
188
189 let mut manifest_to_adapters = BTreeMap::default();
190 for (_, _, adapter) in adapters.values() {
191 if let Some(manifest_name) = adapter.manifest_name() {
192 manifest_to_adapters
193 .entry(manifest_name)
194 .or_insert_with(Vec::default)
195 .push(adapter.clone());
196 }
197 }
198
199 let roots = self.manifest_tree.update(cx, |this, cx| {
200 this.root_for_path(
201 path,
202 &mut manifest_to_adapters.keys().cloned(),
203 delegate,
204 cx,
205 )
206 });
207 let root_path = std::cell::LazyCell::new(move || ProjectPath {
208 worktree_id,
209 path: Arc::from("".as_ref()),
210 });
211 adapters
212 .into_iter()
213 .map(move |(_, (settings, new_languages, adapter))| {
214 // 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.
215 let root_path = adapter
216 .manifest_name()
217 .and_then(|name| roots.get(&name))
218 .cloned()
219 .unwrap_or_else(|| root_path.clone());
220 let attach = adapter.attach_kind();
221
222 let inner_node = self
223 .instances
224 .entry(root_path.worktree_id)
225 .or_default()
226 .roots
227 .entry(root_path.path.clone())
228 .or_default()
229 .entry(adapter.name());
230 let (node, languages) = inner_node.or_insert_with(|| {
231 (
232 Arc::new(InnerTreeNode::new(
233 adapter.name(),
234 attach,
235 root_path.clone(),
236 settings.clone(),
237 )),
238 Default::default(),
239 )
240 });
241 languages.extend(new_languages.iter().cloned());
242 Arc::downgrade(&node).into()
243 })
244 }
245
246 fn adapter_for_name(&self, name: &LanguageServerName) -> Option<Arc<CachedLspAdapter>> {
247 self.languages.adapter_for_name(name)
248 }
249
250 pub fn server_id_for_name(&self, name: &LanguageServerName) -> Option<LanguageServerId> {
251 self.instances
252 .values()
253 .flat_map(|instance| instance.roots.values())
254 .flatten()
255 .find_map(|(server_name, (data, _))| {
256 if server_name == name {
257 data.id.get().copied()
258 } else {
259 None
260 }
261 })
262 }
263
264 fn adapters_for_language(
265 &self,
266 settings_location: SettingsLocation,
267 language_name: &LanguageName,
268 cx: &App,
269 ) -> IndexMap<LanguageServerName, (LspSettings, BTreeSet<LanguageName>, Arc<CachedLspAdapter>)>
270 {
271 let settings = AllLanguageSettings::get(Some(settings_location), cx).language(
272 Some(settings_location),
273 Some(language_name),
274 cx,
275 );
276 if !settings.enable_language_server {
277 return Default::default();
278 }
279 let available_lsp_adapters = self.languages.lsp_adapters(&language_name);
280 let available_language_servers = available_lsp_adapters
281 .iter()
282 .map(|lsp_adapter| lsp_adapter.name.clone())
283 .collect::<Vec<_>>();
284
285 let desired_language_servers =
286 settings.customized_language_servers(&available_language_servers);
287 let adapters_with_settings = desired_language_servers
288 .into_iter()
289 .filter_map(|desired_adapter| {
290 let adapter = if let Some(adapter) = available_lsp_adapters
291 .iter()
292 .find(|adapter| adapter.name == desired_adapter)
293 {
294 Some(adapter.clone())
295 } else if let Some(adapter) =
296 self.languages.load_available_lsp_adapter(&desired_adapter)
297 {
298 self.languages
299 .register_lsp_adapter(language_name.clone(), adapter.adapter.clone());
300 Some(adapter)
301 } else {
302 None
303 }?;
304 let adapter_settings = crate::lsp_store::language_server_settings_for(
305 settings_location,
306 &adapter.name,
307 cx,
308 )
309 .cloned()
310 .unwrap_or_default();
311 Some((
312 adapter.name(),
313 (
314 adapter_settings,
315 BTreeSet::from_iter([language_name.clone()]),
316 adapter,
317 ),
318 ))
319 })
320 .collect::<IndexMap<_, _>>();
321 // After starting all the language servers, reorder them to reflect the desired order
322 // based on the settings.
323 //
324 // This is done, in part, to ensure that language servers loaded at different points
325 // (e.g., native vs extension) still end up in the right order at the end, rather than
326 // it being based on which language server happened to be loaded in first.
327 self.languages.reorder_language_servers(
328 &language_name,
329 adapters_with_settings
330 .values()
331 .map(|(_, _, adapter)| adapter.clone())
332 .collect(),
333 );
334
335 adapters_with_settings
336 }
337
338 // Rebasing a tree:
339 // - Clears it out
340 // - Provides you with the indirect access to the old tree while you're reinitializing a new one (by querying it).
341 pub(crate) fn rebase(&mut self) -> ServerTreeRebase<'_> {
342 ServerTreeRebase::new(self)
343 }
344
345 /// Remove nodes with a given ID from the tree.
346 pub(crate) fn remove_nodes(&mut self, ids: &BTreeSet<LanguageServerId>) {
347 for (_, servers) in &mut self.instances {
348 for (_, nodes) in &mut servers.roots {
349 nodes.retain(|_, (node, _)| node.id.get().map_or(true, |id| !ids.contains(&id)));
350 }
351 }
352 }
353}
354
355pub(crate) struct ServerTreeRebase<'a> {
356 old_contents: BTreeMap<WorktreeId, ServersForWorktree>,
357 new_tree: &'a mut LanguageServerTree,
358 /// All server IDs seen in the old tree.
359 all_server_ids: BTreeMap<LanguageServerId, LanguageServerName>,
360 /// Server IDs we've preserved for a new iteration of the tree. `all_server_ids - rebased_server_ids` is the
361 /// set of server IDs that can be shut down.
362 rebased_server_ids: BTreeSet<LanguageServerId>,
363}
364
365impl<'tree> ServerTreeRebase<'tree> {
366 fn new(new_tree: &'tree mut LanguageServerTree) -> Self {
367 let old_contents = std::mem::take(&mut new_tree.instances);
368 new_tree.attach_kind_cache.clear();
369 let all_server_ids = old_contents
370 .values()
371 .flat_map(|nodes| {
372 nodes.roots.values().flat_map(|servers| {
373 servers.values().filter_map(|server| {
374 server
375 .0
376 .id
377 .get()
378 .copied()
379 .map(|id| (id, server.0.name.clone()))
380 })
381 })
382 })
383 .collect();
384 Self {
385 old_contents,
386 new_tree,
387 all_server_ids,
388 rebased_server_ids: BTreeSet::new(),
389 }
390 }
391
392 pub(crate) fn get<'a>(
393 &'a mut self,
394 path: ProjectPath,
395 query: AdapterQuery<'_>,
396 delegate: Arc<dyn LspAdapterDelegate>,
397 cx: &mut App,
398 ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
399 let settings_location = SettingsLocation {
400 worktree_id: path.worktree_id,
401 path: &path.path,
402 };
403 let adapters = match query {
404 AdapterQuery::Language(language_name) => {
405 self.new_tree
406 .adapters_for_language(settings_location, language_name, cx)
407 }
408 AdapterQuery::Adapter(language_server_name) => {
409 IndexMap::from_iter(self.new_tree.adapter_for_name(language_server_name).map(
410 |adapter| {
411 (
412 adapter.name(),
413 (LspSettings::default(), BTreeSet::new(), adapter),
414 )
415 },
416 ))
417 }
418 };
419
420 self.new_tree
421 .get_with_adapters(path, adapters, delegate, cx)
422 .filter_map(|node| {
423 // Inspect result of the query and initialize it ourselves before
424 // handing it off to the caller.
425 let disposition = node.0.upgrade()?;
426
427 if disposition.id.get().is_some() {
428 return Some(node);
429 }
430 let Some((existing_node, _)) = self
431 .old_contents
432 .get(&disposition.path.worktree_id)
433 .and_then(|worktree_nodes| worktree_nodes.roots.get(&disposition.path.path))
434 .and_then(|roots| roots.get(&disposition.name))
435 .filter(|(old_node, _)| {
436 disposition.attach == old_node.attach
437 && disposition.settings == old_node.settings
438 })
439 else {
440 return Some(node);
441 };
442 if let Some(existing_id) = existing_node.id.get() {
443 self.rebased_server_ids.insert(*existing_id);
444 disposition.id.set(*existing_id).ok();
445 }
446
447 Some(node)
448 })
449 }
450
451 /// Returns IDs of servers that are no longer referenced (and can be shut down).
452 pub(crate) fn finish(self) -> BTreeMap<LanguageServerId, LanguageServerName> {
453 self.all_server_ids
454 .into_iter()
455 .filter(|(id, _)| !self.rebased_server_ids.contains(id))
456 .collect()
457 }
458}