extension_host.rs

   1mod anthropic_migration;
   2mod capability_granter;
   3mod copilot_migration;
   4pub mod extension_settings;
   5mod google_ai_migration;
   6pub mod headless_host;
   7mod open_router_migration;
   8mod openai_migration;
   9pub mod wasm_host;
  10
  11#[cfg(test)]
  12mod extension_store_test;
  13
  14use anyhow::{Context as _, Result, anyhow, bail};
  15use async_compression::futures::bufread::GzipDecoder;
  16use async_tar::Archive;
  17use client::ExtensionProvides;
  18use client::{Client, ExtensionMetadata, GetExtensionsResponse, proto, telemetry::Telemetry};
  19use collections::{BTreeMap, BTreeSet, HashSet, btree_map};
  20
  21pub use extension::ExtensionManifest;
  22use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
  23use extension::{
  24    ExtensionContextServerProxy, ExtensionDebugAdapterProviderProxy, ExtensionEvents,
  25    ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageModelProviderProxy,
  26    ExtensionLanguageProxy, ExtensionLanguageServerProxy, ExtensionSlashCommandProxy,
  27    ExtensionSnippetProxy, ExtensionThemeProxy,
  28};
  29use fs::{Fs, RemoveOptions};
  30use futures::future::join_all;
  31use futures::{
  32    AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
  33    channel::{
  34        mpsc::{UnboundedSender, unbounded},
  35        oneshot,
  36    },
  37    io::BufReader,
  38    select_biased,
  39};
  40use gpui::{
  41    App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, SharedString, Task,
  42    WeakEntity, actions,
  43};
  44use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
  45use language::{
  46    LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LoadedLanguage,
  47    QUERY_FILENAME_PREFIXES, Rope,
  48};
  49use node_runtime::NodeRuntime;
  50use project::ContextProviderWithTasks;
  51use release_channel::ReleaseChannel;
  52use remote::RemoteClient;
  53use semver::Version;
  54use serde::{Deserialize, Serialize};
  55use settings::Settings;
  56use std::ops::RangeInclusive;
  57use std::str::FromStr;
  58use std::{
  59    cmp::Ordering,
  60    path::{self, Path, PathBuf},
  61    sync::Arc,
  62    time::Duration,
  63};
  64use url::Url;
  65use util::{ResultExt, paths::RemotePathBuf};
  66use wasm_host::llm_provider::ExtensionLanguageModelProvider;
  67use wasm_host::{
  68    WasmExtension, WasmHost,
  69    wit::{LlmModelInfo, LlmProviderInfo, is_supported_wasm_api_version, wasm_api_version_range},
  70};
  71
  72struct LlmProviderWithModels {
  73    provider_info: LlmProviderInfo,
  74    models: Vec<LlmModelInfo>,
  75    is_authenticated: bool,
  76    icon_path: Option<SharedString>,
  77    auth_config: Option<extension::LanguageModelAuthConfig>,
  78}
  79
  80pub use extension::{
  81    ExtensionLibraryKind, GrammarManifestEntry, OldExtensionManifest, SchemaVersion,
  82};
  83pub use extension_settings::ExtensionSettings;
  84
  85pub const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
  86const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
  87
  88/// Extension IDs that are being migrated from hardcoded LLM providers.
  89/// For backwards compatibility, if the user has the corresponding env var set,
  90/// we automatically enable env var reading for these extensions on first install.
  91const LEGACY_LLM_EXTENSION_IDS: &[&str] = &[
  92    "anthropic",
  93    "copilot-chat",
  94    "google-ai",
  95    "openrouter",
  96    "openai",
  97];
  98
  99/// Migrates legacy LLM provider extensions by auto-enabling env var reading
 100/// if the env var is currently present in the environment.
 101///
 102/// This is idempotent: if the provider is already in `allowed_env_var_providers`,
 103/// we skip. This means if a user explicitly removes it, it will be re-added on
 104/// next launch if the env var is still set - but that's predictable behavior.
 105fn migrate_legacy_llm_provider_env_var(manifest: &ExtensionManifest, cx: &mut App) {
 106    // Only apply migration to known legacy LLM extensions
 107    if !LEGACY_LLM_EXTENSION_IDS.contains(&manifest.id.as_ref()) {
 108        return;
 109    }
 110
 111    // Check each provider in the manifest
 112    for (provider_id, provider_entry) in &manifest.language_model_providers {
 113        let Some(auth_config) = &provider_entry.auth else {
 114            continue;
 115        };
 116        let Some(env_var_name) = &auth_config.env_var else {
 117            continue;
 118        };
 119
 120        let full_provider_id: Arc<str> = format!("{}:{}", manifest.id, provider_id).into();
 121
 122        // Check if the env var is present and non-empty
 123        let env_var_is_set = std::env::var(env_var_name)
 124            .map(|v| !v.is_empty())
 125            .unwrap_or(false);
 126
 127        // If env var isn't set, no need to do anything
 128        if !env_var_is_set {
 129            continue;
 130        }
 131
 132        // Check if already enabled in settings
 133        let already_enabled = ExtensionSettings::get_global(cx)
 134            .allowed_env_var_providers
 135            .contains(full_provider_id.as_ref());
 136
 137        if already_enabled {
 138            continue;
 139        }
 140
 141        // Enable env var reading since the env var is set
 142        settings::update_settings_file(<dyn fs::Fs>::global(cx), cx, {
 143            let full_provider_id = full_provider_id.clone();
 144            move |settings, _| {
 145                let providers = settings
 146                    .extension
 147                    .allowed_env_var_providers
 148                    .get_or_insert_with(Vec::new);
 149
 150                if !providers
 151                    .iter()
 152                    .any(|id| id.as_ref() == full_provider_id.as_ref())
 153                {
 154                    providers.push(full_provider_id);
 155                }
 156            }
 157        });
 158    }
 159}
 160
 161/// The current extension [`SchemaVersion`] supported by Zed.
 162const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1);
 163
 164/// Extensions that should no longer be loaded or downloaded.
 165///
 166/// These snippets should no longer be downloaded or loaded, because their
 167/// functionality has been integrated into the core editor.
 168const SUPPRESSED_EXTENSIONS: &[&str] = &["snippets", "ruff", "ty", "basedpyright"];
 169
 170/// Returns the [`SchemaVersion`] range that is compatible with this version of Zed.
 171pub fn schema_version_range() -> RangeInclusive<SchemaVersion> {
 172    SchemaVersion::ZERO..=CURRENT_SCHEMA_VERSION
 173}
 174
 175/// Returns whether the given extension version is compatible with this version of Zed.
 176pub fn is_version_compatible(
 177    release_channel: ReleaseChannel,
 178    extension_version: &ExtensionMetadata,
 179) -> bool {
 180    let schema_version = extension_version.manifest.schema_version.unwrap_or(0);
 181    if CURRENT_SCHEMA_VERSION.0 < schema_version {
 182        return false;
 183    }
 184
 185    if let Some(wasm_api_version) = extension_version
 186        .manifest
 187        .wasm_api_version
 188        .as_ref()
 189        .and_then(|wasm_api_version| Version::from_str(wasm_api_version).ok())
 190        && !is_supported_wasm_api_version(release_channel, wasm_api_version)
 191    {
 192        return false;
 193    }
 194
 195    true
 196}
 197
 198pub struct ExtensionStore {
 199    pub proxy: Arc<ExtensionHostProxy>,
 200    pub builder: Arc<ExtensionBuilder>,
 201    pub extension_index: ExtensionIndex,
 202    pub fs: Arc<dyn Fs>,
 203    pub http_client: Arc<HttpClientWithUrl>,
 204    pub telemetry: Option<Arc<Telemetry>>,
 205    pub reload_tx: UnboundedSender<Option<Arc<str>>>,
 206    pub reload_complete_senders: Vec<oneshot::Sender<()>>,
 207    pub installed_dir: PathBuf,
 208    pub outstanding_operations: BTreeMap<Arc<str>, ExtensionOperation>,
 209    pub index_path: PathBuf,
 210    pub modified_extensions: HashSet<Arc<str>>,
 211    pub wasm_host: Arc<WasmHost>,
 212    pub wasm_extensions: Vec<(Arc<ExtensionManifest>, WasmExtension)>,
 213    pub tasks: Vec<Task<()>>,
 214    pub remote_clients: Vec<WeakEntity<RemoteClient>>,
 215    pub ssh_registered_tx: UnboundedSender<()>,
 216}
 217
 218#[derive(Clone, Copy)]
 219pub enum ExtensionOperation {
 220    Upgrade,
 221    Install,
 222    /// Auto-install from settings - triggers legacy LLM provider migrations
 223    AutoInstall,
 224    Remove,
 225}
 226
 227#[derive(Clone)]
 228pub enum Event {
 229    ExtensionsUpdated,
 230    StartedReloading,
 231    ExtensionInstalled(Arc<str>),
 232    ExtensionUninstalled(Arc<str>),
 233    ExtensionFailedToLoad(Arc<str>),
 234}
 235
 236impl EventEmitter<Event> for ExtensionStore {}
 237
 238struct GlobalExtensionStore(Entity<ExtensionStore>);
 239
 240impl Global for GlobalExtensionStore {}
 241
 242#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
 243pub struct ExtensionIndex {
 244    pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
 245    pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
 246    #[serde(default)]
 247    pub icon_themes: BTreeMap<Arc<str>, ExtensionIndexIconThemeEntry>,
 248    pub languages: BTreeMap<LanguageName, ExtensionIndexLanguageEntry>,
 249}
 250
 251#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
 252pub struct ExtensionIndexEntry {
 253    pub manifest: Arc<ExtensionManifest>,
 254    pub dev: bool,
 255}
 256
 257#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 258pub struct ExtensionIndexThemeEntry {
 259    pub extension: Arc<str>,
 260    pub path: PathBuf,
 261}
 262
 263#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 264pub struct ExtensionIndexIconThemeEntry {
 265    pub extension: Arc<str>,
 266    pub path: PathBuf,
 267}
 268
 269#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 270pub struct ExtensionIndexLanguageEntry {
 271    pub extension: Arc<str>,
 272    pub path: PathBuf,
 273    pub matcher: LanguageMatcher,
 274    pub hidden: bool,
 275    pub grammar: Option<Arc<str>>,
 276}
 277
 278actions!(
 279    zed,
 280    [
 281        /// Reloads all installed extensions.
 282        ReloadExtensions
 283    ]
 284);
 285
 286pub fn init(
 287    extension_host_proxy: Arc<ExtensionHostProxy>,
 288    fs: Arc<dyn Fs>,
 289    client: Arc<Client>,
 290    node_runtime: NodeRuntime,
 291    cx: &mut App,
 292) {
 293    let store = cx.new(move |cx| {
 294        ExtensionStore::new(
 295            paths::extensions_dir().clone(),
 296            None,
 297            extension_host_proxy,
 298            fs,
 299            client.http_client(),
 300            client.http_client(),
 301            Some(client.telemetry().clone()),
 302            node_runtime,
 303            cx,
 304        )
 305    });
 306
 307    cx.on_action(|_: &ReloadExtensions, cx| {
 308        let store = cx.global::<GlobalExtensionStore>().0.clone();
 309        store.update(cx, |store, cx| drop(store.reload(None, cx)));
 310    });
 311
 312    cx.set_global(GlobalExtensionStore(store));
 313}
 314
 315impl ExtensionStore {
 316    pub fn try_global(cx: &App) -> Option<Entity<Self>> {
 317        cx.try_global::<GlobalExtensionStore>()
 318            .map(|store| store.0.clone())
 319    }
 320
 321    pub fn global(cx: &App) -> Entity<Self> {
 322        cx.global::<GlobalExtensionStore>().0.clone()
 323    }
 324
 325    pub fn new(
 326        extensions_dir: PathBuf,
 327        build_dir: Option<PathBuf>,
 328        extension_host_proxy: Arc<ExtensionHostProxy>,
 329        fs: Arc<dyn Fs>,
 330        http_client: Arc<HttpClientWithUrl>,
 331        builder_client: Arc<dyn HttpClient>,
 332        telemetry: Option<Arc<Telemetry>>,
 333        node_runtime: NodeRuntime,
 334        cx: &mut Context<Self>,
 335    ) -> Self {
 336        let work_dir = extensions_dir.join("work");
 337        let build_dir = build_dir.unwrap_or_else(|| extensions_dir.join("build"));
 338        let installed_dir = extensions_dir.join("installed");
 339        let index_path = extensions_dir.join("index.json");
 340
 341        let (reload_tx, mut reload_rx) = unbounded();
 342        let (connection_registered_tx, mut connection_registered_rx) = unbounded();
 343        let mut this = Self {
 344            proxy: extension_host_proxy.clone(),
 345            extension_index: Default::default(),
 346            installed_dir,
 347            index_path,
 348            builder: Arc::new(ExtensionBuilder::new(builder_client, build_dir)),
 349            outstanding_operations: Default::default(),
 350            modified_extensions: Default::default(),
 351            reload_complete_senders: Vec::new(),
 352            wasm_host: WasmHost::new(
 353                fs.clone(),
 354                http_client.clone(),
 355                node_runtime,
 356                extension_host_proxy,
 357                work_dir,
 358                cx,
 359            ),
 360            wasm_extensions: Vec::new(),
 361            fs,
 362            http_client,
 363            telemetry,
 364            reload_tx,
 365            tasks: Vec::new(),
 366
 367            remote_clients: Default::default(),
 368            ssh_registered_tx: connection_registered_tx,
 369        };
 370
 371        // The extensions store maintains an index file, which contains a complete
 372        // list of the installed extensions and the resources that they provide.
 373        // This index is loaded synchronously on startup.
 374        let (index_content, index_metadata, extensions_metadata) =
 375            cx.background_executor().block(async {
 376                futures::join!(
 377                    this.fs.load(&this.index_path),
 378                    this.fs.metadata(&this.index_path),
 379                    this.fs.metadata(&this.installed_dir),
 380                )
 381            });
 382
 383        // Normally, there is no need to rebuild the index. But if the index file
 384        // is invalid or is out-of-date according to the filesystem mtimes, then
 385        // it must be asynchronously rebuilt.
 386        let mut extension_index = ExtensionIndex::default();
 387        let mut extension_index_needs_rebuild = true;
 388        if let Ok(index_content) = index_content
 389            && let Some(index) = serde_json::from_str(&index_content).log_err()
 390        {
 391            extension_index = index;
 392            if let (Ok(Some(index_metadata)), Ok(Some(extensions_metadata))) =
 393                (index_metadata, extensions_metadata)
 394                && index_metadata
 395                    .mtime
 396                    .bad_is_greater_than(extensions_metadata.mtime)
 397            {
 398                extension_index_needs_rebuild = false;
 399            }
 400        }
 401
 402        // Immediately load all of the extensions in the initial manifest. If the
 403        // index needs to be rebuild, then enqueue
 404        let load_initial_extensions = this.extensions_updated(extension_index, cx);
 405        let mut reload_future = None;
 406        if extension_index_needs_rebuild {
 407            reload_future = Some(this.reload(None, cx));
 408        }
 409
 410        cx.spawn(async move |this, cx| {
 411            if let Some(future) = reload_future {
 412                future.await;
 413            }
 414            this.update(cx, |this, cx| this.auto_install_extensions(cx))
 415                .ok();
 416            this.update(cx, |this, cx| this.check_for_updates(cx)).ok();
 417        })
 418        .detach();
 419
 420        // Perform all extension loading in a single task to ensure that we
 421        // never attempt to simultaneously load/unload extensions from multiple
 422        // parallel tasks.
 423        this.tasks.push(cx.spawn(async move |this, cx| {
 424            async move {
 425                load_initial_extensions.await;
 426
 427                let mut index_changed = false;
 428                let mut debounce_timer = cx.background_spawn(futures::future::pending()).fuse();
 429                loop {
 430                    select_biased! {
 431                        _ = debounce_timer => {
 432                            if index_changed {
 433                                let index = this
 434                                    .update(cx, |this, cx| this.rebuild_extension_index(cx))?
 435                                    .await;
 436                                this.update(cx, |this, cx| this.extensions_updated(index, cx))?
 437                                    .await;
 438                                index_changed = false;
 439                            }
 440
 441                            Self::update_remote_clients(&this, cx).await?;
 442                        }
 443                        _ = connection_registered_rx.next() => {
 444                            debounce_timer = cx
 445                                .background_executor()
 446                                .timer(RELOAD_DEBOUNCE_DURATION)
 447                                .fuse();
 448                        }
 449                        extension_id = reload_rx.next() => {
 450                            let Some(extension_id) = extension_id else { break; };
 451                            this.update(cx, |this, _| {
 452                                this.modified_extensions.extend(extension_id);
 453                            })?;
 454                            index_changed = true;
 455                            debounce_timer = cx
 456                                .background_executor()
 457                                .timer(RELOAD_DEBOUNCE_DURATION)
 458                                .fuse();
 459                        }
 460                    }
 461                }
 462
 463                anyhow::Ok(())
 464            }
 465            .map(drop)
 466            .await;
 467        }));
 468
 469        // Watch the installed extensions directory for changes. Whenever changes are
 470        // detected, rebuild the extension index, and load/unload any extensions that
 471        // have been added, removed, or modified.
 472        this.tasks.push(cx.background_spawn({
 473            let fs = this.fs.clone();
 474            let reload_tx = this.reload_tx.clone();
 475            let installed_dir = this.installed_dir.clone();
 476            async move {
 477                let (mut paths, _) = fs.watch(&installed_dir, FS_WATCH_LATENCY).await;
 478                while let Some(events) = paths.next().await {
 479                    for event in events {
 480                        let Ok(event_path) = event.path.strip_prefix(&installed_dir) else {
 481                            continue;
 482                        };
 483
 484                        if let Some(path::Component::Normal(extension_dir_name)) =
 485                            event_path.components().next()
 486                            && let Some(extension_id) = extension_dir_name.to_str()
 487                        {
 488                            reload_tx.unbounded_send(Some(extension_id.into())).ok();
 489                        }
 490                    }
 491                }
 492            }
 493        }));
 494
 495        this
 496    }
 497
 498    pub fn reload(
 499        &mut self,
 500        modified_extension: Option<Arc<str>>,
 501        cx: &mut Context<Self>,
 502    ) -> impl Future<Output = ()> + use<> {
 503        let (tx, rx) = oneshot::channel();
 504        self.reload_complete_senders.push(tx);
 505        self.reload_tx
 506            .unbounded_send(modified_extension)
 507            .expect("reload task exited");
 508        cx.emit(Event::StartedReloading);
 509
 510        async move {
 511            rx.await.ok();
 512        }
 513    }
 514
 515    fn extensions_dir(&self) -> PathBuf {
 516        self.installed_dir.clone()
 517    }
 518
 519    pub fn outstanding_operations(&self) -> &BTreeMap<Arc<str>, ExtensionOperation> {
 520        &self.outstanding_operations
 521    }
 522
 523    pub fn installed_extensions(&self) -> &BTreeMap<Arc<str>, ExtensionIndexEntry> {
 524        &self.extension_index.extensions
 525    }
 526
 527    pub fn dev_extensions(&self) -> impl Iterator<Item = &Arc<ExtensionManifest>> {
 528        self.extension_index
 529            .extensions
 530            .values()
 531            .filter_map(|extension| extension.dev.then_some(&extension.manifest))
 532    }
 533
 534    pub fn extension_manifest_for_id(&self, extension_id: &str) -> Option<&Arc<ExtensionManifest>> {
 535        self.extension_index
 536            .extensions
 537            .get(extension_id)
 538            .map(|extension| &extension.manifest)
 539    }
 540
 541    /// Returns the names of themes provided by extensions.
 542    pub fn extension_themes<'a>(
 543        &'a self,
 544        extension_id: &'a str,
 545    ) -> impl Iterator<Item = &'a Arc<str>> {
 546        self.extension_index
 547            .themes
 548            .iter()
 549            .filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name))
 550    }
 551
 552    /// Returns the path to the theme file within an extension, if there is an
 553    /// extension that provides the theme.
 554    pub fn path_to_extension_theme(&self, theme_name: &str) -> Option<PathBuf> {
 555        let entry = self.extension_index.themes.get(theme_name)?;
 556
 557        Some(
 558            self.extensions_dir()
 559                .join(entry.extension.as_ref())
 560                .join(&entry.path),
 561        )
 562    }
 563
 564    /// Returns the names of icon themes provided by extensions.
 565    pub fn extension_icon_themes<'a>(
 566        &'a self,
 567        extension_id: &'a str,
 568    ) -> impl Iterator<Item = &'a Arc<str>> {
 569        self.extension_index
 570            .icon_themes
 571            .iter()
 572            .filter_map(|(name, icon_theme)| {
 573                icon_theme
 574                    .extension
 575                    .as_ref()
 576                    .eq(extension_id)
 577                    .then_some(name)
 578            })
 579    }
 580
 581    /// Returns the path to the icon theme file within an extension, if there is
 582    /// an extension that provides the icon theme.
 583    pub fn path_to_extension_icon_theme(
 584        &self,
 585        icon_theme_name: &str,
 586    ) -> Option<(PathBuf, PathBuf)> {
 587        let entry = self.extension_index.icon_themes.get(icon_theme_name)?;
 588
 589        let icon_theme_path = self
 590            .extensions_dir()
 591            .join(entry.extension.as_ref())
 592            .join(&entry.path);
 593        let icons_root_path = self.extensions_dir().join(entry.extension.as_ref());
 594
 595        Some((icon_theme_path, icons_root_path))
 596    }
 597
 598    pub fn fetch_extensions(
 599        &self,
 600        search: Option<&str>,
 601        provides_filter: Option<&BTreeSet<ExtensionProvides>>,
 602        cx: &mut Context<Self>,
 603    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 604        let version = CURRENT_SCHEMA_VERSION.to_string();
 605        let mut query = vec![("max_schema_version", version.as_str())];
 606        if let Some(search) = search {
 607            query.push(("filter", search));
 608        }
 609
 610        let provides_filter = provides_filter.map(|provides_filter| {
 611            provides_filter
 612                .iter()
 613                .map(|provides| provides.to_string())
 614                .collect::<Vec<_>>()
 615                .join(",")
 616        });
 617        if let Some(provides_filter) = provides_filter.as_deref() {
 618            query.push(("provides", provides_filter));
 619        }
 620
 621        self.fetch_extensions_from_api("/extensions", &query, cx)
 622    }
 623
 624    pub fn fetch_extensions_with_update_available(
 625        &mut self,
 626        cx: &mut Context<Self>,
 627    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 628        let schema_versions = schema_version_range();
 629        let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
 630        let extension_settings = ExtensionSettings::get_global(cx);
 631        let extension_ids = self
 632            .extension_index
 633            .extensions
 634            .iter()
 635            .filter(|(id, entry)| !entry.dev && extension_settings.should_auto_update(id))
 636            .map(|(id, _)| id.as_ref())
 637            .collect::<Vec<_>>()
 638            .join(",");
 639        let task = self.fetch_extensions_from_api(
 640            "/extensions/updates",
 641            &[
 642                ("min_schema_version", &schema_versions.start().to_string()),
 643                ("max_schema_version", &schema_versions.end().to_string()),
 644                (
 645                    "min_wasm_api_version",
 646                    &wasm_api_versions.start().to_string(),
 647                ),
 648                ("max_wasm_api_version", &wasm_api_versions.end().to_string()),
 649                ("ids", &extension_ids),
 650            ],
 651            cx,
 652        );
 653        cx.spawn(async move |this, cx| {
 654            let extensions = task.await?;
 655            this.update(cx, |this, _cx| {
 656                extensions
 657                    .into_iter()
 658                    .filter(|extension| {
 659                        this.extension_index
 660                            .extensions
 661                            .get(&extension.id)
 662                            .is_none_or(|installed_extension| {
 663                                installed_extension.manifest.version != extension.manifest.version
 664                            })
 665                    })
 666                    .collect()
 667            })
 668        })
 669    }
 670
 671    pub fn fetch_extension_versions(
 672        &self,
 673        extension_id: &str,
 674        cx: &mut Context<Self>,
 675    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 676        self.fetch_extensions_from_api(&format!("/extensions/{extension_id}"), &[], cx)
 677    }
 678
 679    /// Installs any extensions that should be included with Zed by default.
 680    ///
 681    /// This can be used to make certain functionality provided by extensions
 682    /// available out-of-the-box.
 683    pub fn auto_install_extensions(&mut self, cx: &mut Context<Self>) {
 684        if cfg!(test) {
 685            return;
 686        }
 687
 688        let extension_settings = ExtensionSettings::get_global(cx);
 689
 690        let extensions_to_install = extension_settings
 691            .auto_install_extensions
 692            .keys()
 693            .filter(|extension_id| extension_settings.should_auto_install(extension_id))
 694            .filter(|extension_id| {
 695                let is_already_installed = self
 696                    .extension_index
 697                    .extensions
 698                    .contains_key(extension_id.as_ref());
 699                let dominated = SUPPRESSED_EXTENSIONS.contains(&extension_id.as_ref());
 700                !is_already_installed && !dominated
 701            })
 702            .cloned()
 703            .collect::<Vec<_>>();
 704
 705        cx.spawn(async move |this, cx| {
 706            for extension_id in extensions_to_install {
 707                // When enabled, this checks if an extension exists locally in the repo's extensions/
 708                // directory and installs it as a dev extension instead of fetching from the registry.
 709                // This is useful for testing auto-installed extensions before they've been published.
 710                // Set to `true` only during local development/testing of new auto-install extensions.
 711                #[cfg(debug_assertions)]
 712                const DEBUG_ALLOW_UNPUBLISHED_AUTO_EXTENSIONS: bool = false;
 713
 714                #[cfg(debug_assertions)]
 715                if DEBUG_ALLOW_UNPUBLISHED_AUTO_EXTENSIONS {
 716                    let local_extension_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
 717                        .parent()
 718                        .unwrap()
 719                        .parent()
 720                        .unwrap()
 721                        .join("extensions")
 722                        .join(extension_id.as_ref());
 723
 724                    if local_extension_path.exists() {
 725                        // Force-remove existing extension directory if it exists and isn't a symlink
 726                        // This handles the case where the extension was previously installed from the registry
 727                        if let Some(installed_dir) = this
 728                            .update(cx, |this, _cx| this.installed_dir.clone())
 729                            .ok()
 730                        {
 731                            let existing_path = installed_dir.join(extension_id.as_ref());
 732                            if existing_path.exists() {
 733                                let metadata = std::fs::symlink_metadata(&existing_path);
 734                                let is_symlink = metadata.map(|m| m.is_symlink()).unwrap_or(false);
 735                                if !is_symlink {
 736                                    if let Err(e) = std::fs::remove_dir_all(&existing_path) {
 737                                        log::error!(
 738                                            "Failed to remove existing extension directory {:?}: {}",
 739                                            existing_path,
 740                                            e
 741                                        );
 742                                    }
 743                                }
 744                            }
 745                        }
 746
 747                        if let Some(task) = this
 748                            .update(cx, |this, cx| {
 749                                this.install_dev_extension(local_extension_path, cx)
 750                            })
 751                            .ok()
 752                        {
 753                            task.await.log_err();
 754                        }
 755                        continue;
 756                    }
 757                }
 758
 759                this.update(cx, |this, cx| {
 760                    this.auto_install_latest_extension(extension_id.clone(), cx);
 761                })
 762                .ok();
 763            }
 764        })
 765        .detach();
 766    }
 767
 768    pub fn check_for_updates(&mut self, cx: &mut Context<Self>) {
 769        let task = self.fetch_extensions_with_update_available(cx);
 770        cx.spawn(async move |this, cx| Self::upgrade_extensions(this, task.await?, cx).await)
 771            .detach();
 772    }
 773
 774    async fn upgrade_extensions(
 775        this: WeakEntity<Self>,
 776        extensions: Vec<ExtensionMetadata>,
 777        cx: &mut AsyncApp,
 778    ) -> Result<()> {
 779        for extension in extensions {
 780            let task = this.update(cx, |this, cx| {
 781                if let Some(installed_extension) =
 782                    this.extension_index.extensions.get(&extension.id)
 783                {
 784                    let installed_version =
 785                        Version::from_str(&installed_extension.manifest.version).ok()?;
 786                    let latest_version = Version::from_str(&extension.manifest.version).ok()?;
 787
 788                    if installed_version >= latest_version {
 789                        return None;
 790                    }
 791                }
 792
 793                Some(this.upgrade_extension(extension.id, extension.manifest.version, cx))
 794            })?;
 795
 796            if let Some(task) = task {
 797                task.await.log_err();
 798            }
 799        }
 800        anyhow::Ok(())
 801    }
 802
 803    fn fetch_extensions_from_api(
 804        &self,
 805        path: &str,
 806        query: &[(&str, &str)],
 807        cx: &mut Context<ExtensionStore>,
 808    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 809        let url = self.http_client.build_zed_api_url(path, query);
 810        let http_client = self.http_client.clone();
 811        cx.spawn(async move |_, _| {
 812            let mut response = http_client
 813                .get(url?.as_ref(), AsyncBody::empty(), true)
 814                .await?;
 815
 816            let mut body = Vec::new();
 817            response
 818                .body_mut()
 819                .read_to_end(&mut body)
 820                .await
 821                .context("error reading extensions")?;
 822
 823            if response.status().is_client_error() {
 824                let text = String::from_utf8_lossy(body.as_slice());
 825                bail!(
 826                    "status error {}, response: {text:?}",
 827                    response.status().as_u16()
 828                );
 829            }
 830
 831            let mut response: GetExtensionsResponse = serde_json::from_slice(&body)?;
 832
 833            response
 834                .data
 835                .retain(|extension| !SUPPRESSED_EXTENSIONS.contains(&extension.id.as_ref()));
 836
 837            Ok(response.data)
 838        })
 839    }
 840
 841    pub fn install_extension(
 842        &mut self,
 843        extension_id: Arc<str>,
 844        version: Arc<str>,
 845        cx: &mut Context<Self>,
 846    ) {
 847        self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Install, cx)
 848            .detach_and_log_err(cx);
 849    }
 850
 851    fn install_or_upgrade_extension_at_endpoint(
 852        &mut self,
 853        extension_id: Arc<str>,
 854        url: Url,
 855        operation: ExtensionOperation,
 856        cx: &mut Context<Self>,
 857    ) -> Task<Result<()>> {
 858        let extension_dir = self.installed_dir.join(extension_id.as_ref());
 859        let http_client = self.http_client.clone();
 860        let fs = self.fs.clone();
 861
 862        match self.outstanding_operations.entry(extension_id.clone()) {
 863            btree_map::Entry::Occupied(_) => return Task::ready(Ok(())),
 864            btree_map::Entry::Vacant(e) => e.insert(operation),
 865        };
 866        cx.notify();
 867
 868        cx.spawn(async move |this, cx| {
 869            let _finish = cx.on_drop(&this, {
 870                let extension_id = extension_id.clone();
 871                move |this, cx| {
 872                    this.outstanding_operations.remove(extension_id.as_ref());
 873                    cx.notify();
 874                }
 875            });
 876
 877            let mut response = http_client
 878                .get(url.as_ref(), Default::default(), true)
 879                .await
 880                .context("downloading extension")?;
 881
 882            fs.remove_dir(
 883                &extension_dir,
 884                RemoveOptions {
 885                    recursive: true,
 886                    ignore_if_not_exists: true,
 887                },
 888            )
 889            .await?;
 890
 891            let content_length = response
 892                .headers()
 893                .get(http_client::http::header::CONTENT_LENGTH)
 894                .and_then(|value| value.to_str().ok()?.parse::<usize>().ok());
 895
 896            let mut body = BufReader::new(response.body_mut());
 897            let mut tar_gz_bytes = Vec::new();
 898            body.read_to_end(&mut tar_gz_bytes).await?;
 899
 900            if let Some(content_length) = content_length {
 901                let actual_len = tar_gz_bytes.len();
 902                if content_length != actual_len {
 903                    bail!(concat!(
 904                        "downloaded extension size {actual_len} ",
 905                        "does not match content length {content_length}"
 906                    ));
 907                }
 908            }
 909            let decompressed_bytes = GzipDecoder::new(BufReader::new(tar_gz_bytes.as_slice()));
 910            let archive = Archive::new(decompressed_bytes);
 911            archive.unpack(extension_dir).await?;
 912            this.update(cx, |this, cx| this.reload(Some(extension_id.clone()), cx))?
 913                .await;
 914
 915            if matches!(
 916                operation,
 917                ExtensionOperation::Install | ExtensionOperation::AutoInstall
 918            ) {
 919                this.update(cx, |this, cx| {
 920                    cx.emit(Event::ExtensionInstalled(extension_id.clone()));
 921                    if let Some(events) = ExtensionEvents::try_global(cx)
 922                        && let Some(manifest) = this.extension_manifest_for_id(&extension_id)
 923                    {
 924                        events.update(cx, |this, cx| {
 925                            this.emit(extension::Event::ExtensionInstalled(manifest.clone()), cx)
 926                        });
 927                    }
 928
 929                    // Run legacy LLM provider migrations only for auto-installed extensions
 930                    if matches!(operation, ExtensionOperation::AutoInstall) {
 931                        if let Some(manifest) = this.extension_manifest_for_id(&extension_id) {
 932                            migrate_legacy_llm_provider_env_var(&manifest, cx);
 933                        }
 934                        copilot_migration::migrate_copilot_credentials_if_needed(&extension_id, cx);
 935                        anthropic_migration::migrate_anthropic_credentials_if_needed(
 936                            &extension_id,
 937                            cx,
 938                        );
 939                        google_ai_migration::migrate_google_ai_credentials_if_needed(
 940                            &extension_id,
 941                            cx,
 942                        );
 943                        openai_migration::migrate_openai_credentials_if_needed(&extension_id, cx);
 944                        open_router_migration::migrate_open_router_credentials_if_needed(
 945                            &extension_id,
 946                            cx,
 947                        );
 948                    }
 949                })
 950                .ok();
 951            }
 952
 953            anyhow::Ok(())
 954        })
 955    }
 956
 957    pub fn install_latest_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
 958        self.install_latest_extension_with_operation(extension_id, ExtensionOperation::Install, cx);
 959    }
 960
 961    /// Auto-install an extension, triggering legacy LLM provider migrations.
 962    fn auto_install_latest_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
 963        self.install_latest_extension_with_operation(
 964            extension_id,
 965            ExtensionOperation::AutoInstall,
 966            cx,
 967        );
 968    }
 969
 970    fn install_latest_extension_with_operation(
 971        &mut self,
 972        extension_id: Arc<str>,
 973        operation: ExtensionOperation,
 974        cx: &mut Context<Self>,
 975    ) {
 976        let schema_versions = schema_version_range();
 977        let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
 978
 979        let Some(url) = self
 980            .http_client
 981            .build_zed_api_url(
 982                &format!("/extensions/{extension_id}/download"),
 983                &[
 984                    ("min_schema_version", &schema_versions.start().to_string()),
 985                    ("max_schema_version", &schema_versions.end().to_string()),
 986                    (
 987                        "min_wasm_api_version",
 988                        &wasm_api_versions.start().to_string(),
 989                    ),
 990                    ("max_wasm_api_version", &wasm_api_versions.end().to_string()),
 991                ],
 992            )
 993            .log_err()
 994        else {
 995            return;
 996        };
 997
 998        self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx)
 999            .detach_and_log_err(cx);
