extension_store.rs

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