extension_host.rs

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