extension_store.rs

   1pub mod extension_builder;
   2mod extension_indexed_docs_provider;
   3mod extension_lsp_adapter;
   4mod extension_manifest;
   5mod extension_settings;
   6mod extension_slash_command;
   7mod wasm_host;
   8
   9#[cfg(test)]
  10mod extension_store_test;
  11
  12use crate::extension_indexed_docs_provider::ExtensionIndexedDocsProvider;
  13use crate::extension_manifest::SchemaVersion;
  14use crate::extension_slash_command::ExtensionSlashCommand;
  15use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
  16use anyhow::{anyhow, bail, Context as _, Result};
  17use assistant_slash_command::SlashCommandRegistry;
  18use async_compression::futures::bufread::GzipDecoder;
  19use async_tar::Archive;
  20use client::{telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
  21use collections::{btree_map, BTreeMap, HashSet};
  22use extension_builder::{CompileExtensionOptions, ExtensionBuilder};
  23use fs::{Fs, RemoveOptions};
  24use futures::{
  25    channel::{
  26        mpsc::{unbounded, UnboundedSender},
  27        oneshot,
  28    },
  29    io::BufReader,
  30    select_biased, AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
  31};
  32use gpui::{
  33    actions, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Task,
  34    WeakModel,
  35};
  36use http::{AsyncBody, HttpClient, HttpClientWithUrl};
  37use indexed_docs::{IndexedDocsRegistry, ProviderId};
  38use language::{
  39    LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES,
  40};
  41use node_runtime::NodeRuntime;
  42use project::ContextProviderWithTasks;
  43use release_channel::ReleaseChannel;
  44use semantic_version::SemanticVersion;
  45use serde::{Deserialize, Serialize};
  46use settings::Settings;
  47use std::ops::RangeInclusive;
  48use std::str::FromStr;
  49use std::{
  50    cmp::Ordering,
  51    path::{self, Path, PathBuf},
  52    sync::Arc,
  53    time::{Duration, Instant},
  54};
  55use theme::{ThemeRegistry, ThemeSettings};
  56use url::Url;
  57use util::{maybe, ResultExt};
  58use wasm_host::{
  59    wit::{is_supported_wasm_api_version, wasm_api_version_range},
  60    WasmExtension, WasmHost,
  61};
  62
  63pub use extension_manifest::{
  64    ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry, OldExtensionManifest,
  65};
  66pub use extension_settings::ExtensionSettings;
  67
  68const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
  69const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
  70
  71/// The current extension [`SchemaVersion`] supported by Zed.
  72const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1);
  73
  74/// Returns the [`SchemaVersion`] range that is compatible with this version of Zed.
  75pub fn schema_version_range() -> RangeInclusive<SchemaVersion> {
  76    SchemaVersion::ZERO..=CURRENT_SCHEMA_VERSION
  77}
  78
  79/// Returns whether the given extension version is compatible with this version of Zed.
  80pub fn is_version_compatible(
  81    release_channel: ReleaseChannel,
  82    extension_version: &ExtensionMetadata,
  83) -> bool {
  84    let schema_version = extension_version.manifest.schema_version.unwrap_or(0);
  85    if CURRENT_SCHEMA_VERSION.0 < schema_version {
  86        return false;
  87    }
  88
  89    if let Some(wasm_api_version) = extension_version
  90        .manifest
  91        .wasm_api_version
  92        .as_ref()
  93        .and_then(|wasm_api_version| SemanticVersion::from_str(wasm_api_version).ok())
  94    {
  95        if !is_supported_wasm_api_version(release_channel, wasm_api_version) {
  96            return false;
  97        }
  98    }
  99
 100    true
 101}
 102
 103pub struct ExtensionStore {
 104    builder: Arc<ExtensionBuilder>,
 105    extension_index: ExtensionIndex,
 106    fs: Arc<dyn Fs>,
 107    http_client: Arc<HttpClientWithUrl>,
 108    telemetry: Option<Arc<Telemetry>>,
 109    reload_tx: UnboundedSender<Option<Arc<str>>>,
 110    reload_complete_senders: Vec<oneshot::Sender<()>>,
 111    installed_dir: PathBuf,
 112    outstanding_operations: BTreeMap<Arc<str>, ExtensionOperation>,
 113    index_path: PathBuf,
 114    language_registry: Arc<LanguageRegistry>,
 115    theme_registry: Arc<ThemeRegistry>,
 116    slash_command_registry: Arc<SlashCommandRegistry>,
 117    indexed_docs_registry: Arc<IndexedDocsRegistry>,
 118    modified_extensions: HashSet<Arc<str>>,
 119    wasm_host: Arc<WasmHost>,
 120    wasm_extensions: Vec<(Arc<ExtensionManifest>, WasmExtension)>,
 121    tasks: Vec<Task<()>>,
 122}
 123
 124#[derive(Clone, Copy)]
 125pub enum ExtensionOperation {
 126    Upgrade,
 127    Install,
 128    Remove,
 129}
 130
 131#[derive(Clone)]
 132pub enum Event {
 133    ExtensionsUpdated,
 134    StartedReloading,
 135    ExtensionInstalled(Arc<str>),
 136    ExtensionFailedToLoad(Arc<str>),
 137}
 138
 139impl EventEmitter<Event> for ExtensionStore {}
 140
 141struct GlobalExtensionStore(Model<ExtensionStore>);
 142
 143impl Global for GlobalExtensionStore {}
 144
 145#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
 146pub struct ExtensionIndex {
 147    pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
 148    pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
 149    pub languages: BTreeMap<Arc<str>, ExtensionIndexLanguageEntry>,
 150}
 151
 152#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
 153pub struct ExtensionIndexEntry {
 154    pub manifest: Arc<ExtensionManifest>,
 155    pub dev: bool,
 156}
 157
 158#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 159pub struct ExtensionIndexThemeEntry {
 160    extension: Arc<str>,
 161    path: PathBuf,
 162}
 163
 164#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 165pub struct ExtensionIndexLanguageEntry {
 166    extension: Arc<str>,
 167    path: PathBuf,
 168    matcher: LanguageMatcher,
 169    grammar: Option<Arc<str>>,
 170}
 171
 172actions!(zed, [ReloadExtensions]);
 173
 174pub fn init(
 175    fs: Arc<dyn Fs>,
 176    client: Arc<Client>,
 177    node_runtime: Arc<dyn NodeRuntime>,
 178    language_registry: Arc<LanguageRegistry>,
 179    theme_registry: Arc<ThemeRegistry>,
 180    cx: &mut AppContext,
 181) {
 182    ExtensionSettings::register(cx);
 183
 184    let store = cx.new_model(move |cx| {
 185        ExtensionStore::new(
 186            paths::extensions_dir().clone(),
 187            None,
 188            fs,
 189            client.http_client().clone(),
 190            Some(client.telemetry().clone()),
 191            node_runtime,
 192            language_registry,
 193            theme_registry,
 194            SlashCommandRegistry::global(cx),
 195            IndexedDocsRegistry::global(cx),
 196            cx,
 197        )
 198    });
 199
 200    cx.on_action(|_: &ReloadExtensions, cx| {
 201        let store = cx.global::<GlobalExtensionStore>().0.clone();
 202        store.update(cx, |store, cx| drop(store.reload(None, cx)));
 203    });
 204
 205    cx.set_global(GlobalExtensionStore(store));
 206}
 207
 208impl ExtensionStore {
 209    pub fn try_global(cx: &AppContext) -> Option<Model<Self>> {
 210        cx.try_global::<GlobalExtensionStore>()
 211            .map(|store| store.0.clone())
 212    }
 213
 214    pub fn global(cx: &AppContext) -> Model<Self> {
 215        cx.global::<GlobalExtensionStore>().0.clone()
 216    }
 217
 218    #[allow(clippy::too_many_arguments)]
 219    pub fn new(
 220        extensions_dir: PathBuf,
 221        build_dir: Option<PathBuf>,
 222        fs: Arc<dyn Fs>,
 223        http_client: Arc<HttpClientWithUrl>,
 224        telemetry: Option<Arc<Telemetry>>,
 225        node_runtime: Arc<dyn NodeRuntime>,
 226        language_registry: Arc<LanguageRegistry>,
 227        theme_registry: Arc<ThemeRegistry>,
 228        slash_command_registry: Arc<SlashCommandRegistry>,
 229        indexed_docs_registry: Arc<IndexedDocsRegistry>,
 230        cx: &mut ModelContext<Self>,
 231    ) -> Self {
 232        let work_dir = extensions_dir.join("work");
 233        let build_dir = build_dir.unwrap_or_else(|| extensions_dir.join("build"));
 234        let installed_dir = extensions_dir.join("installed");
 235        let index_path = extensions_dir.join("index.json");
 236
 237        let (reload_tx, mut reload_rx) = unbounded();
 238        let mut this = Self {
 239            extension_index: Default::default(),
 240            installed_dir,
 241            index_path,
 242            builder: Arc::new(ExtensionBuilder::new(build_dir)),
 243            outstanding_operations: Default::default(),
 244            modified_extensions: Default::default(),
 245            reload_complete_senders: Vec::new(),
 246            wasm_host: WasmHost::new(
 247                fs.clone(),
 248                http_client.clone(),
 249                node_runtime,
 250                language_registry.clone(),
 251                work_dir,
 252                cx,
 253            ),
 254            wasm_extensions: Vec::new(),
 255            fs,
 256            http_client,
 257            telemetry,
 258            language_registry,
 259            theme_registry,
 260            slash_command_registry,
 261            indexed_docs_registry,
 262            reload_tx,
 263            tasks: Vec::new(),
 264        };
 265
 266        // The extensions store maintains an index file, which contains a complete
 267        // list of the installed extensions and the resources that they provide.
 268        // This index is loaded synchronously on startup.
 269        let (index_content, index_metadata, extensions_metadata) =
 270            cx.background_executor().block(async {
 271                futures::join!(
 272                    this.fs.load(&this.index_path),
 273                    this.fs.metadata(&this.index_path),
 274                    this.fs.metadata(&this.installed_dir),
 275                )
 276            });
 277
 278        // Normally, there is no need to rebuild the index. But if the index file
 279        // is invalid or is out-of-date according to the filesystem mtimes, then
 280        // it must be asynchronously rebuilt.
 281        let mut extension_index = ExtensionIndex::default();
 282        let mut extension_index_needs_rebuild = true;
 283        if let Some(index_content) = index_content.ok() {
 284            if let Some(index) = serde_json::from_str(&index_content).log_err() {
 285                extension_index = index;
 286                if let (Ok(Some(index_metadata)), Ok(Some(extensions_metadata))) =
 287                    (index_metadata, extensions_metadata)
 288                {
 289                    if index_metadata.mtime > extensions_metadata.mtime {
 290                        extension_index_needs_rebuild = false;
 291                    }
 292                }
 293            }
 294        }
 295
 296        // Immediately load all of the extensions in the initial manifest. If the
 297        // index needs to be rebuild, then enqueue
 298        let load_initial_extensions = this.extensions_updated(extension_index, cx);
 299        let mut reload_future = None;
 300        if extension_index_needs_rebuild {
 301            reload_future = Some(this.reload(None, cx));
 302        }
 303
 304        cx.spawn(|this, mut cx| async move {
 305            if let Some(future) = reload_future {
 306                future.await;
 307            }
 308            this.update(&mut cx, |this, cx| this.auto_install_extensions(cx))
 309                .ok();
 310            this.update(&mut cx, |this, cx| this.check_for_updates(cx))
 311                .ok();
 312        })
 313        .detach();
 314
 315        // Perform all extension loading in a single task to ensure that we
 316        // never attempt to simultaneously load/unload extensions from multiple
 317        // parallel tasks.
 318        this.tasks.push(cx.spawn(|this, mut cx| {
 319            async move {
 320                load_initial_extensions.await;
 321
 322                let mut debounce_timer = cx
 323                    .background_executor()
 324                    .spawn(futures::future::pending())
 325                    .fuse();
 326                loop {
 327                    select_biased! {
 328                        _ = debounce_timer => {
 329                            let index = this
 330                                .update(&mut cx, |this, cx| this.rebuild_extension_index(cx))?
 331                                .await;
 332                            this.update(&mut cx, |this, cx| this.extensions_updated(index, cx))?
 333                                .await;
 334                        }
 335                        extension_id = reload_rx.next() => {
 336                            let Some(extension_id) = extension_id else { break; };
 337                            this.update(&mut cx, |this, _| {
 338                                this.modified_extensions.extend(extension_id);
 339                            })?;
 340                            debounce_timer = cx
 341                                .background_executor()
 342                                .timer(RELOAD_DEBOUNCE_DURATION)
 343                                .fuse();
 344                        }
 345                    }
 346                }
 347
 348                anyhow::Ok(())
 349            }
 350            .map(drop)
 351        }));
 352
 353        // Watch the installed extensions directory for changes. Whenever changes are
 354        // detected, rebuild the extension index, and load/unload any extensions that
 355        // have been added, removed, or modified.
 356        this.tasks.push(cx.background_executor().spawn({
 357            let fs = this.fs.clone();
 358            let reload_tx = this.reload_tx.clone();
 359            let installed_dir = this.installed_dir.clone();
 360            async move {
 361                let (mut paths, _) = fs.watch(&installed_dir, FS_WATCH_LATENCY).await;
 362                while let Some(paths) = paths.next().await {
 363                    for path in paths {
 364                        let Ok(event_path) = path.strip_prefix(&installed_dir) else {
 365                            continue;
 366                        };
 367
 368                        if let Some(path::Component::Normal(extension_dir_name)) =
 369                            event_path.components().next()
 370                        {
 371                            if let Some(extension_id) = extension_dir_name.to_str() {
 372                                reload_tx.unbounded_send(Some(extension_id.into())).ok();
 373                            }
 374                        }
 375                    }
 376                }
 377            }
 378        }));
 379
 380        this
 381    }
 382
 383    fn reload(
 384        &mut self,
 385        modified_extension: Option<Arc<str>>,
 386        cx: &mut ModelContext<Self>,
 387    ) -> impl Future<Output = ()> {
 388        let (tx, rx) = oneshot::channel();
 389        self.reload_complete_senders.push(tx);
 390        self.reload_tx
 391            .unbounded_send(modified_extension)
 392            .expect("reload task exited");
 393        cx.emit(Event::StartedReloading);
 394
 395        async move {
 396            rx.await.ok();
 397        }
 398    }
 399
 400    fn extensions_dir(&self) -> PathBuf {
 401        self.installed_dir.clone()
 402    }
 403
 404    pub fn outstanding_operations(&self) -> &BTreeMap<Arc<str>, ExtensionOperation> {
 405        &self.outstanding_operations
 406    }
 407
 408    pub fn installed_extensions(&self) -> &BTreeMap<Arc<str>, ExtensionIndexEntry> {
 409        &self.extension_index.extensions
 410    }
 411
 412    pub fn dev_extensions(&self) -> impl Iterator<Item = &Arc<ExtensionManifest>> {
 413        self.extension_index
 414            .extensions
 415            .values()
 416            .filter_map(|extension| extension.dev.then_some(&extension.manifest))
 417    }
 418
 419    /// Returns the names of themes provided by extensions.
 420    pub fn extension_themes<'a>(
 421        &'a self,
 422        extension_id: &'a str,
 423    ) -> impl Iterator<Item = &'a Arc<str>> {
 424        self.extension_index
 425            .themes
 426            .iter()
 427            .filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name))
 428    }
 429
 430    pub fn fetch_extensions(
 431        &self,
 432        search: Option<&str>,
 433        cx: &mut ModelContext<Self>,
 434    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 435        let version = CURRENT_SCHEMA_VERSION.to_string();
 436        let mut query = vec![("max_schema_version", version.as_str())];
 437        if let Some(search) = search {
 438            query.push(("filter", search));
 439        }
 440
 441        self.fetch_extensions_from_api("/extensions", &query, cx)
 442    }
 443
 444    pub fn fetch_extensions_with_update_available(
 445        &mut self,
 446        cx: &mut ModelContext<Self>,
 447    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 448        let schema_versions = schema_version_range();
 449        let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
 450        let extension_settings = ExtensionSettings::get_global(cx);
 451        let extension_ids = self
 452            .extension_index
 453            .extensions
 454            .keys()
 455            .map(|id| id.as_ref())
 456            .filter(|id| extension_settings.should_auto_update(id))
 457            .collect::<Vec<_>>()
 458            .join(",");
 459        let task = self.fetch_extensions_from_api(
 460            "/extensions/updates",
 461            &[
 462                ("min_schema_version", &schema_versions.start().to_string()),
 463                ("max_schema_version", &schema_versions.end().to_string()),
 464                (
 465                    "min_wasm_api_version",
 466                    &wasm_api_versions.start().to_string(),
 467                ),
 468                ("max_wasm_api_version", &wasm_api_versions.end().to_string()),
 469                ("ids", &extension_ids),
 470            ],
 471            cx,
 472        );
 473        cx.spawn(move |this, mut cx| async move {
 474            let extensions = task.await?;
 475            this.update(&mut cx, |this, _cx| {
 476                extensions
 477                    .into_iter()
 478                    .filter(|extension| {
 479                        this.extension_index.extensions.get(&extension.id).map_or(
 480                            true,
 481                            |installed_extension| {
 482                                installed_extension.manifest.version != extension.manifest.version
 483                            },
 484                        )
 485                    })
 486                    .collect()
 487            })
 488        })
 489    }
 490
 491    pub fn fetch_extension_versions(
 492        &self,
 493        extension_id: &str,
 494        cx: &mut ModelContext<Self>,
 495    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 496        self.fetch_extensions_from_api(&format!("/extensions/{extension_id}"), &[], cx)
 497    }
 498
 499    /// Installs any extensions that should be included with Zed by default.
 500    ///
 501    /// This can be used to make certain functionality provided by extensions
 502    /// available out-of-the-box.
 503    pub fn auto_install_extensions(&mut self, cx: &mut ModelContext<Self>) {
 504        let extension_settings = ExtensionSettings::get_global(cx);
 505
 506        let extensions_to_install = extension_settings
 507            .auto_install_extensions
 508            .keys()
 509            .filter(|extension_id| extension_settings.should_auto_install(extension_id))
 510            .filter(|extension_id| {
 511                let is_already_installed = self
 512                    .extension_index
 513                    .extensions
 514                    .contains_key(extension_id.as_ref());
 515                !is_already_installed
 516            })
 517            .cloned()
 518            .collect::<Vec<_>>();
 519
 520        cx.spawn(move |this, mut cx| async move {
 521            for extension_id in extensions_to_install {
 522                this.update(&mut cx, |this, cx| {
 523                    this.install_latest_extension(extension_id.clone(), cx);
 524                })
 525                .ok();
 526            }
 527        })
 528        .detach();
 529    }
 530
 531    pub fn check_for_updates(&mut self, cx: &mut ModelContext<Self>) {
 532        let task = self.fetch_extensions_with_update_available(cx);
 533        cx.spawn(move |this, mut cx| async move {
 534            Self::upgrade_extensions(this, task.await?, &mut cx).await
 535        })
 536        .detach();
 537    }
 538
 539    async fn upgrade_extensions(
 540        this: WeakModel<Self>,
 541        extensions: Vec<ExtensionMetadata>,
 542        cx: &mut AsyncAppContext,
 543    ) -> Result<()> {
 544        for extension in extensions {
 545            let task = this.update(cx, |this, cx| {
 546                if let Some(installed_extension) =
 547                    this.extension_index.extensions.get(&extension.id)
 548                {
 549                    let installed_version =
 550                        SemanticVersion::from_str(&installed_extension.manifest.version).ok()?;
 551                    let latest_version =
 552                        SemanticVersion::from_str(&extension.manifest.version).ok()?;
 553
 554                    if installed_version >= latest_version {
 555                        return None;
 556                    }
 557                }
 558
 559                Some(this.upgrade_extension(extension.id, extension.manifest.version, cx))
 560            })?;
 561
 562            if let Some(task) = task {
 563                task.await.log_err();
 564            }
 565        }
 566        anyhow::Ok(())
 567    }
 568
 569    fn fetch_extensions_from_api(
 570        &self,
 571        path: &str,
 572        query: &[(&str, &str)],
 573        cx: &mut ModelContext<'_, ExtensionStore>,
 574    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 575        let url = self.http_client.build_zed_api_url(path, &query);
 576        let http_client = self.http_client.clone();
 577        cx.spawn(move |_, _| async move {
 578            let mut response = http_client
 579                .get(&url?.as_ref(), AsyncBody::empty(), true)
 580                .await?;
 581
 582            let mut body = Vec::new();
 583            response
 584                .body_mut()
 585                .read_to_end(&mut body)
 586                .await
 587                .context("error reading extensions")?;
 588
 589            if response.status().is_client_error() {
 590                let text = String::from_utf8_lossy(body.as_slice());
 591                bail!(
 592                    "status error {}, response: {text:?}",
 593                    response.status().as_u16()
 594                );
 595            }
 596
 597            let response: GetExtensionsResponse = serde_json::from_slice(&body)?;
 598            Ok(response.data)
 599        })
 600    }
 601
 602    pub fn install_extension(
 603        &mut self,
 604        extension_id: Arc<str>,
 605        version: Arc<str>,
 606        cx: &mut ModelContext<Self>,
 607    ) {
 608        self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Install, cx)
 609            .detach_and_log_err(cx);
 610    }
 611
 612    fn install_or_upgrade_extension_at_endpoint(
 613        &mut self,
 614        extension_id: Arc<str>,
 615        url: Url,
 616        operation: ExtensionOperation,
 617        cx: &mut ModelContext<Self>,
 618    ) -> Task<Result<()>> {
 619        let extension_dir = self.installed_dir.join(extension_id.as_ref());
 620        let http_client = self.http_client.clone();
 621        let fs = self.fs.clone();
 622
 623        match self.outstanding_operations.entry(extension_id.clone()) {
 624            btree_map::Entry::Occupied(_) => return Task::ready(Ok(())),
 625            btree_map::Entry::Vacant(e) => e.insert(operation),
 626        };
 627        cx.notify();
 628
 629        cx.spawn(move |this, mut cx| async move {
 630            let _finish = util::defer({
 631                let this = this.clone();
 632                let mut cx = cx.clone();
 633                let extension_id = extension_id.clone();
 634                move || {
 635                    this.update(&mut cx, |this, cx| {
 636                        this.outstanding_operations.remove(extension_id.as_ref());
 637                        cx.notify();
 638                    })
 639                    .ok();
 640                }
 641            });
 642
 643            let mut response = http_client
 644                .get(&url.as_ref(), Default::default(), true)
 645                .await
 646                .map_err(|err| anyhow!("error downloading extension: {}", err))?;
 647
 648            fs.remove_dir(
 649                &extension_dir,
 650                RemoveOptions {
 651                    recursive: true,
 652                    ignore_if_not_exists: true,
 653                },
 654            )
 655            .await?;
 656
 657            let content_length = response
 658                .headers()
 659                .get(isahc::http::header::CONTENT_LENGTH)
 660                .and_then(|value| value.to_str().ok()?.parse::<usize>().ok());
 661
 662            let mut body = BufReader::new(response.body_mut());
 663            let mut tar_gz_bytes = Vec::new();
 664            body.read_to_end(&mut tar_gz_bytes).await?;
 665
 666            if let Some(content_length) = content_length {
 667                let actual_len = tar_gz_bytes.len();
 668                if content_length != actual_len {
 669                    bail!("downloaded extension size {actual_len} does not match content length {content_length}");
 670                }
 671            }
 672            let decompressed_bytes = GzipDecoder::new(BufReader::new(tar_gz_bytes.as_slice()));
 673            let archive = Archive::new(decompressed_bytes);
 674            archive.unpack(extension_dir).await?;
 675            this.update(&mut cx, |this, cx| {
 676                this.reload(Some(extension_id.clone()), cx)
 677            })?
 678            .await;
 679
 680            match operation {
 681                ExtensionOperation::Install => {
 682                    this.update(&mut cx, |_, cx| {
 683                        cx.emit(Event::ExtensionInstalled(extension_id));
 684                    })
 685                    .ok();
 686                }
 687                _ => {}
 688            }
 689
 690            anyhow::Ok(())
 691        })
 692    }
 693
 694    pub fn install_latest_extension(
 695        &mut self,
 696        extension_id: Arc<str>,
 697        cx: &mut ModelContext<Self>,
 698    ) {
 699        log::info!("installing extension {extension_id} latest version");
 700
 701        let schema_versions = schema_version_range();
 702        let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
 703
 704        let Some(url) = self
 705            .http_client
 706            .build_zed_api_url(
 707                &format!("/extensions/{extension_id}/download"),
 708                &[
 709                    ("min_schema_version", &schema_versions.start().to_string()),
 710                    ("max_schema_version", &schema_versions.end().to_string()),
 711                    (
 712                        "min_wasm_api_version",
 713                        &wasm_api_versions.start().to_string(),
 714                    ),
 715                    ("max_wasm_api_version", &wasm_api_versions.end().to_string()),
 716                ],
 717            )
 718            .log_err()
 719        else {
 720            return;
 721        };
 722
 723        self.install_or_upgrade_extension_at_endpoint(
 724            extension_id,
 725            url,
 726            ExtensionOperation::Install,
 727            cx,
 728        )
 729        .detach_and_log_err(cx);
 730    }
 731
 732    pub fn upgrade_extension(
 733        &mut self,
 734        extension_id: Arc<str>,
 735        version: Arc<str>,
 736        cx: &mut ModelContext<Self>,
 737    ) -> Task<Result<()>> {
 738        self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Upgrade, cx)
 739    }
 740
 741    fn install_or_upgrade_extension(
 742        &mut self,
 743        extension_id: Arc<str>,
 744        version: Arc<str>,
 745        operation: ExtensionOperation,
 746        cx: &mut ModelContext<Self>,
 747    ) -> Task<Result<()>> {
 748        log::info!("installing extension {extension_id} {version}");
 749        let Some(url) = self
 750            .http_client
 751            .build_zed_api_url(
 752                &format!("/extensions/{extension_id}/{version}/download"),
 753                &[],
 754            )
 755            .log_err()
 756        else {
 757            return Task::ready(Ok(()));
 758        };
 759
 760        self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx)
 761    }
 762
 763    pub fn uninstall_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
 764        let extension_dir = self.installed_dir.join(extension_id.as_ref());
 765        let fs = self.fs.clone();
 766
 767        match self.outstanding_operations.entry(extension_id.clone()) {
 768            btree_map::Entry::Occupied(_) => return,
 769            btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
 770        };
 771
 772        cx.spawn(move |this, mut cx| async move {
 773            let _finish = util::defer({
 774                let this = this.clone();
 775                let mut cx = cx.clone();
 776                let extension_id = extension_id.clone();
 777                move || {
 778                    this.update(&mut cx, |this, cx| {
 779                        this.outstanding_operations.remove(extension_id.as_ref());
 780                        cx.notify();
 781                    })
 782                    .ok();
 783                }
 784            });
 785
 786            fs.remove_dir(
 787                &extension_dir,
 788                RemoveOptions {
 789                    recursive: true,
 790                    ignore_if_not_exists: true,
 791                },
 792            )
 793            .await?;
 794
 795            this.update(&mut cx, |this, cx| this.reload(None, cx))?
 796                .await;
 797            anyhow::Ok(())
 798        })
 799        .detach_and_log_err(cx)
 800    }
 801
 802    pub fn install_dev_extension(
 803        &mut self,
 804        extension_source_path: PathBuf,
 805        cx: &mut ModelContext<Self>,
 806    ) -> Task<Result<()>> {
 807        let extensions_dir = self.extensions_dir();
 808        let fs = self.fs.clone();
 809        let builder = self.builder.clone();
 810
 811        cx.spawn(move |this, mut cx| async move {
 812            let mut extension_manifest =
 813                ExtensionManifest::load(fs.clone(), &extension_source_path).await?;
 814            let extension_id = extension_manifest.id.clone();
 815
 816            if !this.update(&mut cx, |this, cx| {
 817                match this.outstanding_operations.entry(extension_id.clone()) {
 818                    btree_map::Entry::Occupied(_) => return false,
 819                    btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
 820                };
 821                cx.notify();
 822                true
 823            })? {
 824                return Ok(());
 825            }
 826
 827            let _finish = util::defer({
 828                let this = this.clone();
 829                let mut cx = cx.clone();
 830                let extension_id = extension_id.clone();
 831                move || {
 832                    this.update(&mut cx, |this, cx| {
 833                        this.outstanding_operations.remove(extension_id.as_ref());
 834                        cx.notify();
 835                    })
 836                    .ok();
 837                }
 838            });
 839
 840            cx.background_executor()
 841                .spawn({
 842                    let extension_source_path = extension_source_path.clone();
 843                    async move {
 844                        builder
 845                            .compile_extension(
 846                                &extension_source_path,
 847                                &mut extension_manifest,
 848                                CompileExtensionOptions { release: false },
 849                            )
 850                            .await
 851                    }
 852                })
 853                .await?;
 854
 855            let output_path = &extensions_dir.join(extension_id.as_ref());
 856            if let Some(metadata) = fs.metadata(&output_path).await? {
 857                if metadata.is_symlink {
 858                    fs.remove_file(
 859                        &output_path,
 860                        RemoveOptions {
 861                            recursive: false,
 862                            ignore_if_not_exists: true,
 863                        },
 864                    )
 865                    .await?;
 866                } else {
 867                    bail!("extension {extension_id} is already installed");
 868                }
 869            }
 870
 871            fs.create_symlink(output_path, extension_source_path)
 872                .await?;
 873
 874            this.update(&mut cx, |this, cx| this.reload(None, cx))?
 875                .await;
 876            Ok(())
 877        })
 878    }
 879
 880    pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
 881        let path = self.installed_dir.join(extension_id.as_ref());
 882        let builder = self.builder.clone();
 883        let fs = self.fs.clone();
 884
 885        match self.outstanding_operations.entry(extension_id.clone()) {
 886            btree_map::Entry::Occupied(_) => return,
 887            btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Upgrade),
 888        };
 889
 890        cx.notify();
 891        let compile = cx.background_executor().spawn(async move {
 892            let mut manifest = ExtensionManifest::load(fs, &path).await?;
 893            builder
 894                .compile_extension(
 895                    &path,
 896                    &mut manifest,
 897                    CompileExtensionOptions { release: true },
 898                )
 899                .await
 900        });
 901
 902        cx.spawn(|this, mut cx| async move {
 903            let result = compile.await;
 904
 905            this.update(&mut cx, |this, cx| {
 906                this.outstanding_operations.remove(&extension_id);
 907                cx.notify();
 908            })?;
 909
 910            if result.is_ok() {
 911                this.update(&mut cx, |this, cx| this.reload(Some(extension_id), cx))?
 912                    .await;
 913            }
 914
 915            result
 916        })
 917        .detach_and_log_err(cx)
 918    }
 919
 920    /// Updates the set of installed extensions.
 921    ///
 922    /// First, this unloads any themes, languages, or grammars that are
 923    /// no longer in the manifest, or whose files have changed on disk.
 924    /// Then it loads any themes, languages, or grammars that are newly
 925    /// added to the manifest, or whose files have changed on disk.
 926    fn extensions_updated(
 927        &mut self,
 928        new_index: ExtensionIndex,
 929        cx: &mut ModelContext<Self>,
 930    ) -> Task<()> {
 931        let old_index = &self.extension_index;
 932
 933        // Determine which extensions need to be loaded and unloaded, based
 934        // on the changes to the manifest and the extensions that we know have been
 935        // modified.
 936        let mut extensions_to_unload = Vec::default();
 937        let mut extensions_to_load = Vec::default();
 938        {
 939            let mut old_keys = old_index.extensions.iter().peekable();
 940            let mut new_keys = new_index.extensions.iter().peekable();
 941            loop {
 942                match (old_keys.peek(), new_keys.peek()) {
 943                    (None, None) => break,
 944                    (None, Some(_)) => {
 945                        extensions_to_load.push(new_keys.next().unwrap().0.clone());
 946                    }
 947                    (Some(_), None) => {
 948                        extensions_to_unload.push(old_keys.next().unwrap().0.clone());
 949                    }
 950                    (Some((old_key, _)), Some((new_key, _))) => match old_key.cmp(&new_key) {
 951                        Ordering::Equal => {
 952                            let (old_key, old_value) = old_keys.next().unwrap();
 953                            let (new_key, new_value) = new_keys.next().unwrap();
 954                            if old_value != new_value || self.modified_extensions.contains(old_key)
 955                            {
 956                                extensions_to_unload.push(old_key.clone());
 957                                extensions_to_load.push(new_key.clone());
 958                            }
 959                        }
 960                        Ordering::Less => {
 961                            extensions_to_unload.push(old_keys.next().unwrap().0.clone());
 962                        }
 963                        Ordering::Greater => {
 964                            extensions_to_load.push(new_keys.next().unwrap().0.clone());
 965                        }
 966                    },
 967                }
 968            }
 969            self.modified_extensions.clear();
 970        }
 971
 972        if extensions_to_load.is_empty() && extensions_to_unload.is_empty() {
 973            return Task::ready(());
 974        }
 975
 976        let reload_count = extensions_to_unload
 977            .iter()
 978            .filter(|id| extensions_to_load.contains(id))
 979            .count();
 980
 981        log::info!(
 982            "extensions updated. loading {}, reloading {}, unloading {}",
 983            extensions_to_load.len() - reload_count,
 984            reload_count,
 985            extensions_to_unload.len() - reload_count
 986        );
 987
 988        if let Some(telemetry) = &self.telemetry {
 989            for extension_id in &extensions_to_load {
 990                if let Some(extension) = new_index.extensions.get(extension_id) {
 991                    telemetry.report_extension_event(
 992                        extension_id.clone(),
 993                        extension.manifest.version.clone(),
 994                    );
 995                }
 996            }
 997        }
 998
 999        let themes_to_remove = old_index
1000            .themes
1001            .iter()
1002            .filter_map(|(name, entry)| {
1003                if extensions_to_unload.contains(&entry.extension) {
1004                    Some(name.clone().into())
1005                } else {
1006                    None
1007                }
1008            })
1009            .collect::<Vec<_>>();
1010        let languages_to_remove = old_index
1011            .languages
1012            .iter()
1013            .filter_map(|(name, entry)| {
1014                if extensions_to_unload.contains(&entry.extension) {
1015                    Some(name.clone())
1016                } else {
1017                    None
1018                }
1019            })
1020            .collect::<Vec<_>>();
1021        let mut grammars_to_remove = Vec::new();
1022        for extension_id in &extensions_to_unload {
1023            let Some(extension) = old_index.extensions.get(extension_id) else {
1024                continue;
1025            };
1026            grammars_to_remove.extend(extension.manifest.grammars.keys().cloned());
1027            for (language_server_name, config) in extension.manifest.language_servers.iter() {
1028                for language in config.languages() {
1029                    self.language_registry
1030                        .remove_lsp_adapter(&language, language_server_name);
1031                }
1032            }
1033        }
1034
1035        self.wasm_extensions
1036            .retain(|(extension, _)| !extensions_to_unload.contains(&extension.id));
1037        self.theme_registry.remove_user_themes(&themes_to_remove);
1038        self.language_registry
1039            .remove_languages(&languages_to_remove, &grammars_to_remove);
1040
1041        let languages_to_add = new_index
1042            .languages
1043            .iter()
1044            .filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
1045            .collect::<Vec<_>>();
1046        let mut grammars_to_add = Vec::new();
1047        let mut themes_to_add = Vec::new();
1048        for extension_id in &extensions_to_load {
1049            let Some(extension) = new_index.extensions.get(extension_id) else {
1050                continue;
1051            };
1052
1053            grammars_to_add.extend(extension.manifest.grammars.keys().map(|grammar_name| {
1054                let mut grammar_path = self.installed_dir.clone();
1055                grammar_path.extend([extension_id.as_ref(), "grammars"]);
1056                grammar_path.push(grammar_name.as_ref());
1057                grammar_path.set_extension("wasm");
1058                (grammar_name.clone(), grammar_path)
1059            }));
1060            themes_to_add.extend(extension.manifest.themes.iter().map(|theme_path| {
1061                let mut path = self.installed_dir.clone();
1062                path.extend([Path::new(extension_id.as_ref()), theme_path.as_path()]);
1063                path
1064            }));
1065        }
1066
1067        self.language_registry
1068            .register_wasm_grammars(grammars_to_add);
1069
1070        for (language_name, language) in languages_to_add {
1071            let mut language_path = self.installed_dir.clone();
1072            language_path.extend([
1073                Path::new(language.extension.as_ref()),
1074                language.path.as_path(),
1075            ]);
1076            self.language_registry.register_language(
1077                language_name.clone(),
1078                language.grammar.clone(),
1079                language.matcher.clone(),
1080                move || {
1081                    let config = std::fs::read_to_string(language_path.join("config.toml"))?;
1082                    let config: LanguageConfig = ::toml::from_str(&config)?;
1083                    let queries = load_plugin_queries(&language_path);
1084                    let tasks = std::fs::read_to_string(language_path.join("tasks.json"))
1085                        .ok()
1086                        .and_then(|contents| {
1087                            let definitions = serde_json_lenient::from_str(&contents).log_err()?;
1088                            Some(Arc::new(ContextProviderWithTasks::new(definitions)) as Arc<_>)
1089                        });
1090
1091                    Ok((config, queries, tasks))
1092                },
1093            );
1094        }
1095
1096        let fs = self.fs.clone();
1097        let wasm_host = self.wasm_host.clone();
1098        let root_dir = self.installed_dir.clone();
1099        let theme_registry = self.theme_registry.clone();
1100        let extension_entries = extensions_to_load
1101            .iter()
1102            .filter_map(|name| new_index.extensions.get(name).cloned())
1103            .collect::<Vec<_>>();
1104
1105        self.extension_index = new_index;
1106        cx.notify();
1107        cx.emit(Event::ExtensionsUpdated);
1108
1109        cx.spawn(|this, mut cx| async move {
1110            cx.background_executor()
1111                .spawn({
1112                    let fs = fs.clone();
1113                    async move {
1114                        for theme_path in &themes_to_add {
1115                            theme_registry
1116                                .load_user_theme(&theme_path, fs.clone())
1117                                .await
1118                                .log_err();
1119                        }
1120                    }
1121                })
1122                .await;
1123
1124            let mut wasm_extensions = Vec::new();
1125            for extension in extension_entries {
1126                if extension.manifest.lib.kind.is_none() {
1127                    continue;
1128                };
1129
1130                let wasm_extension = maybe!(async {
1131                    let mut path = root_dir.clone();
1132                    path.extend([extension.manifest.clone().id.as_ref(), "extension.wasm"]);
1133                    let mut wasm_file = fs
1134                        .open_sync(&path)
1135                        .await
1136                        .context("failed to open wasm file")?;
1137
1138                    let mut wasm_bytes = Vec::new();
1139                    wasm_file
1140                        .read_to_end(&mut wasm_bytes)
1141                        .context("failed to read wasm")?;
1142
1143                    wasm_host
1144                        .load_extension(
1145                            wasm_bytes,
1146                            extension.manifest.clone().clone(),
1147                            cx.background_executor().clone(),
1148                        )
1149                        .await
1150                        .with_context(|| {
1151                            format!("failed to load wasm extension {}", extension.manifest.id)
1152                        })
1153                })
1154                .await;
1155
1156                if let Some(wasm_extension) = wasm_extension.log_err() {
1157                    wasm_extensions.push((extension.manifest.clone(), wasm_extension));
1158                } else {
1159                    this.update(&mut cx, |_, cx| {
1160                        cx.emit(Event::ExtensionFailedToLoad(extension.manifest.id.clone()))
1161                    })
1162                    .ok();
1163                }
1164            }
1165
1166            this.update(&mut cx, |this, cx| {
1167                this.reload_complete_senders.clear();
1168
1169                for (manifest, wasm_extension) in &wasm_extensions {
1170                    for (language_server_id, language_server_config) in &manifest.language_servers {
1171                        for language in language_server_config.languages() {
1172                            this.language_registry.register_lsp_adapter(
1173                                language.clone(),
1174                                Arc::new(ExtensionLspAdapter {
1175                                    extension: wasm_extension.clone(),
1176                                    host: this.wasm_host.clone(),
1177                                    language_server_id: language_server_id.clone(),
1178                                    config: wit::LanguageServerConfig {
1179                                        name: language_server_id.0.to_string(),
1180                                        language_name: language.to_string(),
1181                                    },
1182                                }),
1183                            );
1184                        }
1185                    }
1186
1187                    for (slash_command_name, slash_command) in &manifest.slash_commands {
1188                        this.slash_command_registry.register_command(
1189                            ExtensionSlashCommand {
1190                                command: crate::wit::SlashCommand {
1191                                    name: slash_command_name.to_string(),
1192                                    description: slash_command.description.to_string(),
1193                                    tooltip_text: slash_command.tooltip_text.to_string(),
1194                                    requires_argument: slash_command.requires_argument,
1195                                },
1196                                extension: wasm_extension.clone(),
1197                                host: this.wasm_host.clone(),
1198                            },
1199                            false,
1200                        );
1201                    }
1202
1203                    for (provider_id, _provider) in &manifest.indexed_docs_providers {
1204                        this.indexed_docs_registry.register_provider(Box::new(
1205                            ExtensionIndexedDocsProvider {
1206                                extension: wasm_extension.clone(),
1207                                host: this.wasm_host.clone(),
1208                                id: ProviderId(provider_id.clone()),
1209                            },
1210                        ));
1211                    }
1212                }
1213
1214                this.wasm_extensions.extend(wasm_extensions);
1215                ThemeSettings::reload_current_theme(cx)
1216            })
1217            .ok();
1218        })
1219    }
1220
1221    fn rebuild_extension_index(&self, cx: &mut ModelContext<Self>) -> Task<ExtensionIndex> {
1222        let fs = self.fs.clone();
1223        let work_dir = self.wasm_host.work_dir.clone();
1224        let extensions_dir = self.installed_dir.clone();
1225        let index_path = self.index_path.clone();
1226        cx.background_executor().spawn(async move {
1227            let start_time = Instant::now();
1228            let mut index = ExtensionIndex::default();
1229
1230            fs.create_dir(&work_dir).await.log_err();
1231            fs.create_dir(&extensions_dir).await.log_err();
1232
1233            let extension_paths = fs.read_dir(&extensions_dir).await;
1234            if let Ok(mut extension_paths) = extension_paths {
1235                while let Some(extension_dir) = extension_paths.next().await {
1236                    let Ok(extension_dir) = extension_dir else {
1237                        continue;
1238                    };
1239
1240                    if extension_dir
1241                        .file_name()
1242                        .map_or(false, |file_name| file_name == ".DS_Store")
1243                    {
1244                        continue;
1245                    }
1246
1247                    Self::add_extension_to_index(fs.clone(), extension_dir, &mut index)
1248                        .await
1249                        .log_err();
1250                }
1251            }
1252
1253            if let Ok(index_json) = serde_json::to_string_pretty(&index) {
1254                fs.save(&index_path, &index_json.as_str().into(), Default::default())
1255                    .await
1256                    .context("failed to save extension index")
1257                    .log_err();
1258            }
1259
1260            log::info!("rebuilt extension index in {:?}", start_time.elapsed());
1261            index
1262        })
1263    }
1264
1265    async fn add_extension_to_index(
1266        fs: Arc<dyn Fs>,
1267        extension_dir: PathBuf,
1268        index: &mut ExtensionIndex,
1269    ) -> Result<()> {
1270        let mut extension_manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
1271        let extension_id = extension_manifest.id.clone();
1272
1273        // TODO: distinguish dev extensions more explicitly, by the absence
1274        // of a checksum file that we'll create when downloading normal extensions.
1275        let is_dev = fs
1276            .metadata(&extension_dir)
1277            .await?
1278            .ok_or_else(|| anyhow!("directory does not exist"))?
1279            .is_symlink;
1280
1281        if let Ok(mut language_paths) = fs.read_dir(&extension_dir.join("languages")).await {
1282            while let Some(language_path) = language_paths.next().await {
1283                let language_path = language_path?;
1284                let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else {
1285                    continue;
1286                };
1287                let Ok(Some(fs_metadata)) = fs.metadata(&language_path).await else {
1288                    continue;
1289                };
1290                if !fs_metadata.is_dir {
1291                    continue;
1292                }
1293                let config = fs.load(&language_path.join("config.toml")).await?;
1294                let config = ::toml::from_str::<LanguageConfig>(&config)?;
1295
1296                let relative_path = relative_path.to_path_buf();
1297                if !extension_manifest.languages.contains(&relative_path) {
1298                    extension_manifest.languages.push(relative_path.clone());
1299                }
1300
1301                index.languages.insert(
1302                    config.name.clone(),
1303                    ExtensionIndexLanguageEntry {
1304                        extension: extension_id.clone(),
1305                        path: relative_path,
1306                        matcher: config.matcher,
1307                        grammar: config.grammar,
1308                    },
1309                );
1310            }
1311        }
1312
1313        if let Ok(mut theme_paths) = fs.read_dir(&extension_dir.join("themes")).await {
1314            while let Some(theme_path) = theme_paths.next().await {
1315                let theme_path = theme_path?;
1316                let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) else {
1317                    continue;
1318                };
1319
1320                let Some(theme_family) = ThemeRegistry::read_user_theme(&theme_path, fs.clone())
1321                    .await
1322                    .log_err()
1323                else {
1324                    continue;
1325                };
1326
1327                let relative_path = relative_path.to_path_buf();
1328                if !extension_manifest.themes.contains(&relative_path) {
1329                    extension_manifest.themes.push(relative_path.clone());
1330                }
1331
1332                for theme in theme_family.themes {
1333                    index.themes.insert(
1334                        theme.name.into(),
1335                        ExtensionIndexThemeEntry {
1336                            extension: extension_id.clone(),
1337                            path: relative_path.clone(),
1338                        },
1339                    );
1340                }
1341            }
1342        }
1343
1344        let extension_wasm_path = extension_dir.join("extension.wasm");
1345        if fs.is_file(&extension_wasm_path).await {
1346            extension_manifest
1347                .lib
1348                .kind
1349                .get_or_insert(ExtensionLibraryKind::Rust);
1350        }
1351
1352        index.extensions.insert(
1353            extension_id.clone(),
1354            ExtensionIndexEntry {
1355                dev: is_dev,
1356                manifest: Arc::new(extension_manifest),
1357            },
1358        );
1359
1360        Ok(())
1361    }
1362}
1363
1364fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
1365    let mut result = LanguageQueries::default();
1366    if let Some(entries) = std::fs::read_dir(root_path).log_err() {
1367        for entry in entries {
1368            let Some(entry) = entry.log_err() else {
1369                continue;
1370            };
1371            let path = entry.path();
1372            if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
1373                if !remainder.ends_with(".scm") {
1374                    continue;
1375                }
1376                for (name, query) in QUERY_FILENAME_PREFIXES {
1377                    if remainder.starts_with(name) {
1378                        if let Some(contents) = std::fs::read_to_string(&path).log_err() {
1379                            match query(&mut result) {
1380                                None => *query(&mut result) = Some(contents.into()),
1381                                Some(r) => r.to_mut().push_str(contents.as_ref()),
1382                            }
1383                        }
1384                        break;
1385                    }
1386                }
1387            }
1388        }
1389    }
1390    result
1391}