1000    }
1001
1002    pub fn upgrade_extension(
1003        &mut self,
1004        extension_id: Arc<str>,
1005        version: Arc<str>,
1006        cx: &mut Context<Self>,
1007    ) -> Task<Result<()>> {
1008        self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Upgrade, cx)
1009    }
1010
1011    fn install_or_upgrade_extension(
1012        &mut self,
1013        extension_id: Arc<str>,
1014        version: Arc<str>,
1015        operation: ExtensionOperation,
1016        cx: &mut Context<Self>,
1017    ) -> Task<Result<()>> {
1018        let Some(url) = self
1019            .http_client
1020            .build_zed_api_url(
1021                &format!("/extensions/{extension_id}/{version}/download"),
1022                &[],
1023            )
1024            .log_err()
1025        else {
1026            return Task::ready(Ok(()));
1027        };
1028
1029        self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx)
1030    }
1031
1032    pub fn uninstall_extension(
1033        &mut self,
1034        extension_id: Arc<str>,
1035        cx: &mut Context<Self>,
1036    ) -> Task<Result<()>> {
1037        let extension_dir = self.installed_dir.join(extension_id.as_ref());
1038        let work_dir = self.wasm_host.work_dir.join(extension_id.as_ref());
1039        let fs = self.fs.clone();
1040
1041        let extension_manifest = self.extension_manifest_for_id(&extension_id).cloned();
1042
1043        match self.outstanding_operations.entry(extension_id.clone()) {
1044            btree_map::Entry::Occupied(_) => return Task::ready(Ok(())),
1045            btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
1046        };
1047
1048        cx.spawn(async move |extension_store, cx| {
1049            let _finish = cx.on_drop(&extension_store, {
1050                let extension_id = extension_id.clone();
1051                move |this, cx| {
1052                    this.outstanding_operations.remove(extension_id.as_ref());
1053                    cx.notify();
1054                }
1055            });
1056
1057            fs.remove_dir(
1058                &extension_dir,
1059                RemoveOptions {
1060                    recursive: true,
1061                    ignore_if_not_exists: true,
1062                },
1063            )
1064            .await
1065            .with_context(|| format!("Removing extension dir {extension_dir:?}"))?;
1066
1067            extension_store
1068                .update(cx, |extension_store, cx| extension_store.reload(None, cx))?
1069                .await;
1070
1071            // There's a race between wasm extension fully stopping and the directory removal.
1072            // On Windows, it's impossible to remove a directory that has a process running in it.
1073            for i in 0..3 {
1074                cx.background_executor()
1075                    .timer(Duration::from_millis(i * 100))
1076                    .await;
1077                let removal_result = fs
1078                    .remove_dir(
1079                        &work_dir,
1080                        RemoveOptions {
1081                            recursive: true,
1082                            ignore_if_not_exists: true,
1083                        },
1084                    )
1085                    .await;
1086                match removal_result {
1087                    Ok(()) => break,
1088                    Err(e) => {
1089                        if i == 2 {
1090                            log::error!("Failed to remove extension work dir {work_dir:?} : {e}");
1091                        }
1092                    }
1093                }
1094            }
1095
1096            extension_store.update(cx, |_, cx| {
1097                cx.emit(Event::ExtensionUninstalled(extension_id.clone()));
1098                if let Some(events) = ExtensionEvents::try_global(cx)
1099                    && let Some(manifest) = extension_manifest
1100                {
1101                    events.update(cx, |this, cx| {
1102                        this.emit(extension::Event::ExtensionUninstalled(manifest.clone()), cx)
1103                    });
1104                }
1105            })?;
1106
1107            anyhow::Ok(())
1108        })
1109    }
1110
1111    pub fn install_dev_extension(
1112        &mut self,
1113        extension_source_path: PathBuf,
1114        cx: &mut Context<Self>,
1115    ) -> Task<Result<()>> {
1116        let extensions_dir = self.extensions_dir();
1117        let fs = self.fs.clone();
1118        let builder = self.builder.clone();
1119
1120        cx.spawn(async move |this, cx| {
1121            let mut extension_manifest =
1122                ExtensionManifest::load(fs.clone(), &extension_source_path).await?;
1123            let extension_id = extension_manifest.id.clone();
1124
1125            if let Some(uninstall_task) = this
1126                .update(cx, |this, cx| {
1127                    this.extension_index
1128                        .extensions
1129                        .get(extension_id.as_ref())
1130                        .is_some_and(|index_entry| !index_entry.dev)
1131                        .then(|| this.uninstall_extension(extension_id.clone(), cx))
1132                })
1133                .ok()
1134                .flatten()
1135            {
1136                uninstall_task.await.log_err();
1137            }
1138
1139            if !this.update(cx, |this, cx| {
1140                match this.outstanding_operations.entry(extension_id.clone()) {
1141                    btree_map::Entry::Occupied(_) => return false,
1142                    btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Install),
1143                };
1144                cx.notify();
1145                true
1146            })? {
1147                return Ok(());
1148            }
1149
1150            let _finish = cx.on_drop(&this, {
1151                let extension_id = extension_id.clone();
1152                move |this, cx| {
1153                    this.outstanding_operations.remove(extension_id.as_ref());
1154                    cx.notify();
1155                }
1156            });
1157
1158            cx.background_spawn({
1159                let extension_source_path = extension_source_path.clone();
1160                let fs = fs.clone();
1161                async move {
1162                    builder
1163                        .compile_extension(
1164                            &extension_source_path,
1165                            &mut extension_manifest,
1166                            CompileExtensionOptions { release: false },
1167                            fs,
1168                        )
1169                        .await
1170                }
1171            })
1172            .await
1173            .inspect_err(|error| {
1174                util::log_err(error);
1175            })?;
1176
1177            let output_path = &extensions_dir.join(extension_id.as_ref());
1178            if let Some(metadata) = fs.metadata(output_path).await? {
1179                if metadata.is_symlink {
1180                    fs.remove_file(
1181                        output_path,
1182                        RemoveOptions {
1183                            recursive: false,
1184                            ignore_if_not_exists: true,
1185                        },
1186                    )
1187                    .await?;
1188                } else {
1189                    bail!("extension {extension_id} is still installed");
1190                }
1191            }
1192
1193            fs.create_symlink(output_path, extension_source_path)
1194                .await?;
1195
1196            this.update(cx, |this, cx| this.reload(None, cx))?.await;
1197            this.update(cx, |this, cx| {
1198                cx.emit(Event::ExtensionInstalled(extension_id.clone()));
1199                if let Some(events) = ExtensionEvents::try_global(cx)
1200                    && let Some(manifest) = this.extension_manifest_for_id(&extension_id)
1201                {
1202                    events.update(cx, |this, cx| {
1203                        this.emit(extension::Event::ExtensionInstalled(manifest.clone()), cx)
1204                    });
1205                }
1206            })?;
1207
1208            Ok(())
1209        })
1210    }
1211
1212    pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
1213        let path = self.installed_dir.join(extension_id.as_ref());
1214        let builder = self.builder.clone();
1215        let fs = self.fs.clone();
1216
1217        match self.outstanding_operations.entry(extension_id.clone()) {
1218            btree_map::Entry::Occupied(_) => return,
1219            btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Upgrade),
1220        };
1221
1222        cx.notify();
1223        let compile = cx.background_spawn(async move {
1224            let mut manifest = ExtensionManifest::load(fs.clone(), &path).await?;
1225            builder
1226                .compile_extension(
1227                    &path,
1228                    &mut manifest,
1229                    CompileExtensionOptions { release: true },
1230                    fs,
1231                )
1232                .await
1233        });
1234
1235        cx.spawn(async move |this, cx| {
1236            let result = compile.await;
1237
1238            this.update(cx, |this, cx| {
1239                this.outstanding_operations.remove(&extension_id);
1240                cx.notify();
1241            })?;
1242
1243            if result.is_ok() {
1244                this.update(cx, |this, cx| this.reload(Some(extension_id), cx))?
1245                    .await;
1246            }
1247
1248            result
1249        })
1250        .detach_and_log_err(cx)
1251    }
1252
1253    /// Updates the set of installed extensions.
1254    ///
1255    /// First, this unloads any themes, languages, or grammars that are
1256    /// no longer in the manifest, or whose files have changed on disk.
1257    /// Then it loads any themes, languages, or grammars that are newly
1258    /// added to the manifest, or whose files have changed on disk.
1259    fn extensions_updated(
1260        &mut self,
1261        mut new_index: ExtensionIndex,
1262        cx: &mut Context<Self>,
1263    ) -> Task<()> {
1264        let old_index = &self.extension_index;
1265
1266        new_index
1267            .extensions
1268            .retain(|extension_id, _| !SUPPRESSED_EXTENSIONS.contains(&extension_id.as_ref()));
1269
1270        // Determine which extensions need to be loaded and unloaded, based
1271        // on the changes to the manifest and the extensions that we know have been
1272        // modified.
1273        let mut extensions_to_unload = Vec::default();
1274        let mut extensions_to_load = Vec::default();
1275        {
1276            let mut old_keys = old_index.extensions.iter().peekable();
1277            let mut new_keys = new_index.extensions.iter().peekable();
1278            loop {
1279                match (old_keys.peek(), new_keys.peek()) {
1280                    (None, None) => break,
1281                    (None, Some(_)) => {
1282                        extensions_to_load.push(new_keys.next().unwrap().0.clone());
1283                    }
1284                    (Some(_), None) => {
1285                        extensions_to_unload.push(old_keys.next().unwrap().0.clone());
1286                    }
1287                    (Some((old_key, _)), Some((new_key, _))) => match old_key.cmp(new_key) {
1288                        Ordering::Equal => {
1289                            let (old_key, old_value) = old_keys.next().unwrap();
1290                            let (new_key, new_value) = new_keys.next().unwrap();
1291                            if old_value != new_value || self.modified_extensions.contains(old_key)
1292                            {
1293                                extensions_to_unload.push(old_key.clone());
1294                                extensions_to_load.push(new_key.clone());
1295                            }
1296                        }
1297                        Ordering::Less => {
1298                            extensions_to_unload.push(old_keys.next().unwrap().0.clone());
1299                        }
1300                        Ordering::Greater => {
1301                            extensions_to_load.push(new_keys.next().unwrap().0.clone());
1302                        }
1303                    },
1304                }
1305            }
1306            self.modified_extensions.clear();
1307        }
1308
1309        if extensions_to_load.is_empty() && extensions_to_unload.is_empty() {
1310            self.reload_complete_senders.clear();
1311            return Task::ready(());
1312        }
1313
1314        let extension_ids = extensions_to_load
1315            .iter()
1316            .filter_map(|id| {
1317                Some((
1318                    id.clone(),
1319                    new_index.extensions.get(id)?.manifest.version.clone(),
1320                ))
1321            })
1322            .collect::<Vec<_>>();
1323
1324        telemetry::event!("Extensions Loaded", id_and_versions = extension_ids);
1325
1326        let themes_to_remove = old_index
1327            .themes
1328            .iter()
1329            .filter_map(|(name, entry)| {
1330                if extensions_to_unload.contains(&entry.extension) {
1331                    Some(name.clone().into())
1332                } else {
1333                    None
1334                }
1335            })
1336            .collect::<Vec<_>>();
1337        let icon_themes_to_remove = old_index
1338            .icon_themes
1339            .iter()
1340            .filter_map(|(name, entry)| {
1341                if extensions_to_unload.contains(&entry.extension) {
1342                    Some(name.clone().into())
1343                } else {
1344                    None
1345                }
1346            })
1347            .collect::<Vec<_>>();
1348        let languages_to_remove = old_index
1349            .languages
1350            .iter()
1351            .filter_map(|(name, entry)| {
1352                if extensions_to_unload.contains(&entry.extension) {
1353                    Some(name.clone())
1354                } else {
1355                    None
1356                }
1357            })
1358            .collect::<Vec<_>>();
1359        let mut grammars_to_remove = Vec::new();
1360        let mut server_removal_tasks = Vec::with_capacity(extensions_to_unload.len());
1361        for extension_id in &extensions_to_unload {
1362            let Some(extension) = old_index.extensions.get(extension_id) else {
1363                continue;
1364            };
1365            grammars_to_remove.extend(extension.manifest.grammars.keys().cloned());
1366            for (language_server_name, config) in &extension.manifest.language_servers {
1367                for language in config.languages() {
1368                    server_removal_tasks.push(self.proxy.remove_language_server(
1369                        &language,
1370                        language_server_name,
1371                        cx,
1372                    ));
1373                }
1374            }
1375
1376            for server_id in extension.manifest.context_servers.keys() {
1377                self.proxy.unregister_context_server(server_id.clone(), cx);
1378            }
1379            for adapter in extension.manifest.debug_adapters.keys() {
1380                self.proxy.unregister_debug_adapter(adapter.clone());
1381            }
1382            for locator in extension.manifest.debug_locators.keys() {
1383                self.proxy.unregister_debug_locator(locator.clone());
1384            }
1385            for command_name in extension.manifest.slash_commands.keys() {
1386                self.proxy.unregister_slash_command(command_name.clone());
1387            }
1388            for provider_id in extension.manifest.language_model_providers.keys() {
1389                let full_provider_id: Arc<str> = format!("{}:{}", extension_id, provider_id).into();
1390                self.proxy
1391                    .unregister_language_model_provider(full_provider_id, cx);
1392            }
1393        }
1394
1395        self.wasm_extensions
1396            .retain(|(extension, _)| !extensions_to_unload.contains(&extension.id));
1397        self.proxy.remove_user_themes(themes_to_remove);
1398        self.proxy.remove_icon_themes(icon_themes_to_remove);
1399        self.proxy
1400            .remove_languages(&languages_to_remove, &grammars_to_remove);
1401
1402        let mut grammars_to_add = Vec::new();
1403        let mut themes_to_add = Vec::new();
1404        let mut icon_themes_to_add = Vec::new();
1405        let mut snippets_to_add = Vec::new();
1406        for extension_id in &extensions_to_load {
1407            let Some(extension) = new_index.extensions.get(extension_id) else {
1408                continue;
1409            };
1410
1411            grammars_to_add.extend(extension.manifest.grammars.keys().map(|grammar_name| {
1412                let mut grammar_path = self.installed_dir.clone();
1413                grammar_path.extend([extension_id.as_ref(), "grammars"]);
1414                grammar_path.push(grammar_name.as_ref());
1415                grammar_path.set_extension("wasm");
1416                (grammar_name.clone(), grammar_path)
1417            }));
1418            themes_to_add.extend(extension.manifest.themes.iter().map(|theme_path| {
1419                let mut path = self.installed_dir.clone();
1420                path.extend([Path::new(extension_id.as_ref()), theme_path.as_path()]);
1421                path
1422            }));
1423            icon_themes_to_add.extend(extension.manifest.icon_themes.iter().map(
1424                |icon_theme_path| {
1425                    let mut path = self.installed_dir.clone();
1426                    path.extend([Path::new(extension_id.as_ref()), icon_theme_path.as_path()]);
1427
1428                    let mut icons_root_path = self.installed_dir.clone();
1429                    icons_root_path.extend([Path::new(extension_id.as_ref())]);
1430
1431                    (path, icons_root_path)
1432                },
1433            ));
1434            snippets_to_add.extend(extension.manifest.snippets.iter().map(|snippets_path| {
1435                let mut path = self.installed_dir.clone();
1436                path.extend([Path::new(extension_id.as_ref()), snippets_path.as_path()]);
1437                path
1438            }));
1439        }
1440
1441        self.proxy.register_grammars(grammars_to_add);
1442        let languages_to_add = new_index
1443            .languages
1444            .iter()
1445            .filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
1446            .collect::<Vec<_>>();
1447        for (language_name, language) in languages_to_add {
1448            let mut language_path = self.installed_dir.clone();
1449            language_path.extend([
1450                Path::new(language.extension.as_ref()),
1451                language.path.as_path(),
1452            ]);
1453            self.proxy.register_language(
1454                language_name.clone(),
1455                language.grammar.clone(),
1456                language.matcher.clone(),
1457                language.hidden,
1458                Arc::new(move || {
1459                    let config = std::fs::read_to_string(language_path.join("config.toml"))?;
1460                    let config: LanguageConfig = ::toml::from_str(&config)?;
1461                    let queries = load_plugin_queries(&language_path);
1462                    let context_provider =
1463                        std::fs::read_to_string(language_path.join("tasks.json"))
1464                            .ok()
1465                            .and_then(|contents| {
1466                                let definitions =
1467                                    serde_json_lenient::from_str(&contents).log_err()?;
1468                                Some(Arc::new(ContextProviderWithTasks::new(definitions)) as Arc<_>)
1469                            });
1470
1471                    Ok(LoadedLanguage {
1472                        config,
1473                        queries,
1474                        context_provider,
1475                        toolchain_provider: None,
1476                        manifest_name: None,
1477                    })
1478                }),
1479            );
1480        }
1481
1482        let fs = self.fs.clone();
1483        let wasm_host = self.wasm_host.clone();
1484        let root_dir = self.installed_dir.clone();
1485        let proxy = self.proxy.clone();
1486        let extension_entries = extensions_to_load
1487            .iter()
1488            .filter_map(|name| new_index.extensions.get(name).cloned())
1489            .collect::<Vec<_>>();
1490        self.extension_index = new_index;
1491        cx.notify();
1492        cx.emit(Event::ExtensionsUpdated);
1493
1494        cx.spawn(async move |this, cx| {
1495            cx.background_spawn({
1496                let fs = fs.clone();
1497                async move {
1498                    let _ = join_all(server_removal_tasks).await;
1499                    for theme_path in themes_to_add {
1500                        proxy
1501                            .load_user_theme(theme_path, fs.clone())
1502                            .await
1503                            .log_err();
1504                    }
1505
1506                    for (icon_theme_path, icons_root_path) in icon_themes_to_add {
1507                        proxy
1508                            .load_icon_theme(icon_theme_path, icons_root_path, fs.clone())
1509                            .await
1510                            .log_err();
1511                    }
1512
1513                    for snippets_path in &snippets_to_add {
1514                        match fs
1515                            .load(snippets_path)
1516                            .await
1517                            .with_context(|| format!("Loading snippets from {snippets_path:?}"))
1518                        {
1519                            Ok(snippets_contents) => {
1520                                proxy
1521                                    .register_snippet(snippets_path, &snippets_contents)
1522                                    .log_err();
1523                            }
1524                            Err(e) => log::error!("Cannot load snippets: {e:#}"),
1525                        }
1526                    }
1527                }
1528            })
1529            .await;
1530
1531            let mut wasm_extensions: Vec<(
1532                Arc<ExtensionManifest>,
1533                WasmExtension,
1534                Vec<LlmProviderWithModels>,
1535            )> = Vec::new();
1536            for extension in extension_entries {
1537                if extension.manifest.lib.kind.is_none() {
1538                    continue;
1539                };
1540
1541                let extension_path = root_dir.join(extension.manifest.id.as_ref());
1542                let wasm_extension = WasmExtension::load(
1543                    &extension_path,
1544                    &extension.manifest,
1545                    wasm_host.clone(),
1546                    cx,
1547                )
1548                .await
1549                .with_context(|| format!("Loading extension from {extension_path:?}"));
1550
1551                match wasm_extension {
1552                    Ok(wasm_extension) => {
1553                        // Query for LLM providers if the manifest declares any
1554                        let mut llm_providers_with_models = Vec::new();
1555                        if !extension.manifest.language_model_providers.is_empty() {
1556                            let providers_result = wasm_extension
1557                                .call(|ext, store| {
1558                                    async move { ext.call_llm_providers(store).await }.boxed()
1559                                })
1560                                .await;
1561
1562                            if let Ok(Ok(providers)) = providers_result {
1563                                for provider_info in providers {
1564                                    let models_result = wasm_extension
1565                                        .call({
1566                                            let provider_id = provider_info.id.clone();
1567                                            |ext, store| {
1568                                                async move {
1569                                                    ext.call_llm_provider_models(store, &provider_id)
1570                                                        .await
1571                                                }
1572                                                .boxed()
1573                                            }
1574                                        })
1575                                        .await;
1576
1577                                    let models: Vec<LlmModelInfo> = match models_result {
1578                                        Ok(Ok(Ok(models))) => models,
1579                                        Ok(Ok(Err(e))) => {
1580                                            log::error!(
1581                                                "Failed to get models for LLM provider {} in extension {}: {}",
1582                                                provider_info.id,
1583                                                extension.manifest.id,
1584                                                e
1585                                            );
1586                                            Vec::new()
1587                                        }
1588                                        Ok(Err(e)) => {
1589                                            log::error!(
1590                                                "Wasm error calling llm_provider_models for {} in extension {}: {:?}",
1591                                                provider_info.id,
1592                                                extension.manifest.id,
1593                                                e
1594                                            );
1595                                            Vec::new()
1596                                        }
1597                                        Err(e) => {
1598                                            log::error!(
1599                                                "Extension call failed for llm_provider_models {} in extension {}: {:?}",
1600                                                provider_info.id,
1601                                                extension.manifest.id,
1602                                                e
1603                                            );
1604                                            Vec::new()
1605                                        }
1606                                    };
1607
1608                                    // Query initial authentication state
1609                                    let is_authenticated = wasm_extension
1610                                        .call({
1611                                            let provider_id = provider_info.id.clone();
1612                                            |ext, store| {
1613                                                async move {
1614                                                    ext.call_llm_provider_is_authenticated(
1615                                                        store,
1616                                                        &provider_id,
1617                                                    )
1618                                                    .await
1619                                                }
1620                                                .boxed()
1621                                            }
1622                                        })
1623                                        .await
1624                                        .unwrap_or(Ok(false))
1625                                        .unwrap_or(false);
1626
1627                                    // Resolve icon path if provided
1628                                    let icon_path = provider_info.icon.as_ref().map(|icon| {
1629                                        let icon_file_path = extension_path.join(icon);
1630                                        // Canonicalize to resolve symlinks (dev extensions are symlinked)
1631                                        let absolute_icon_path = icon_file_path
1632                                            .canonicalize()
1633                                            .unwrap_or(icon_file_path)
1634                                            .to_string_lossy()
1635                                            .to_string();
1636                                        SharedString::from(absolute_icon_path)
1637                                    });
1638
1639                                    let provider_id_arc: Arc<str> =
1640                                        provider_info.id.as_str().into();
1641                                    let auth_config = extension
1642                                        .manifest
1643                                        .language_model_providers
1644                                        .get(&provider_id_arc)
1645                                        .and_then(|entry| entry.auth.clone());
1646
1647                                    llm_providers_with_models.push(LlmProviderWithModels {
1648                                        provider_info,
1649                                        models,
1650                                        is_authenticated,
1651                                        icon_path,
1652                                        auth_config,
1653                                    });
1654                                }
1655                            } else {
1656                                log::error!(
1657                                    "Failed to get LLM providers from extension {}: {:?}",
1658                                    extension.manifest.id,
1659                                    providers_result
1660                                );
1661                            }
1662                        }
1663
1664                        wasm_extensions.push((
1665                            extension.manifest.clone(),
1666                            wasm_extension,
1667                            llm_providers_with_models,
1668                        ))
1669                    }
1670                    Err(e) => {
1671                        log::error!(
1672                            "Failed to load extension: {}, {:#}",
1673                            extension.manifest.id,
1674                            e
1675                        );
1676                        this.update(cx, |_, cx| {
1677                            cx.emit(Event::ExtensionFailedToLoad(extension.manifest.id.clone()))
1678                        })
1679                        .ok();
1680                    }
1681                }
1682            }
1683
1684            this.update(cx, |this, cx| {
1685                this.reload_complete_senders.clear();
1686
1687                for (manifest, wasm_extension, llm_providers_with_models) in &wasm_extensions {
1688                    let extension = Arc::new(wasm_extension.clone());
1689
1690                    for (language_server_id, language_server_config) in &manifest.language_servers {
1691                        for language in language_server_config.languages() {
1692                            this.proxy.register_language_server(
1693                                extension.clone(),
1694                                language_server_id.clone(),
1695                                language.clone(),
1696                            );
1697                        }
1698                    }
1699
1700                    for (slash_command_name, slash_command) in &manifest.slash_commands {
1701                        this.proxy.register_slash_command(
1702                            extension.clone(),
1703                            extension::SlashCommand {
1704                                name: slash_command_name.to_string(),
1705                                description: slash_command.description.to_string(),
1706                                // We don't currently expose this as a configurable option, as it currently drives
1707                                // the `menu_text` on the `SlashCommand` trait, which is not used for slash commands
1708                                // defined in extensions, as they are not able to be added to the menu.
1709                                tooltip_text: String::new(),
1710                                requires_argument: slash_command.requires_argument,
1711                            },
1712                        );
1713                    }
1714
1715                    for id in manifest.context_servers.keys() {
1716                        this.proxy
1717                            .register_context_server(extension.clone(), id.clone(), cx);
1718                    }
1719
1720                    for (debug_adapter, meta) in &manifest.debug_adapters {
1721                        let mut path = root_dir.clone();
1722                        path.push(Path::new(manifest.id.as_ref()));
1723                        if let Some(schema_path) = &meta.schema_path {
1724                            path.push(schema_path);
1725                        } else {
1726                            path.push("debug_adapter_schemas");
1727                            path.push(Path::new(debug_adapter.as_ref()).with_extension("json"));
1728                        }
1729
1730                        this.proxy.register_debug_adapter(
1731                            extension.clone(),
1732                            debug_adapter.clone(),
1733                            &path,
1734                        );
1735                    }
1736
1737                    for debug_adapter in manifest.debug_locators.keys() {
1738                        this.proxy
1739                            .register_debug_locator(extension.clone(), debug_adapter.clone());
1740                    }
1741
1742                    // Register LLM providers
1743                    for llm_provider in llm_providers_with_models {
1744                        let provider_id: Arc<str> =
1745                            format!("{}:{}", manifest.id, llm_provider.provider_info.id).into();
1746                        let wasm_ext = extension.as_ref().clone();
1747                        let pinfo = llm_provider.provider_info.clone();
1748                        let mods = llm_provider.models.clone();
1749                        let auth = llm_provider.is_authenticated;
1750                        let icon = llm_provider.icon_path.clone();
1751                        let auth_config = llm_provider.auth_config.clone();
1752
1753                        this.proxy.register_language_model_provider(
1754                            provider_id.clone(),
1755                            Box::new(move |cx: &mut App| {
1756                                let provider = Arc::new(ExtensionLanguageModelProvider::new(
1757                                    wasm_ext, pinfo, mods, auth, icon, auth_config, cx,
1758                                ));
1759                                language_model::LanguageModelRegistry::global(cx).update(
1760                                    cx,
1761                                    |registry, cx| {
1762                                        registry.register_provider(provider, cx);
1763                                    },
1764                                );
1765                            }),
1766                            cx,
1767                        );
1768                    }
1769                }
1770
1771                let wasm_extensions_without_llm: Vec<_> = wasm_extensions
1772                    .into_iter()
1773                    .map(|(manifest, ext, _)| (manifest, ext))
1774                    .collect();
1775                this.wasm_extensions.extend(wasm_extensions_without_llm);
1776                this.proxy.set_extensions_loaded();
1777                this.proxy.reload_current_theme(cx);
1778                this.proxy.reload_current_icon_theme(cx);
1779
1780                if let Some(events) = ExtensionEvents::try_global(cx) {
1781                    events.update(cx, |this, cx| {
1782                        this.emit(extension::Event::ExtensionsInstalledChanged, cx)
1783                    });
1784                }
1785            })
1786            .ok();
1787        })
1788    }
1789
1790    fn rebuild_extension_index(&self, cx: &mut Context<Self>) -> Task<ExtensionIndex> {
1791        let fs = self.fs.clone();
1792        let work_dir = self.wasm_host.work_dir.clone();
1793        let extensions_dir = self.installed_dir.clone();
1794        let index_path = self.index_path.clone();
1795        let proxy = self.proxy.clone();
1796        cx.background_spawn(async move {
1797            let mut index = ExtensionIndex::default();
1798
1799            fs.create_dir(&work_dir).await.log_err();
1800            fs.create_dir(&extensions_dir).await.log_err();
1801
1802            let extension_paths = fs.read_dir(&extensions_dir).await;
1803            if let Ok(mut extension_paths) = extension_paths {
1804                while let Some(extension_dir) = extension_paths.next().await {
1805                    let Ok(extension_dir) = extension_dir else {
1806                        continue;
1807                    };
1808
1809                    if extension_dir
1810                        .file_name()
1811                        .is_some_and(|file_name| file_name == ".DS_Store")
1812                    {
1813                        continue;
1814                    }
1815
1816                    Self::add_extension_to_index(
1817                        fs.clone(),
1818                        extension_dir,
1819                        &mut index,
1820                        proxy.clone(),
1821                    )
1822                    .await
1823                    .log_err();
1824                }
1825            }
1826
1827            if let Ok(index_json) = serde_json::to_string_pretty(&index) {
1828                fs.save(&index_path, &index_json.as_str().into(), Default::default())
1829                    .await
1830                    .context("failed to save extension index")
1831                    .log_err();
1832            }
1833
1834            index
1835        })
1836    }
1837
1838    async fn add_extension_to_index(
1839        fs: Arc<dyn Fs>,
1840        extension_dir: PathBuf,
1841        index: &mut ExtensionIndex,
1842        proxy: Arc<ExtensionHostProxy>,
1843    ) -> Result<()> {
1844        let mut extension_manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
1845        let extension_id = extension_manifest.id.clone();
1846
1847        if SUPPRESSED_EXTENSIONS.contains(&extension_id.as_ref()) {
1848            return Ok(());
1849        }
1850
1851        // TODO: distinguish dev extensions more explicitly, by the absence
1852        // of a checksum file that we'll create when downloading normal extensions.
1853        let is_dev = fs
1854            .metadata(&extension_dir)
1855            .await?
1856            .context("directory does not exist")?
1857            .is_symlink;
1858
1859        if let Ok(mut language_paths) = fs.read_dir(&extension_dir.join("languages")).await {
1860            while let Some(language_path) = language_paths.next().await {
1861                let language_path = language_path?;
1862                let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else {
1863                    continue;
1864                };
1865                let Ok(Some(fs_metadata)) = fs.metadata(&language_path).await else {
1866                    continue;
1867                };
1868                if !fs_metadata.is_dir {
1869                    continue;
1870                }
1871                let config = fs.load(&language_path.join("config.toml")).await?;
1872                let config = ::toml::from_str::<LanguageConfig>(&config)?;
1873
1874                let relative_path = relative_path.to_path_buf();
1875                if !extension_manifest.languages.contains(&relative_path) {
1876                    extension_manifest.languages.push(relative_path.clone());
1877                }
1878
1879                index.languages.insert(
1880                    config.name.clone(),
1881                    ExtensionIndexLanguageEntry {
1882                        extension: extension_id.clone(),
1883                        path: relative_path,
1884                        matcher: config.matcher,
1885                        hidden: config.hidden,
1886                        grammar: config.grammar,
1887                    },
1888                );
1889            }
1890        }
1891
1892        if let Ok(mut theme_paths) = fs.read_dir(&extension_dir.join("themes")).await {
1893            while let Some(theme_path) = theme_paths.next().await {
1894                let theme_path = theme_path?;
1895                let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) else {
1896                    continue;
1897                };
1898
1899                let Some(theme_families) = proxy
1900                    .list_theme_names(theme_path.clone(), fs.clone())
1901                    .await
1902                    .log_err()
1903                else {
1904                    continue;
1905                };
1906
1907                let relative_path = relative_path.to_path_buf();
1908                if !extension_manifest.themes.contains(&relative_path) {
1909                    extension_manifest.themes.push(relative_path.clone());
1910                }
1911
1912                for theme_name in theme_families {
1913                    index.themes.insert(
1914                        theme_name.into(),
1915                        ExtensionIndexThemeEntry {
1916                            extension: extension_id.clone(),
1917                            path: relative_path.clone(),
1918                        },
1919                    );
1920                }
1921            }
1922        }
1923
1924        if let Ok(mut icon_theme_paths) = fs.read_dir(&extension_dir.join("icon_themes")).await {
1925            while let Some(icon_theme_path) = icon_theme_paths.next().await {
1926                let icon_theme_path = icon_theme_path?;
1927                let Ok(relative_path) = icon_theme_path.strip_prefix(&extension_dir) else {
1928                    continue;
1929                };
1930
1931                let Some(icon_theme_families) = proxy
1932                    .list_icon_theme_names(icon_theme_path.clone(), fs.clone())
1933                    .await
1934                    .log_err()
1935                else {
1936                    continue;
1937                };
1938
1939                let relative_path = relative_path.to_path_buf();
1940                if !extension_manifest.icon_themes.contains(&relative_path) {
1941                    extension_manifest.icon_themes.push(relative_path.clone());
1942                }
1943
1944                for icon_theme_name in icon_theme_families {
1945                    index.icon_themes.insert(
1946                        icon_theme_name.into(),
1947                        ExtensionIndexIconThemeEntry {
1948                            extension: extension_id.clone(),
1949                            path: relative_path.clone(),
1950                        },
1951                    );
1952                }
1953            }
1954        }
1955
1956        let extension_wasm_path = extension_dir.join("extension.wasm");
1957        if fs.is_file(&extension_wasm_path).await {
1958            extension_manifest
1959                .lib
1960                .kind
1961                .get_or_insert(ExtensionLibraryKind::Rust);
1962        }
1963
1964        index.extensions.insert(
1965            extension_id.clone(),
1966            ExtensionIndexEntry {
1967                dev: is_dev,
1968                manifest: Arc::new(extension_manifest),
1969            },
1970        );
1971
1972        Ok(())
1973    }
1974
1975    fn prepare_remote_extension(
1976        &mut self,
1977        extension_id: Arc<str>,
1978        is_dev: bool,
1979        tmp_dir: PathBuf,
1980        cx: &mut Context<Self>,
1981    ) -> Task<Result<()>> {
1982        let src_dir = self.extensions_dir().join(extension_id.as_ref());
1983        let Some(loaded_extension) = self.extension_index.extensions.get(&extension_id).cloned()
1984        else {
1985            return Task::ready(Err(anyhow!("extension no longer installed")));
1986        };
1987        let fs = self.fs.clone();
1988        cx.background_spawn(async move {
1989            const EXTENSION_TOML: &str = "extension.toml";
1990            const EXTENSION_WASM: &str = "extension.wasm";
1991            const CONFIG_TOML: &str = "config.toml";
1992
1993            if is_dev {
1994                let manifest_toml = toml::to_string(&loaded_extension.manifest)?;
1995                fs.save(
1996                    &tmp_dir.join(EXTENSION_TOML),
1997                    &Rope::from(manifest_toml),
1998                    language::LineEnding::Unix,
1999                )
2000                .await?;
2001            } else {
2002                fs.copy_file(
2003                    &src_dir.join(EXTENSION_TOML),
2004                    &tmp_dir.join(EXTENSION_TOML),
2005                    fs::CopyOptions::default(),
2006                )
2007                .await?
2008            }
2009
2010            if fs.is_file(&src_dir.join(EXTENSION_WASM)).await {
2011                fs.copy_file(
2012                    &src_dir.join(EXTENSION_WASM),
2013                    &tmp_dir.join(EXTENSION_WASM),
2014                    fs::CopyOptions::default(),
2015                )
2016                .await?
2017            }
2018
2019            for language_path in loaded_extension.manifest.languages.iter() {
2020                if fs
2021                    .is_file(&src_dir.join(language_path).join(CONFIG_TOML))
2022                    .await
2023                {
2024                    fs.create_dir(&tmp_dir.join(language_path)).await?;
2025                    fs.copy_file(
2026                        &src_dir.join(language_path).join(CONFIG_TOML),
2027                        &tmp_dir.join(language_path).join(CONFIG_TOML),
2028                        fs::CopyOptions::default(),
2029                    )
2030                    .await?
2031                }
2032            }
2033
2034            for (adapter_name, meta) in loaded_extension.manifest.debug_adapters.iter() {
2035                let schema_path = &extension::build_debug_adapter_schema_path(adapter_name, meta);
2036
2037                if fs.is_file(&src_dir.join(schema_path)).await {
2038                    if let Some(parent) = schema_path.parent() {
2039                        fs.create_dir(&tmp_dir.join(parent)).await?
2040                    }
2041                    fs.copy_file(
2042                        &src_dir.join(schema_path),
2043                        &tmp_dir.join(schema_path),
2044                        fs::CopyOptions::default(),
2045                    )
2046                    .await?
2047                }
2048            }
2049
2050            Ok(())
2051        })
2052    }
2053
2054    async fn sync_extensions_to_remotes(
2055        this: &WeakEntity<Self>,
2056        client: WeakEntity<RemoteClient>,
2057        cx: &mut AsyncApp,
2058    ) -> Result<()> {
2059        let extensions = this.update(cx, |this, _cx| {
2060            this.extension_index
2061                .extensions
2062                .iter()
2063                .filter_map(|(id, entry)| {
2064                    if !entry.manifest.allow_remote_load() {
2065                        return None;
2066                    }
2067                    Some(proto::Extension {
2068                        id: id.to_string(),
2069                        version: entry.manifest.version.to_string(),
2070                        dev: entry.dev,
2071                    })
2072                })
2073                .collect()
2074        })?;
2075
2076        let response = client
2077            .update(cx, |client, _cx| {
2078                client
2079                    .proto_client()
2080                    .request(proto::SyncExtensions { extensions })
2081            })?
2082            .await?;
2083        let path_style = client.read_with(cx, |client, _| client.path_style())?;
2084
2085        for missing_extension in response.missing_extensions.into_iter() {
2086            let tmp_dir = tempfile::tempdir()?;
2087            this.update(cx, |this, cx| {
2088                this.prepare_remote_extension(
2089                    missing_extension.id.clone().into(),
2090                    missing_extension.dev,
2091                    tmp_dir.path().to_owned(),
2092                    cx,
2093                )
2094            })?
2095            .await?;
2096            let dest_dir = RemotePathBuf::new(
2097                path_style
2098                    .join(&response.tmp_dir, &missing_extension.id)
2099                    .with_context(|| {
2100                        format!(
2101                            "failed to construct destination path: {:?}, {:?}",
2102                            response.tmp_dir, missing_extension.id,
2103                        )
2104                    })?,
2105                path_style,
2106            );
2107
2108            client
2109                .update(cx, |client, cx| {
2110                    client.upload_directory(tmp_dir.path().to_owned(), dest_dir.clone(), cx)
2111                })?
2112                .await?;
2113
2114            let result = client
2115                .update(cx, |client, _cx| {
2116                    client.proto_client().request(proto::InstallExtension {
2117                        tmp_dir: dest_dir.to_proto(),
2118                        extension: Some(missing_extension.clone()),
2119                    })
2120                })?
2121                .await;
2122
2123            if let Err(e) = result {
2124                log::error!(
2125                    "Failed to install extension {}: {}",
2126                    missing_extension.id,
2127                    e
2128                );
2129            }
2130        }
2131
2132        anyhow::Ok(())
2133    }
2134
2135    pub async fn update_remote_clients(this: &WeakEntity<Self>, cx: &mut AsyncApp) -> Result<()> {
2136        let clients = this.update(cx, |this, _cx| {
2137            this.remote_clients.retain(|v| v.upgrade().is_some());
2138            this.remote_clients.clone()
2139        })?;
2140
2141        for client in clients {
2142            Self::sync_extensions_to_remotes(this, client, cx)
2143                .await
2144                .log_err();
2145        }
2146
2147        anyhow::Ok(())
2148    }
2149
2150    pub fn register_remote_client(
2151        &mut self,
2152        client: Entity<RemoteClient>,
2153        _cx: &mut Context<Self>,
2154    ) {
2155        self.remote_clients.push(client.downgrade());
2156        self.ssh_registered_tx.unbounded_send(()).ok();
2157    }
2158}
2159
2160fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
2161    let mut result = LanguageQueries::default();
2162    if let Some(entries) = std::fs::read_dir(root_path).log_err() {
2163        for entry in entries {
2164            let Some(entry) = entry.log_err() else {
2165                continue;
2166            };
2167            let path = entry.path();
2168            if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
2169                if !remainder.ends_with(".scm") {
2170                    continue;
2171                }
2172                for (name, query) in QUERY_FILENAME_PREFIXES {
2173                    if remainder.starts_with(name) {
2174                        if let Some(contents) = std::fs::read_to_string(&path).log_err() {
2175                            match query(&mut result) {
2176                                None => *query(&mut result) = Some(contents.into()),
2177                                Some(r) => r.to_mut().push_str(contents.as_ref()),
2178                            }
2179                        }
2180                        break;
2181                    }
2182                }
2183            }
2184        }
2185    }
2186    result
2187}