manifest_tree.rs

  1//! This module defines a Manifest Tree.
  2//!
  3//! A Manifest Tree is responsible for determining where the manifests for subprojects are located in a project.
  4//! This then is used to provide those locations to language servers & determine locations eligible for toolchain selection.
  5
  6mod manifest_store;
  7mod path_trie;
  8mod server_tree;
  9
 10use std::{
 11    borrow::Borrow,
 12    collections::{BTreeMap, hash_map::Entry},
 13    ops::ControlFlow,
 14    path::Path,
 15    sync::Arc,
 16};
 17
 18use collections::HashMap;
 19use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription};
 20use language::{ManifestDelegate, ManifestName, ManifestQuery};
 21pub use manifest_store::ManifestProviders;
 22use path_trie::{LabelPresence, RootPathTrie, TriePath};
 23use settings::{SettingsStore, WorktreeId};
 24use worktree::{Event as WorktreeEvent, Snapshot, Worktree};
 25
 26use crate::{
 27    ProjectPath,
 28    worktree_store::{WorktreeStore, WorktreeStoreEvent},
 29};
 30
 31pub(crate) use server_tree::{
 32    AdapterQuery, LanguageServerTree, LanguageServerTreeNode, LaunchDisposition,
 33};
 34
 35struct WorktreeRoots {
 36    roots: RootPathTrie<ManifestName>,
 37    worktree_store: Entity<WorktreeStore>,
 38    _worktree_subscription: Subscription,
 39}
 40
 41impl WorktreeRoots {
 42    fn new(
 43        worktree_store: Entity<WorktreeStore>,
 44        worktree: Entity<Worktree>,
 45        cx: &mut App,
 46    ) -> Entity<Self> {
 47        cx.new(|cx| Self {
 48            roots: RootPathTrie::new(),
 49            worktree_store,
 50            _worktree_subscription: cx.subscribe(&worktree, |this: &mut Self, _, event, cx| {
 51                match event {
 52                    WorktreeEvent::UpdatedEntries(changes) => {
 53                        for (path, _, kind) in changes.iter() {
 54                            match kind {
 55                                worktree::PathChange::Removed => {
 56                                    let path = TriePath::from(path.as_ref());
 57                                    this.roots.remove(&path);
 58                                }
 59                                _ => {}
 60                            }
 61                        }
 62                    }
 63                    WorktreeEvent::UpdatedGitRepositories(_) => {}
 64                    WorktreeEvent::DeletedEntry(entry_id) => {
 65                        let Some(entry) = this.worktree_store.read(cx).entry_for_id(*entry_id, cx)
 66                        else {
 67                            return;
 68                        };
 69                        let path = TriePath::from(entry.path.as_ref());
 70                        this.roots.remove(&path);
 71                    }
 72                }
 73            }),
 74        })
 75    }
 76}
 77
 78pub struct ManifestTree {
 79    root_points: HashMap<WorktreeId, Entity<WorktreeRoots>>,
 80    worktree_store: Entity<WorktreeStore>,
 81    _subscriptions: [Subscription; 2],
 82}
 83
 84#[derive(PartialEq)]
 85pub(crate) enum ManifestTreeEvent {
 86    WorktreeRemoved(WorktreeId),
 87    Cleared,
 88}
 89
 90impl EventEmitter<ManifestTreeEvent> for ManifestTree {}
 91
 92impl ManifestTree {
 93    pub fn new(worktree_store: Entity<WorktreeStore>, cx: &mut App) -> Entity<Self> {
 94        cx.new(|cx| Self {
 95            root_points: Default::default(),
 96            _subscriptions: [
 97                cx.subscribe(&worktree_store, Self::on_worktree_store_event),
 98                cx.observe_global::<SettingsStore>(|this, cx| {
 99                    for (_, roots) in &mut this.root_points {
100                        roots.update(cx, |worktree_roots, _| {
101                            worktree_roots.roots = RootPathTrie::new();
102                        })
103                    }
104                    cx.emit(ManifestTreeEvent::Cleared);
105                }),
106            ],
107            worktree_store,
108        })
109    }
110    pub(crate) fn root_for_path(
111        &mut self,
112        ProjectPath { worktree_id, path }: ProjectPath,
113        manifests: &mut dyn Iterator<Item = ManifestName>,
114        delegate: Arc<dyn ManifestDelegate>,
115        cx: &mut App,
116    ) -> BTreeMap<ManifestName, ProjectPath> {
117        debug_assert_eq!(delegate.worktree_id(), worktree_id);
118        let mut roots = BTreeMap::from_iter(
119            manifests.map(|manifest| (manifest, (None, LabelPresence::KnownAbsent))),
120        );
121        let worktree_roots = match self.root_points.entry(worktree_id) {
122            Entry::Occupied(occupied_entry) => occupied_entry.get().clone(),
123            Entry::Vacant(vacant_entry) => {
124                let Some(worktree) = self
125                    .worktree_store
126                    .read(cx)
127                    .worktree_for_id(worktree_id, cx)
128                else {
129                    return Default::default();
130                };
131                let roots = WorktreeRoots::new(self.worktree_store.clone(), worktree, cx);
132                vacant_entry.insert(roots).clone()
133            }
134        };
135
136        let key = TriePath::from(&*path);
137        worktree_roots.read_with(cx, |this, _| {
138            this.roots.walk(&key, &mut |path, labels| {
139                for (label, presence) in labels {
140                    if let Some((marked_path, current_presence)) = roots.get_mut(label) {
141                        if *current_presence > *presence {
142                            debug_assert!(false, "RootPathTrie precondition violation; while walking the tree label presence is only allowed to increase");
143                        }
144                        *marked_path = Some(ProjectPath {worktree_id, path: path.clone()});
145                        *current_presence = *presence;
146                    }
147
148                }
149                ControlFlow::Continue(())
150            });
151        });
152
153        for (manifest_name, (root_path, presence)) in &mut roots {
154            if *presence == LabelPresence::Present {
155                continue;
156            }
157
158            let depth = root_path
159                .as_ref()
160                .map(|root_path| {
161                    path.strip_prefix(&root_path.path)
162                        .unwrap()
163                        .components()
164                        .count()
165                })
166                .unwrap_or_else(|| path.components().count() + 1);
167
168            if depth > 0 {
169                let Some(provider) = ManifestProviders::global(cx).get(manifest_name.borrow())
170                else {
171                    log::warn!("Manifest provider `{}` not found", manifest_name.as_ref());
172                    continue;
173                };
174
175                let root = provider.search(ManifestQuery {
176                    path: path.clone(),
177                    depth,
178                    delegate: delegate.clone(),
179                });
180                match root {
181                    Some(known_root) => worktree_roots.update(cx, |this, _| {
182                        let root = TriePath::from(&*known_root);
183                        this.roots
184                            .insert(&root, manifest_name.clone(), LabelPresence::Present);
185                        *presence = LabelPresence::Present;
186                        *root_path = Some(ProjectPath {
187                            worktree_id,
188                            path: known_root,
189                        });
190                    }),
191                    None => worktree_roots.update(cx, |this, _| {
192                        this.roots
193                            .insert(&key, manifest_name.clone(), LabelPresence::KnownAbsent);
194                    }),
195                }
196            }
197        }
198
199        roots
200            .into_iter()
201            .filter_map(|(k, (path, presence))| {
202                let path = path?;
203                presence.eq(&LabelPresence::Present).then(|| (k, path))
204            })
205            .collect()
206    }
207    fn on_worktree_store_event(
208        &mut self,
209        _: Entity<WorktreeStore>,
210        evt: &WorktreeStoreEvent,
211        cx: &mut Context<Self>,
212    ) {
213        match evt {
214            WorktreeStoreEvent::WorktreeRemoved(_, worktree_id) => {
215                self.root_points.remove(&worktree_id);
216                cx.emit(ManifestTreeEvent::WorktreeRemoved(*worktree_id));
217            }
218            _ => {}
219        }
220    }
221}
222
223pub(crate) struct ManifestQueryDelegate {
224    worktree: Snapshot,
225}
226impl ManifestQueryDelegate {
227    pub fn new(worktree: Snapshot) -> Self {
228        Self { worktree }
229    }
230}
231
232impl ManifestDelegate for ManifestQueryDelegate {
233    fn exists(&self, path: &Path, is_dir: Option<bool>) -> bool {
234        self.worktree.entry_for_path(path).map_or(false, |entry| {
235            is_dir.map_or(true, |is_required_to_be_dir| {
236                is_required_to_be_dir == entry.is_dir()
237            })
238        })
239    }
240
241    fn worktree_id(&self) -> WorktreeId {
242        self.worktree.id()
243    }
244}