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
131impl LanguageServerTree {
132 pub(crate) fn new(
133 project_tree: Model<ProjectTree>,
134 languages: Arc<LanguageRegistry>,
135 cx: &mut AppContext,
136 ) -> Model<Self> {
137 cx.new_model(|cx| Self {
138 _subscriptions: cx.subscribe(
139 &project_tree,
140 |_: &mut Self, _, event, _| {
141 if event == &ProjectTreeEvent::Cleared {}
142 },
143 ),
144 project_tree,
145 instances: Default::default(),
146 attach_kind_cache: Default::default(),
147 languages,
148 })
149 }
150 /// Memoize calls to attach_kind on LspAdapter (which might be a WASM extension, thus ~expensive to call).
151 fn attach_kind(&mut self, adapter: &AdapterWrapper) -> Attach {
152 *self
153 .attach_kind_cache
154 .entry(adapter.0.name.clone())
155 .or_insert_with(|| adapter.0.attach_kind())
156 }
157
158 /// Get all language server root points for a given path and language; the language servers might already be initialized at a given path.
159 pub(crate) fn get<'a>(
160 &'a mut self,
161 path: ProjectPath,
162 language_name: &LanguageName,
163 delegate: Arc<dyn LspAdapterDelegate>,
164 cx: &mut AppContext,
165 ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
166 let settings_location = SettingsLocation {
167 worktree_id: path.worktree_id,
168 path: &path.path,
169 };
170 let adapters = self.adapters_for_language(settings_location, language_name, cx);
171 self.get_with_adapters(path, adapters, delegate, cx)
172 }
173
174 fn get_with_adapters<'a>(
175 &'a mut self,
176 path: ProjectPath,
177 adapters: IndexMap<AdapterWrapper, (LspSettings, BTreeSet<LanguageName>)>,
178 delegate: Arc<dyn LspAdapterDelegate>,
179 cx: &mut AppContext,
180 ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
181 let worktree_id = path.worktree_id;
182 #[allow(clippy::mutable_key_type)]
183 let mut roots = self.project_tree.update(cx, |this, cx| {
184 this.root_for_path(
185 path,
186 adapters
187 .iter()
188 .map(|(adapter, _)| adapter.0.clone())
189 .collect(),
190 delegate,
191 cx,
192 )
193 });
194 let mut root_path = None;
195 // 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.
196 for (adapter, _) in adapters.iter() {
197 roots.entry(adapter.clone()).or_insert_with(|| {
198 root_path
199 .get_or_insert_with(|| ProjectPath {
200 worktree_id,
201 path: Arc::from("".as_ref()),
202 })
203 .clone()
204 });
205 }
206
207 roots.into_iter().filter_map(move |(adapter, root_path)| {
208 let attach = self.attach_kind(&adapter);
209 let (settings, new_languages) = adapters.get(&adapter).cloned()?;
210 let inner_node = self
211 .instances
212 .entry(root_path.worktree_id)
213 .or_default()
214 .roots
215 .entry(root_path.path.clone())
216 .or_default()
217 .entry(adapter.0.name.clone());
218 let (node, languages) = inner_node.or_insert_with(move || {
219 (
220 Arc::new(InnerTreeNode::new(
221 adapter.0.name(),
222 attach,
223 root_path,
224 settings,
225 )),
226 Default::default(),
227 )
228 });
229 languages.extend(new_languages);
230 Some(Arc::downgrade(&node).into())
231 })
232 }
233
234 fn adapters_for_language(
235 &self,
236 settings_location: SettingsLocation,
237 language_name: &LanguageName,
238 cx: &AppContext,
239 ) -> IndexMap<AdapterWrapper, (LspSettings, BTreeSet<LanguageName>)> {
240 let settings = AllLanguageSettings::get(Some(settings_location), cx).language(
241 Some(settings_location),
242 Some(language_name),
243 cx,
244 );
245 if !settings.enable_language_server {
246 return Default::default();
247 }
248 let available_lsp_adapters = self.languages.lsp_adapters(&language_name);
249 let available_language_servers = available_lsp_adapters
250 .iter()
251 .map(|lsp_adapter| lsp_adapter.name.clone())
252 .collect::<Vec<_>>();
253
254 let desired_language_servers =
255 settings.customized_language_servers(&available_language_servers);
256 let adapters_with_settings = desired_language_servers
257 .into_iter()
258 .filter_map(|desired_adapter| {
259 let adapter = if let Some(adapter) = available_lsp_adapters
260 .iter()
261 .find(|adapter| adapter.name == desired_adapter)
262 {
263 Some(adapter.clone())
264 } else if let Some(adapter) =
265 self.languages.load_available_lsp_adapter(&desired_adapter)
266 {
267 self.languages
268 .register_lsp_adapter(language_name.clone(), adapter.adapter.clone());
269 Some(adapter)
270 } else {
271 None
272 }?;
273 let adapter_settings = crate::lsp_store::language_server_settings_for(
274 settings_location,
275 &adapter.name,
276 cx,
277 )
278 .cloned()
279 .unwrap_or_default();
280 Some((
281 AdapterWrapper(adapter),
282 (
283 adapter_settings,
284 BTreeSet::from_iter([language_name.clone()]),
285 ),
286 ))
287 })
288 .collect::<IndexMap<_, _>>();
289 adapters_with_settings
290 }
291
292 pub(crate) fn on_settings_changed(
293 &mut self,
294 get_delegate: &mut dyn FnMut(
295 WorktreeId,
296 &mut AppContext,
297 ) -> Option<Arc<dyn LspAdapterDelegate>>,
298 spawn_language_server: &mut dyn FnMut(
299 LaunchDisposition,
300 &mut AppContext,
301 ) -> LanguageServerId,
302 on_language_server_removed: &mut dyn FnMut(LanguageServerId),
303 cx: &mut AppContext,
304 ) {
305 // 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.
306 // 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.
307 let old_instances = std::mem::take(&mut self.instances);
308 let old_attach_kinds = std::mem::take(&mut self.attach_kind_cache);
309
310 let mut referenced_instances = BTreeSet::new();
311 // Re-map the old tree onto a new one. In the process we'll get a list of servers we have to shut down.
312 let mut all_instances = BTreeSet::new();
313
314 for (worktree_id, servers) in &old_instances {
315 // Record all initialized node ids.
316 all_instances.extend(servers.roots.values().flat_map(|servers_at_node| {
317 servers_at_node
318 .values()
319 .filter_map(|(server_node, _)| server_node.id.get().copied())
320 }));
321 let Some(delegate) = get_delegate(*worktree_id, cx) else {
322 // 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).
323 continue;
324 };
325
326 for (path, servers_for_path) in &servers.roots {
327 for (server_name, (_, languages)) in servers_for_path {
328 let settings_location = SettingsLocation {
329 worktree_id: *worktree_id,
330 path: &path,
331 };
332 // Verify which of the previous languages still have this server enabled.
333
334 let mut adapter_with_settings = IndexMap::default();
335
336 for language_name in languages {
337 self.adapters_for_language(settings_location, language_name, cx)
338 .into_iter()
339 .for_each(|(lsp_adapter, lsp_settings)| {
340 if &lsp_adapter.0.name() != server_name {
341 return;
342 }
343 adapter_with_settings
344 .entry(lsp_adapter)
345 .and_modify(|x: &mut (_, BTreeSet<LanguageName>)| {
346 x.1.extend(lsp_settings.1.clone())
347 })
348 .or_insert(lsp_settings);
349 });
350 }
351
352 if adapter_with_settings.is_empty() {
353 // Since all languages that have had this server enabled are now disabled, we can remove the server entirely.
354 continue;
355 };
356
357 for new_node in self.get_with_adapters(
358 ProjectPath {
359 path: path.clone(),
360 worktree_id: *worktree_id,
361 },
362 adapter_with_settings,
363 delegate.clone(),
364 cx,
365 ) {
366 new_node.server_id_or_try_init(|disposition| {
367 let Some((existing_node, _)) = servers
368 .roots
369 .get(&disposition.path.path)
370 .and_then(|roots| roots.get(disposition.server_name))
371 .filter(|(old_node, _)| {
372 old_attach_kinds.get(disposition.server_name).map_or(
373 false,
374 |old_attach| {
375 disposition.attach == *old_attach
376 && disposition.settings == old_node.settings
377 },
378 )
379 })
380 else {
381 return Ok(spawn_language_server(disposition, cx));
382 };
383 if let Some(id) = existing_node.id.get().copied() {
384 // If we have a node with ID assigned (and it's parameters match `disposition`), reuse the id.
385 referenced_instances.insert(id);
386 Ok(id)
387 } else {
388 // Otherwise, if we do have a node but it does not have an ID assigned, keep it that way.
389 Err(())
390 }
391 });
392 }
393 }
394 }
395 }
396 for server_to_remove in all_instances.difference(&referenced_instances) {
397 on_language_server_removed(*server_to_remove);
398 }
399 }
400
401 /// Updates nodes in language server tree in place, changing the ID of initialized nodes.
402 pub(crate) fn restart_language_servers(
403 &mut self,
404 worktree_id: WorktreeId,
405 ids: BTreeSet<LanguageServerId>,
406 restart_callback: &mut dyn FnMut(LanguageServerId, LaunchDisposition) -> LanguageServerId,
407 ) {
408 maybe! {{
409 for (_, nodes) in &mut self.instances.get_mut(&worktree_id)?.roots {
410 for (_, (node, _)) in nodes {
411 let Some(old_server_id) = node.id.get().copied() else {
412 continue;
413 };
414 if !ids.contains(&old_server_id) {
415 continue;
416 }
417
418 let new_id = restart_callback(old_server_id, LaunchDisposition::from(&**node));
419
420 *node = Arc::new(InnerTreeNode::new(node.name.clone(), node.attach, node.path.clone(), node.settings.clone()));
421 node.id.set(new_id).expect("The id to be unset after clearing the node.");
422 }
423 }
424 Some(())
425 }
426 };
427 }
428}