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