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