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//! This module defines a Project Tree.
10
11use std::{
12 collections::{BTreeMap, BTreeSet},
13 path::Path,
14 sync::{Arc, Weak},
15};
16
17use collections::{HashMap, IndexMap};
18use gpui::{App, AppContext, Entity, Subscription};
19use itertools::Itertools;
20use language::{
21 language_settings::AllLanguageSettings, Attach, LanguageName, LanguageRegistry,
22 LspAdapterDelegate,
23};
24use lsp::LanguageServerName;
25use once_cell::sync::OnceCell;
26use settings::{Settings, SettingsLocation, WorktreeId};
27use util::maybe;
28
29use crate::{project_settings::LspSettings, LanguageServerId, ProjectPath};
30
31use super::{AdapterWrapper, ProjectTree, ProjectTreeEvent};
32
33#[derive(Debug, Default)]
34struct ServersForWorktree {
35 roots: BTreeMap<
36 Arc<Path>,
37 BTreeMap<LanguageServerName, (Arc<InnerTreeNode>, BTreeSet<LanguageName>)>,
38 >,
39}
40
41pub struct LanguageServerTree {
42 project_tree: Entity<ProjectTree>,
43 instances: BTreeMap<WorktreeId, ServersForWorktree>,
44 attach_kind_cache: HashMap<LanguageServerName, Attach>,
45 languages: Arc<LanguageRegistry>,
46 _subscriptions: Subscription,
47}
48
49/// A node in language server tree represents either:
50/// - A language server that has already been initialized/updated for a given project
51/// - A soon-to-be-initialized language server.
52#[derive(Clone)]
53pub(crate) struct LanguageServerTreeNode(Weak<InnerTreeNode>);
54
55/// Describes a request to launch a language server.
56#[derive(Debug)]
57pub(crate) struct LaunchDisposition<'a> {
58 pub(crate) server_name: &'a LanguageServerName,
59 pub(crate) attach: Attach,
60 pub(crate) path: ProjectPath,
61 pub(crate) settings: Arc<LspSettings>,
62}
63
64impl<'a> From<&'a InnerTreeNode> for LaunchDisposition<'a> {
65 fn from(value: &'a InnerTreeNode) -> Self {
66 LaunchDisposition {
67 server_name: &value.name,
68 attach: value.attach,
69 path: value.path.clone(),
70 settings: value.settings.clone(),
71 }
72 }
73}
74impl LanguageServerTreeNode {
75 /// Returns a language server ID for this node if there is one.
76 /// Returns None if this node has not been initialized yet or it is no longer in the tree.
77 pub(crate) fn server_id(&self) -> Option<LanguageServerId> {
78 self.0.upgrade()?.id.get().copied()
79 }
80 /// 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.
81 /// May return None if the node no longer belongs to the server tree it was created in.
82 pub(crate) fn server_id_or_init(
83 &self,
84 init: impl FnOnce(LaunchDisposition) -> LanguageServerId,
85 ) -> Option<LanguageServerId> {
86 self.server_id_or_try_init(|disposition| Ok(init(disposition)))
87 }
88 fn server_id_or_try_init(
89 &self,
90 init: impl FnOnce(LaunchDisposition) -> Result<LanguageServerId, ()>,
91 ) -> Option<LanguageServerId> {
92 let this = self.0.upgrade()?;
93 this.id
94 .get_or_try_init(|| init(LaunchDisposition::from(&*this)))
95 .ok()
96 .copied()
97 }
98}
99
100impl From<Weak<InnerTreeNode>> for LanguageServerTreeNode {
101 fn from(weak: Weak<InnerTreeNode>) -> Self {
102 LanguageServerTreeNode(weak)
103 }
104}
105
106#[derive(Debug)]
107struct InnerTreeNode {
108 id: OnceCell<LanguageServerId>,
109 name: LanguageServerName,
110 attach: Attach,
111 path: ProjectPath,
112 settings: Arc<LspSettings>,
113}
114
115impl InnerTreeNode {
116 fn new(
117 name: LanguageServerName,
118 attach: Attach,
119 path: ProjectPath,
120 settings: impl Into<Arc<LspSettings>>,
121 ) -> Self {
122 InnerTreeNode {
123 id: Default::default(),
124 name,
125 attach,
126 path,
127 settings: settings.into(),
128 }
129 }
130}
131
132/// Determines how the list of adapters to query should be constructed.
133pub(crate) enum AdapterQuery<'a> {
134 /// Search for roots of all adapters associated with a given language name.
135 Language(&'a LanguageName),
136 /// Search for roots of adapter with a given name.
137 Adapter(&'a LanguageServerName),
138}
139
140impl LanguageServerTree {
141 pub(crate) fn new(
142 project_tree: Entity<ProjectTree>,
143 languages: Arc<LanguageRegistry>,
144 cx: &mut App,
145 ) -> Entity<Self> {
146 cx.new(|cx| Self {
147 _subscriptions: cx.subscribe(
148 &project_tree,
149 |_: &mut Self, _, event, _| {
150 if event == &ProjectTreeEvent::Cleared {}
151 },
152 ),
153 project_tree,
154 instances: Default::default(),
155 attach_kind_cache: Default::default(),
156 languages,
157 })
158 }
159 /// Memoize calls to attach_kind on LspAdapter (which might be a WASM extension, thus ~expensive to call).
160 fn attach_kind(&mut self, adapter: &AdapterWrapper) -> Attach {
161 *self
162 .attach_kind_cache
163 .entry(adapter.0.name.clone())
164 .or_insert_with(|| adapter.0.attach_kind())
165 }
166
167 /// Get all language server root points for a given path and language; the language servers might already be initialized at a given path.
168 pub(crate) fn get<'a>(
169 &'a mut self,
170 path: ProjectPath,
171 query: AdapterQuery<'_>,
172 delegate: Arc<dyn LspAdapterDelegate>,
173 cx: &mut App,
174 ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
175 let settings_location = SettingsLocation {
176 worktree_id: path.worktree_id,
177 path: &path.path,
178 };
179 let adapters = match query {
180 AdapterQuery::Language(language_name) => {
181 self.adapters_for_language(settings_location, language_name, cx)
182 }
183 AdapterQuery::Adapter(language_server_name) => IndexMap::from_iter(
184 self.adapter_for_name(language_server_name)
185 .map(|adapter| (adapter, (LspSettings::default(), BTreeSet::new()))),
186 ),
187 };
188 self.get_with_adapters(path, adapters, delegate, cx)
189 }
190
191 fn get_with_adapters<'a>(
192 &'a mut self,
193 path: ProjectPath,
194 adapters: IndexMap<AdapterWrapper, (LspSettings, BTreeSet<LanguageName>)>,
195 delegate: Arc<dyn LspAdapterDelegate>,
196 cx: &mut App,
197 ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
198 let worktree_id = path.worktree_id;
199 #[allow(clippy::mutable_key_type)]
200 let mut roots = self.project_tree.update(cx, |this, cx| {
201 this.root_for_path(
202 path,
203 adapters
204 .iter()
205 .map(|(adapter, _)| adapter.0.clone())
206 .collect(),
207 delegate,
208 cx,
209 )
210 });
211 let mut root_path = None;
212 // 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.
213 for (adapter, _) in adapters.iter() {
214 roots.entry(adapter.clone()).or_insert_with(|| {
215 root_path
216 .get_or_insert_with(|| ProjectPath {
217 worktree_id,
218 path: Arc::from("".as_ref()),
219 })
220 .clone()
221 });
222 }
223
224 roots
225 .into_iter()
226 .filter_map(move |(adapter, root_path)| {
227 let attach = self.attach_kind(&adapter);
228 let (index, _, (settings, new_languages)) = adapters.get_full(&adapter)?;
229 let inner_node = self
230 .instances
231 .entry(root_path.worktree_id)
232 .or_default()
233 .roots
234 .entry(root_path.path.clone())
235 .or_default()
236 .entry(adapter.0.name.clone());
237 let (node, languages) = inner_node.or_insert_with(move || {
238 (
239 Arc::new(InnerTreeNode::new(
240 adapter.0.name(),
241 attach,
242 root_path,
243 settings.clone(),
244 )),
245 Default::default(),
246 )
247 });
248 languages.extend(new_languages.iter().cloned());
249 Some((index, Arc::downgrade(&node).into()))
250 })
251 .sorted_by_key(|(index, _)| *index)
252 .map(|(_, node)| node)
253 }
254
255 fn adapter_for_name(&self, name: &LanguageServerName) -> Option<AdapterWrapper> {
256 self.languages.adapter_for_name(name).map(AdapterWrapper)
257 }
258
259 fn adapters_for_language(
260 &self,
261 settings_location: SettingsLocation,
262 language_name: &LanguageName,
263 cx: &App,
264 ) -> IndexMap<AdapterWrapper, (LspSettings, BTreeSet<LanguageName>)> {
265 let settings = AllLanguageSettings::get(Some(settings_location), cx).language(
266 Some(settings_location),
267 Some(language_name),
268 cx,
269 );
270 if !settings.enable_language_server {
271 return Default::default();
272 }
273 let available_lsp_adapters = self.languages.lsp_adapters(&language_name);
274 let available_language_servers = available_lsp_adapters
275 .iter()
276 .map(|lsp_adapter| lsp_adapter.name.clone())
277 .collect::<Vec<_>>();
278
279 let desired_language_servers =
280 settings.customized_language_servers(&available_language_servers);
281 let adapters_with_settings = desired_language_servers
282 .into_iter()
283 .filter_map(|desired_adapter| {
284 let adapter = if let Some(adapter) = available_lsp_adapters
285 .iter()
286 .find(|adapter| adapter.name == desired_adapter)
287 {
288 Some(adapter.clone())
289 } else if let Some(adapter) =
290 self.languages.load_available_lsp_adapter(&desired_adapter)
291 {
292 self.languages
293 .register_lsp_adapter(language_name.clone(), adapter.adapter.clone());
294 Some(adapter)
295 } else {
296 None
297 }?;
298 let adapter_settings = crate::lsp_store::language_server_settings_for(
299 settings_location,
300 &adapter.name,
301 cx,
302 )
303 .cloned()
304 .unwrap_or_default();
305 Some((
306 AdapterWrapper(adapter),
307 (
308 adapter_settings,
309 BTreeSet::from_iter([language_name.clone()]),
310 ),
311 ))
312 })
313 .collect::<IndexMap<_, _>>();
314 // After starting all the language servers, reorder them to reflect the desired order
315 // based on the settings.
316 //
317 // This is done, in part, to ensure that language servers loaded at different points
318 // (e.g., native vs extension) still end up in the right order at the end, rather than
319 // it being based on which language server happened to be loaded in first.
320 self.languages.reorder_language_servers(
321 &language_name,
322 adapters_with_settings
323 .keys()
324 .map(|wrapper| wrapper.0.clone())
325 .collect(),
326 );
327
328 adapters_with_settings
329 }
330
331 pub(crate) fn on_settings_changed(
332 &mut self,
333 get_delegate: &mut dyn FnMut(WorktreeId, &mut App) -> Option<Arc<dyn LspAdapterDelegate>>,
334 spawn_language_server: &mut dyn FnMut(LaunchDisposition, &mut App) -> LanguageServerId,
335 on_language_server_removed: &mut dyn FnMut(LanguageServerId),
336 cx: &mut App,
337 ) {
338 // Settings are checked at query time. Thus, to avoid messing with inference of applicable settings, we're just going to clear ourselves and let the next query repopulate.
339 // We're going to optimistically re-run the queries and re-assign the same language server id when a language server still exists at a given tree node.
340 let old_instances = std::mem::take(&mut self.instances);
341 let old_attach_kinds = std::mem::take(&mut self.attach_kind_cache);
342
343 let mut referenced_instances = BTreeSet::new();
344 // Re-map the old tree onto a new one. In the process we'll get a list of servers we have to shut down.
345 let mut all_instances = BTreeSet::new();
346
347 for (worktree_id, servers) in &old_instances {
348 // Record all initialized node ids.
349 all_instances.extend(servers.roots.values().flat_map(|servers_at_node| {
350 servers_at_node
351 .values()
352 .filter_map(|(server_node, _)| server_node.id.get().copied())
353 }));
354 let Some(delegate) = get_delegate(*worktree_id, cx) else {
355 // If worktree is no longer around, we're just going to shut down all of the language servers (since they've been added to all_instances).
356 continue;
357 };
358
359 for (path, servers_for_path) in &servers.roots {
360 for (server_name, (_, languages)) in servers_for_path {
361 let settings_location = SettingsLocation {
362 worktree_id: *worktree_id,
363 path: &path,
364 };
365 // Verify which of the previous languages still have this server enabled.
366
367 let mut adapter_with_settings = IndexMap::default();
368
369 for language_name in languages {
370 self.adapters_for_language(settings_location, language_name, cx)
371 .into_iter()
372 .for_each(|(lsp_adapter, lsp_settings)| {
373 if &lsp_adapter.0.name() != server_name {
374 return;
375 }
376 adapter_with_settings
377 .entry(lsp_adapter)
378 .and_modify(|x: &mut (_, BTreeSet<LanguageName>)| {
379 x.1.extend(lsp_settings.1.clone())
380 })
381 .or_insert(lsp_settings);
382 });
383 }
384
385 if adapter_with_settings.is_empty() {
386 // Since all languages that have had this server enabled are now disabled, we can remove the server entirely.
387 continue;
388 };
389
390 for new_node in self.get_with_adapters(
391 ProjectPath {
392 path: path.clone(),
393 worktree_id: *worktree_id,
394 },
395 adapter_with_settings,
396 delegate.clone(),
397 cx,
398 ) {
399 new_node.server_id_or_try_init(|disposition| {
400 let Some((existing_node, _)) = servers
401 .roots
402 .get(&disposition.path.path)
403 .and_then(|roots| roots.get(disposition.server_name))
404 .filter(|(old_node, _)| {
405 old_attach_kinds.get(disposition.server_name).map_or(
406 false,
407 |old_attach| {
408 disposition.attach == *old_attach
409 && disposition.settings == old_node.settings
410 },
411 )
412 })
413 else {
414 return Ok(spawn_language_server(disposition, cx));
415 };
416 if let Some(id) = existing_node.id.get().copied() {
417 // If we have a node with ID assigned (and it's parameters match `disposition`), reuse the id.
418 referenced_instances.insert(id);
419 Ok(id)
420 } else {
421 // Otherwise, if we do have a node but it does not have an ID assigned, keep it that way.
422 Err(())
423 }
424 });
425 }
426 }
427 }
428 }
429 for server_to_remove in all_instances.difference(&referenced_instances) {
430 on_language_server_removed(*server_to_remove);
431 }
432 }
433
434 /// Updates nodes in language server tree in place, changing the ID of initialized nodes.
435 pub(crate) fn restart_language_servers(
436 &mut self,
437 worktree_id: WorktreeId,
438 ids: BTreeSet<LanguageServerId>,
439 restart_callback: &mut dyn FnMut(LanguageServerId, LaunchDisposition) -> LanguageServerId,
440 ) {
441 maybe! {{
442 for (_, nodes) in &mut self.instances.get_mut(&worktree_id)?.roots {
443 for (_, (node, _)) in nodes {
444 let Some(old_server_id) = node.id.get().copied() else {
445 continue;
446 };
447 if !ids.contains(&old_server_id) {
448 continue;
449 }
450
451 let new_id = restart_callback(old_server_id, LaunchDisposition::from(&**node));
452
453 *node = Arc::new(InnerTreeNode::new(node.name.clone(), node.attach, node.path.clone(), node.settings.clone()));
454 node.id.set(new_id).expect("The id to be unset after clearing the node.");
455 }
456 }
457 Some(())
458 }
459 };
460 }
461}