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::{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}