server_tree.rs

  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}