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, ManifestDelegate,
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)]
31pub(crate) struct ServersForWorktree {
32 pub(crate) roots: BTreeMap<
33 Arc<Path>,
34 BTreeMap<LanguageServerName, (Arc<InnerTreeNode>, BTreeSet<LanguageName>)>,
35 >,
36}
37
38pub struct LanguageServerTree {
39 manifest_tree: Entity<ManifestTree>,
40 pub(crate) 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 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)]
99pub struct 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 ManifestDelegate>,
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 ManifestDelegate>,
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 fn adapters_for_language(
251 &self,
252 settings_location: SettingsLocation,
253 language_name: &LanguageName,
254 cx: &App,
255 ) -> IndexMap<LanguageServerName, (LspSettings, BTreeSet<LanguageName>, Arc<CachedLspAdapter>)>
256 {
257 let settings = AllLanguageSettings::get(Some(settings_location), cx).language(
258 Some(settings_location),
259 Some(language_name),
260 cx,
261 );
262 if !settings.enable_language_server {
263 return Default::default();
264 }
265 let available_lsp_adapters = self.languages.lsp_adapters(&language_name);
266 let available_language_servers = available_lsp_adapters
267 .iter()
268 .map(|lsp_adapter| lsp_adapter.name.clone())
269 .collect::<Vec<_>>();
270
271 let desired_language_servers =
272 settings.customized_language_servers(&available_language_servers);
273 let adapters_with_settings = desired_language_servers
274 .into_iter()
275 .filter_map(|desired_adapter| {
276 let adapter = if let Some(adapter) = available_lsp_adapters
277 .iter()
278 .find(|adapter| adapter.name == desired_adapter)
279 {
280 Some(adapter.clone())
281 } else if let Some(adapter) =
282 self.languages.load_available_lsp_adapter(&desired_adapter)
283 {
284 self.languages
285 .register_lsp_adapter(language_name.clone(), adapter.adapter.clone());
286 Some(adapter)
287 } else {
288 None
289 }?;
290 let adapter_settings = crate::lsp_store::language_server_settings_for(
291 settings_location,
292 &adapter.name,
293 cx,
294 )
295 .cloned()
296 .unwrap_or_default();
297 Some((
298 adapter.name(),
299 (
300 adapter_settings,
301 BTreeSet::from_iter([language_name.clone()]),
302 adapter,
303 ),
304 ))
305 })
306 .collect::<IndexMap<_, _>>();
307 // After starting all the language servers, reorder them to reflect the desired order
308 // based on the settings.
309 //
310 // This is done, in part, to ensure that language servers loaded at different points
311 // (e.g., native vs extension) still end up in the right order at the end, rather than
312 // it being based on which language server happened to be loaded in first.
313 self.languages.reorder_language_servers(
314 &language_name,
315 adapters_with_settings
316 .values()
317 .map(|(_, _, adapter)| adapter.clone())
318 .collect(),
319 );
320
321 adapters_with_settings
322 }
323
324 // Rebasing a tree:
325 // - Clears it out
326 // - Provides you with the indirect access to the old tree while you're reinitializing a new one (by querying it).
327 pub(crate) fn rebase(&mut self) -> ServerTreeRebase<'_> {
328 ServerTreeRebase::new(self)
329 }
330
331 /// Remove nodes with a given ID from the tree.
332 pub(crate) fn remove_nodes(&mut self, ids: &BTreeSet<LanguageServerId>) {
333 for (_, servers) in &mut self.instances {
334 for (_, nodes) in &mut servers.roots {
335 nodes.retain(|_, (node, _)| node.id.get().map_or(true, |id| !ids.contains(&id)));
336 }
337 }
338 }
339
340 pub(crate) fn register_reused(
341 &mut self,
342 worktree_id: WorktreeId,
343 language_name: LanguageName,
344 reused: LanguageServerTreeNode,
345 ) {
346 let Some(node) = reused.0.upgrade() else {
347 return;
348 };
349
350 self.instances
351 .entry(worktree_id)
352 .or_default()
353 .roots
354 .entry(Arc::from(Path::new("")))
355 .or_default()
356 .entry(node.name.clone())
357 .or_insert_with(|| (node, BTreeSet::new()))
358 .1
359 .insert(language_name);
360 }
361}
362
363pub(crate) struct ServerTreeRebase<'a> {
364 old_contents: BTreeMap<WorktreeId, ServersForWorktree>,
365 new_tree: &'a mut LanguageServerTree,
366 /// All server IDs seen in the old tree.
367 all_server_ids: BTreeMap<LanguageServerId, LanguageServerName>,
368 /// Server IDs we've preserved for a new iteration of the tree. `all_server_ids - rebased_server_ids` is the
369 /// set of server IDs that can be shut down.
370 rebased_server_ids: BTreeSet<LanguageServerId>,
371}
372
373impl<'tree> ServerTreeRebase<'tree> {
374 fn new(new_tree: &'tree mut LanguageServerTree) -> Self {
375 let old_contents = std::mem::take(&mut new_tree.instances);
376 new_tree.attach_kind_cache.clear();
377 let all_server_ids = old_contents
378 .values()
379 .flat_map(|nodes| {
380 nodes.roots.values().flat_map(|servers| {
381 servers.values().filter_map(|server| {
382 server
383 .0
384 .id
385 .get()
386 .copied()
387 .map(|id| (id, server.0.name.clone()))
388 })
389 })
390 })
391 .collect();
392 Self {
393 old_contents,
394 new_tree,
395 all_server_ids,
396 rebased_server_ids: BTreeSet::new(),
397 }
398 }
399
400 pub(crate) fn get<'a>(
401 &'a mut self,
402 path: ProjectPath,
403 query: AdapterQuery<'_>,
404 delegate: Arc<dyn ManifestDelegate>,
405 cx: &mut App,
406 ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
407 let settings_location = SettingsLocation {
408 worktree_id: path.worktree_id,
409 path: &path.path,
410 };
411 let adapters = match query {
412 AdapterQuery::Language(language_name) => {
413 self.new_tree
414 .adapters_for_language(settings_location, language_name, cx)
415 }
416 AdapterQuery::Adapter(language_server_name) => {
417 IndexMap::from_iter(self.new_tree.adapter_for_name(language_server_name).map(
418 |adapter| {
419 (
420 adapter.name(),
421 (LspSettings::default(), BTreeSet::new(), adapter),
422 )
423 },
424 ))
425 }
426 };
427
428 self.new_tree
429 .get_with_adapters(path, adapters, delegate, cx)
430 .filter_map(|node| {
431 // Inspect result of the query and initialize it ourselves before
432 // handing it off to the caller.
433 let disposition = node.0.upgrade()?;
434
435 if disposition.id.get().is_some() {
436 return Some(node);
437 }
438 let Some((existing_node, _)) = self
439 .old_contents
440 .get(&disposition.path.worktree_id)
441 .and_then(|worktree_nodes| worktree_nodes.roots.get(&disposition.path.path))
442 .and_then(|roots| roots.get(&disposition.name))
443 .filter(|(old_node, _)| {
444 disposition.attach == old_node.attach
445 && disposition.settings == old_node.settings
446 })
447 else {
448 return Some(node);
449 };
450 if let Some(existing_id) = existing_node.id.get() {
451 self.rebased_server_ids.insert(*existing_id);
452 disposition.id.set(*existing_id).ok();
453 }
454
455 Some(node)
456 })
457 }
458
459 /// Returns IDs of servers that are no longer referenced (and can be shut down).
460 pub(crate) fn finish(self) -> BTreeMap<LanguageServerId, LanguageServerName> {
461 self.all_server_ids
462 .into_iter()
463 .filter(|(id, _)| !self.rebased_server_ids.contains(id))
464 .collect()
465 }
466
467 pub(crate) fn server_tree(&mut self) -> &mut LanguageServerTree {
468 &mut self.new_tree
469 }
470}