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 as _, Entity, Subscription};
 19use itertools::Itertools;
 20use language::{
 21    language_settings::AllLanguageSettings, Attach, LanguageName, LanguageRegistry,
 22    LspAdapterDelegate,
 23};
 24use lsp::LanguageServerName;
 25use settings::{Settings, SettingsLocation, WorktreeId};
 26use std::sync::OnceLock;
 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: Entity<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        let this = self.0.upgrade()?;
 86        Some(
 87            *this
 88                .id
 89                .get_or_init(|| init(LaunchDisposition::from(&*this))),
 90        )
 91    }
 92}
 93
 94impl From<Weak<InnerTreeNode>> for LanguageServerTreeNode {
 95    fn from(weak: Weak<InnerTreeNode>) -> Self {
 96        LanguageServerTreeNode(weak)
 97    }
 98}
 99
100#[derive(Debug)]
101struct InnerTreeNode {
102    id: OnceLock<LanguageServerId>,
103    name: LanguageServerName,
104    attach: Attach,
105    path: ProjectPath,
106    settings: Arc<LspSettings>,
107}
108
109impl InnerTreeNode {
110    fn new(
111        name: LanguageServerName,
112        attach: Attach,
113        path: ProjectPath,
114        settings: impl Into<Arc<LspSettings>>,
115    ) -> Self {
116        InnerTreeNode {
117            id: Default::default(),
118            name,
119            attach,
120            path,
121            settings: settings.into(),
122        }
123    }
124}
125
126/// Determines how the list of adapters to query should be constructed.
127pub(crate) enum AdapterQuery<'a> {
128    /// Search for roots of all adapters associated with a given language name.
129    Language(&'a LanguageName),
130    /// Search for roots of adapter with a given name.
131    Adapter(&'a LanguageServerName),
132}
133
134impl LanguageServerTree {
135    pub(crate) fn new(
136        project_tree: Entity<ProjectTree>,
137        languages: Arc<LanguageRegistry>,
138        cx: &mut App,
139    ) -> Entity<Self> {
140        cx.new(|cx| Self {
141            _subscriptions: cx.subscribe(
142                &project_tree,
143                |_: &mut Self, _, event, _| {
144                    if event == &ProjectTreeEvent::Cleared {}
145                },
146            ),
147            project_tree,
148            instances: Default::default(),
149            attach_kind_cache: Default::default(),
150            languages,
151        })
152    }
153    /// Memoize calls to attach_kind on LspAdapter (which might be a WASM extension, thus ~expensive to call).
154    fn attach_kind(&mut self, adapter: &AdapterWrapper) -> Attach {
155        *self
156            .attach_kind_cache
157            .entry(adapter.0.name.clone())
158            .or_insert_with(|| adapter.0.attach_kind())
159    }
160
161    /// Get all language server root points for a given path and language; the language servers might already be initialized at a given path.
162    pub(crate) fn get<'a>(
163        &'a mut self,
164        path: ProjectPath,
165        query: AdapterQuery<'_>,
166        delegate: Arc<dyn LspAdapterDelegate>,
167        cx: &mut App,
168    ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
169        let settings_location = SettingsLocation {
170            worktree_id: path.worktree_id,
171            path: &path.path,
172        };
173        let adapters = match query {
174            AdapterQuery::Language(language_name) => {
175                self.adapters_for_language(settings_location, language_name, cx)
176            }
177            AdapterQuery::Adapter(language_server_name) => IndexMap::from_iter(
178                self.adapter_for_name(language_server_name)
179                    .map(|adapter| (adapter, (LspSettings::default(), BTreeSet::new()))),
180            ),
181        };
182        self.get_with_adapters(path, adapters, delegate, cx)
183    }
184
185    fn get_with_adapters<'a>(
186        &'a mut self,
187        path: ProjectPath,
188        adapters: IndexMap<AdapterWrapper, (LspSettings, BTreeSet<LanguageName>)>,
189        delegate: Arc<dyn LspAdapterDelegate>,
190        cx: &mut App,
191    ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
192        let worktree_id = path.worktree_id;
193        #[allow(clippy::mutable_key_type)]
194        let mut roots = self.project_tree.update(cx, |this, cx| {
195            this.root_for_path(
196                path,
197                adapters
198                    .iter()
199                    .map(|(adapter, _)| adapter.0.clone())
200                    .collect(),
201                delegate,
202                cx,
203            )
204        });
205        let mut root_path = None;
206        // 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.
207        for (adapter, _) in adapters.iter() {
208            roots.entry(adapter.clone()).or_insert_with(|| {
209                root_path
210                    .get_or_insert_with(|| ProjectPath {
211                        worktree_id,
212                        path: Arc::from("".as_ref()),
213                    })
214                    .clone()
215            });
216        }
217
218        roots
219            .into_iter()
220            .filter_map(move |(adapter, root_path)| {
221                let attach = self.attach_kind(&adapter);
222                let (index, _, (settings, new_languages)) = adapters.get_full(&adapter)?;
223                let inner_node = self
224                    .instances
225                    .entry(root_path.worktree_id)
226                    .or_default()
227                    .roots
228                    .entry(root_path.path.clone())
229                    .or_default()
230                    .entry(adapter.0.name.clone());
231                let (node, languages) = inner_node.or_insert_with(move || {
232                    (
233                        Arc::new(InnerTreeNode::new(
234                            adapter.0.name(),
235                            attach,
236                            root_path,
237                            settings.clone(),
238                        )),
239                        Default::default(),
240                    )
241                });
242                languages.extend(new_languages.iter().cloned());
243                Some((index, Arc::downgrade(&node).into()))
244            })
245            .sorted_by_key(|(index, _)| *index)
246            .map(|(_, node)| node)
247    }
248
249    fn adapter_for_name(&self, name: &LanguageServerName) -> Option<AdapterWrapper> {
250        self.languages.adapter_for_name(name).map(AdapterWrapper)
251    }
252
253    fn adapters_for_language(
254        &self,
255        settings_location: SettingsLocation,
256        language_name: &LanguageName,
257        cx: &App,
258    ) -> IndexMap<AdapterWrapper, (LspSettings, BTreeSet<LanguageName>)> {
259        let settings = AllLanguageSettings::get(Some(settings_location), cx).language(
260            Some(settings_location),
261            Some(language_name),
262            cx,
263        );
264        if !settings.enable_language_server {
265            return Default::default();
266        }
267        let available_lsp_adapters = self.languages.lsp_adapters(&language_name);
268        let available_language_servers = available_lsp_adapters
269            .iter()
270            .map(|lsp_adapter| lsp_adapter.name.clone())
271            .collect::<Vec<_>>();
272
273        let desired_language_servers =
274            settings.customized_language_servers(&available_language_servers);
275        let adapters_with_settings = desired_language_servers
276            .into_iter()
277            .filter_map(|desired_adapter| {
278                let adapter = if let Some(adapter) = available_lsp_adapters
279                    .iter()
280                    .find(|adapter| adapter.name == desired_adapter)
281                {
282                    Some(adapter.clone())
283                } else if let Some(adapter) =
284                    self.languages.load_available_lsp_adapter(&desired_adapter)
285                {
286                    self.languages
287                        .register_lsp_adapter(language_name.clone(), adapter.adapter.clone());
288                    Some(adapter)
289                } else {
290                    None
291                }?;
292                let adapter_settings = crate::lsp_store::language_server_settings_for(
293                    settings_location,
294                    &adapter.name,
295                    cx,
296                )
297                .cloned()
298                .unwrap_or_default();
299                Some((
300                    AdapterWrapper(adapter),
301                    (
302                        adapter_settings,
303                        BTreeSet::from_iter([language_name.clone()]),
304                    ),
305                ))
306            })
307            .collect::<IndexMap<_, _>>();
308        // After starting all the language servers, reorder them to reflect the desired order
309        // based on the settings.
310        //
311        // This is done, in part, to ensure that language servers loaded at different points
312        // (e.g., native vs extension) still end up in the right order at the end, rather than
313        // it being based on which language server happened to be loaded in first.
314        self.languages.reorder_language_servers(
315            &language_name,
316            adapters_with_settings
317                .keys()
318                .map(|wrapper| wrapper.0.clone())
319                .collect(),
320        );
321
322        adapters_with_settings
323    }
324
325    // Rebasing a tree:
326    // - Clears it out
327    // - Provides you with the indirect access to the old tree while you're reinitializing a new one (by querying it).
328    pub(crate) fn rebase(&mut self) -> ServerTreeRebase<'_> {
329        ServerTreeRebase::new(self)
330    }
331
332    /// Remove nodes with a given ID from the tree.
333    pub(crate) fn remove_nodes(&mut self, ids: &BTreeSet<LanguageServerId>) {
334        for (_, servers) in &mut self.instances {
335            for (_, nodes) in &mut servers.roots {
336                nodes.retain(|_, (node, _)| node.id.get().map_or(true, |id| !ids.contains(&id)));
337            }
338        }
339    }
340}
341
342pub(crate) struct ServerTreeRebase<'a> {
343    old_contents: BTreeMap<WorktreeId, ServersForWorktree>,
344    new_tree: &'a mut LanguageServerTree,
345    /// All server IDs seen in the old tree.
346    all_server_ids: BTreeMap<LanguageServerId, LanguageServerName>,
347    /// Server IDs we've preserved for a new iteration of the tree. `all_server_ids - rebased_server_ids` is the
348    /// set of server IDs that can be shut down.
349    rebased_server_ids: BTreeSet<LanguageServerId>,
350}
351
352impl<'tree> ServerTreeRebase<'tree> {
353    fn new(new_tree: &'tree mut LanguageServerTree) -> Self {
354        let old_contents = std::mem::take(&mut new_tree.instances);
355        new_tree.attach_kind_cache.clear();
356        let all_server_ids = old_contents
357            .values()
358            .flat_map(|nodes| {
359                nodes.roots.values().flat_map(|servers| {
360                    servers.values().filter_map(|server| {
361                        server
362                            .0
363                            .id
364                            .get()
365                            .copied()
366                            .map(|id| (id, server.0.name.clone()))
367                    })
368                })
369            })
370            .collect();
371        Self {
372            old_contents,
373            new_tree,
374            all_server_ids,
375            rebased_server_ids: BTreeSet::new(),
376        }
377    }
378
379    pub(crate) fn get<'a>(
380        &'a mut self,
381        path: ProjectPath,
382        query: AdapterQuery<'_>,
383        delegate: Arc<dyn LspAdapterDelegate>,
384        cx: &mut App,
385    ) -> impl Iterator<Item = LanguageServerTreeNode> + 'a {
386        let settings_location = SettingsLocation {
387            worktree_id: path.worktree_id,
388            path: &path.path,
389        };
390        let adapters = match query {
391            AdapterQuery::Language(language_name) => {
392                self.new_tree
393                    .adapters_for_language(settings_location, language_name, cx)
394            }
395            AdapterQuery::Adapter(language_server_name) => IndexMap::from_iter(
396                self.new_tree
397                    .adapter_for_name(language_server_name)
398                    .map(|adapter| (adapter, (LspSettings::default(), BTreeSet::new()))),
399            ),
400        };
401
402        self.new_tree
403            .get_with_adapters(path, adapters, delegate, cx)
404            .filter_map(|node| {
405                // Inspect result of the query and initialize it ourselves before
406                // handing it off to the caller.
407                let disposition = node.0.upgrade()?;
408
409                if disposition.id.get().is_some() {
410                    return Some(node);
411                }
412                let Some((existing_node, _)) = self
413                    .old_contents
414                    .get(&disposition.path.worktree_id)
415                    .and_then(|worktree_nodes| worktree_nodes.roots.get(&disposition.path.path))
416                    .and_then(|roots| roots.get(&disposition.name))
417                    .filter(|(old_node, _)| {
418                        disposition.attach == old_node.attach
419                            && disposition.settings == old_node.settings
420                    })
421                else {
422                    return Some(node);
423                };
424                if let Some(existing_id) = existing_node.id.get() {
425                    self.rebased_server_ids.insert(*existing_id);
426                    disposition.id.set(*existing_id).ok();
427                }
428
429                Some(node)
430            })
431    }
432
433    /// Returns IDs of servers that are no longer referenced (and can be shut down).
434    pub(crate) fn finish(self) -> BTreeMap<LanguageServerId, LanguageServerName> {
435        self.all_server_ids
436            .into_iter()
437            .filter(|(id, _)| !self.rebased_server_ids.contains(id))
438            .collect()
439    }
440}