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