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 extension_dir = self.installed_dir.join(extension_id.as_ref());
 428        let http_client = self.http_client.clone();
 429        let fs = self.fs.clone();
 430
 431        match self.outstanding_operations.entry(extension_id.clone()) {
 432            hash_map::Entry::Occupied(_) => return,
 433            hash_map::Entry::Vacant(e) => e.insert(operation),
 434        };
 435
 436        cx.spawn(move |this, mut cx| async move {
 437            let _finish = util::defer({
 438                let this = this.clone();
 439                let mut cx = cx.clone();
 440                let extension_id = extension_id.clone();
 441                move || {
 442                    this.update(&mut cx, |this, cx| {
 443                        this.outstanding_operations.remove(extension_id.as_ref());
 444                        cx.notify();
 445                    })
 446                    .ok();
 447                }
 448            });
 449
 450            let mut response = http_client
 451                .get(&url, Default::default(), true)
 452                .await
 453                .map_err(|err| anyhow!("error downloading extension: {}", err))?;
 454
 455            fs.remove_dir(
 456                &extension_dir,
 457                RemoveOptions {
 458                    recursive: true,
 459                    ignore_if_not_exists: true,
 460                },
 461            )
 462            .await?;
 463
 464            let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
 465            let archive = Archive::new(decompressed_bytes);
 466            archive.unpack(extension_dir).await?;
 467            this.update(&mut cx, |this, cx| {
 468                this.reload(Some(extension_id.clone()), cx)
 469            })?
 470            .await;
 471
 472            match operation {
 473                ExtensionOperation::Install => {
 474                    this.update(&mut cx, |_, cx| {
 475                        cx.emit(Event::ExtensionInstalled(extension_id));
 476                    })
 477                    .ok();
 478                }
 479                _ => {}
 480            }
 481
 482            anyhow::Ok(())
 483        })
 484        .detach_and_log_err(cx);
 485    }
 486
 487    pub fn install_latest_extension(
 488        &mut self,
 489        extension_id: Arc<str>,
 490        cx: &mut ModelContext<Self>,
 491    ) {
 492        log::info!("installing extension {extension_id} latest version");
 493
 494        let url = self
 495            .http_client
 496            .build_zed_api_url(&format!("/extensions/{extension_id}/download"));
 497
 498        self.install_or_upgrade_extension_at_endpoint(
 499            extension_id,
 500            url,
 501            ExtensionOperation::Install,
 502            cx,
 503        );
 504    }
 505
 506    pub fn upgrade_extension(
 507        &mut self,
 508        extension_id: Arc<str>,
 509        version: Arc<str>,
 510        cx: &mut ModelContext<Self>,
 511    ) {
 512        self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Upgrade, cx)
 513    }
 514
 515    fn install_or_upgrade_extension(
 516        &mut self,
 517        extension_id: Arc<str>,
 518        version: Arc<str>,
 519        operation: ExtensionOperation,
 520        cx: &mut ModelContext<Self>,
 521    ) {
 522        log::info!("installing extension {extension_id} {version}");
 523        let url = self
 524            .http_client
 525            .build_zed_api_url(&format!("/extensions/{extension_id}/{version}/download"));
 526
 527        self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx);
 528    }
 529
 530    pub fn uninstall_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
 531        let extension_dir = self.installed_dir.join(extension_id.as_ref());
 532        let fs = self.fs.clone();
 533
 534        match self.outstanding_operations.entry(extension_id.clone()) {
 535            hash_map::Entry::Occupied(_) => return,
 536            hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
 537        };
 538
 539        cx.spawn(move |this, mut cx| async move {
 540            let _finish = util::defer({
 541                let this = this.clone();
 542                let mut cx = cx.clone();
 543                let extension_id = extension_id.clone();
 544                move || {
 545                    this.update(&mut cx, |this, cx| {
 546                        this.outstanding_operations.remove(extension_id.as_ref());
 547                        cx.notify();
 548                    })
 549                    .ok();
 550                }
 551            });
 552
 553            fs.remove_dir(
 554                &extension_dir,
 555                RemoveOptions {
 556                    recursive: true,
 557                    ignore_if_not_exists: true,
 558                },
 559            )
 560            .await?;
 561
 562            this.update(&mut cx, |this, cx| this.reload(None, cx))?
 563                .await;
 564            anyhow::Ok(())
 565        })
 566        .detach_and_log_err(cx)
 567    }
 568
 569    pub fn install_dev_extension(
 570        &mut self,
 571        extension_source_path: PathBuf,
 572        cx: &mut ModelContext<Self>,
 573    ) -> Task<Result<()>> {
 574        let extensions_dir = self.extensions_dir();
 575        let fs = self.fs.clone();
 576        let builder = self.builder.clone();
 577
 578        cx.spawn(move |this, mut cx| async move {
 579            let extension_manifest =
 580                Self::load_extension_manifest(fs.clone(), &extension_source_path).await?;
 581            let extension_id = extension_manifest.id.clone();
 582
 583            if !this.update(&mut cx, |this, cx| {
 584                match this.outstanding_operations.entry(extension_id.clone()) {
 585                    hash_map::Entry::Occupied(_) => return false,
 586                    hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
 587                };
 588                cx.notify();
 589                true
 590            })? {
 591                return Ok(());
 592            }
 593
 594            let _finish = util::defer({
 595                let this = this.clone();
 596                let mut cx = cx.clone();
 597                let extension_id = extension_id.clone();
 598                move || {
 599                    this.update(&mut cx, |this, cx| {
 600                        this.outstanding_operations.remove(extension_id.as_ref());
 601                        cx.notify();
 602                    })
 603                    .ok();
 604                }
 605            });
 606
 607            cx.background_executor()
 608                .spawn({
 609                    let extension_source_path = extension_source_path.clone();
 610                    async move {
 611                        builder
 612                            .compile_extension(
 613                                &extension_source_path,
 614                                &extension_manifest,
 615                                CompileExtensionOptions { release: false },
 616                            )
 617                            .await
 618                    }
 619                })
 620                .await?;
 621
 622            let output_path = &extensions_dir.join(extension_id.as_ref());
 623            if let Some(metadata) = fs.metadata(&output_path).await? {
 624                if metadata.is_symlink {
 625                    fs.remove_file(
 626                        &output_path,
 627                        RemoveOptions {
 628                            recursive: false,
 629                            ignore_if_not_exists: true,
 630                        },
 631                    )
 632                    .await?;
 633                } else {
 634                    bail!("extension {extension_id} is already installed");
 635                }
 636            }
 637
 638            fs.create_symlink(output_path, extension_source_path)
 639                .await?;
 640
 641            this.update(&mut cx, |this, cx| this.reload(None, cx))?
 642                .await;
 643            Ok(())
 644        })
 645    }
 646
 647    pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
 648        let path = self.installed_dir.join(extension_id.as_ref());
 649        let builder = self.builder.clone();
 650        let fs = self.fs.clone();
 651
 652        match self.outstanding_operations.entry(extension_id.clone()) {
 653            hash_map::Entry::Occupied(_) => return,
 654            hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Upgrade),
 655        };
 656
 657        cx.notify();
 658        let compile = cx.background_executor().spawn(async move {
 659            let manifest = Self::load_extension_manifest(fs, &path).await?;
 660            builder
 661                .compile_extension(&path, &manifest, CompileExtensionOptions { release: true })
 662                .await
 663        });
 664
 665        cx.spawn(|this, mut cx| async move {
 666            let result = compile.await;
 667
 668            this.update(&mut cx, |this, cx| {
 669                this.outstanding_operations.remove(&extension_id);
 670                cx.notify();
 671            })?;
 672
 673            if result.is_ok() {
 674                this.update(&mut cx, |this, cx| this.reload(Some(extension_id), cx))?
 675                    .await;
 676            }
 677
 678            result
 679        })
 680        .detach_and_log_err(cx)
 681    }
 682
 683    /// Updates the set of installed extensions.
 684    ///
 685    /// First, this unloads any themes, languages, or grammars that are
 686    /// no longer in the manifest, or whose files have changed on disk.
 687    /// Then it loads any themes, languages, or grammars that are newly
 688    /// added to the manifest, or whose files have changed on disk.
 689    fn extensions_updated(
 690        &mut self,
 691        new_index: ExtensionIndex,
 692        cx: &mut ModelContext<Self>,
 693    ) -> Task<()> {
 694        let old_index = &self.extension_index;
 695
 696        // Determine which extensions need to be loaded and unloaded, based
 697        // on the changes to the manifest and the extensions that we know have been
 698        // modified.
 699        let mut extensions_to_unload = Vec::default();
 700        let mut extensions_to_load = Vec::default();
 701        {
 702            let mut old_keys = old_index.extensions.iter().peekable();
 703            let mut new_keys = new_index.extensions.iter().peekable();
 704            loop {
 705                match (old_keys.peek(), new_keys.peek()) {
 706                    (None, None) => break,
 707                    (None, Some(_)) => {
 708                        extensions_to_load.push(new_keys.next().unwrap().0.clone());
 709                    }
 710                    (Some(_), None) => {
 711                        extensions_to_unload.push(old_keys.next().unwrap().0.clone());
 712                    }
 713                    (Some((old_key, _)), Some((new_key, _))) => match old_key.cmp(&new_key) {
 714                        Ordering::Equal => {
 715                            let (old_key, old_value) = old_keys.next().unwrap();
 716                            let (new_key, new_value) = new_keys.next().unwrap();
 717                            if old_value != new_value || self.modified_extensions.contains(old_key)
 718                            {
 719                                extensions_to_unload.push(old_key.clone());
 720                                extensions_to_load.push(new_key.clone());
 721                            }
 722                        }
 723                        Ordering::Less => {
 724                            extensions_to_unload.push(old_keys.next().unwrap().0.clone());
 725                        }
 726                        Ordering::Greater => {
 727                            extensions_to_load.push(new_keys.next().unwrap().0.clone());
 728                        }
 729                    },
 730                }
 731            }
 732            self.modified_extensions.clear();
 733        }
 734
 735        if extensions_to_load.is_empty() && extensions_to_unload.is_empty() {
 736            return Task::ready(());
 737        }
 738
 739        let reload_count = extensions_to_unload
 740            .iter()
 741            .filter(|id| extensions_to_load.contains(id))
 742            .count();
 743
 744        log::info!(
 745            "extensions updated. loading {}, reloading {}, unloading {}",
 746            extensions_to_load.len() - reload_count,
 747            reload_count,
 748            extensions_to_unload.len() - reload_count
 749        );
 750
 751        let themes_to_remove = old_index
 752            .themes
 753            .iter()
 754            .filter_map(|(name, entry)| {
 755                if extensions_to_unload.contains(&entry.extension) {
 756                    Some(name.clone().into())
 757                } else {
 758                    None
 759                }
 760            })
 761            .collect::<Vec<_>>();
 762        let languages_to_remove = old_index
 763            .languages
 764            .iter()
 765            .filter_map(|(name, entry)| {
 766                if extensions_to_unload.contains(&entry.extension) {
 767                    Some(name.clone())
 768                } else {
 769                    None
 770                }
 771            })
 772            .collect::<Vec<_>>();
 773        let mut grammars_to_remove = Vec::new();
 774        for extension_id in &extensions_to_unload {
 775            let Some(extension) = old_index.extensions.get(extension_id) else {
 776                continue;
 777            };
 778            grammars_to_remove.extend(extension.manifest.grammars.keys().cloned());
 779            for (language_server_name, config) in extension.manifest.language_servers.iter() {
 780                self.language_registry
 781                    .remove_lsp_adapter(config.language.as_ref(), language_server_name);
 782            }
 783        }
 784
 785        self.wasm_extensions
 786            .retain(|(extension, _)| !extensions_to_unload.contains(&extension.id));
 787        self.theme_registry.remove_user_themes(&themes_to_remove);
 788        self.language_registry
 789            .remove_languages(&languages_to_remove, &grammars_to_remove);
 790
 791        let languages_to_add = new_index
 792            .languages
 793            .iter()
 794            .filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
 795            .collect::<Vec<_>>();
 796        let mut grammars_to_add = Vec::new();
 797        let mut themes_to_add = Vec::new();
 798        for extension_id in &extensions_to_load {
 799            let Some(extension) = new_index.extensions.get(extension_id) else {
 800                continue;
 801            };
 802
 803            grammars_to_add.extend(extension.manifest.grammars.keys().map(|grammar_name| {
 804                let mut grammar_path = self.installed_dir.clone();
 805                grammar_path.extend([extension_id.as_ref(), "grammars"]);
 806                grammar_path.push(grammar_name.as_ref());
 807                grammar_path.set_extension("wasm");
 808                (grammar_name.clone(), grammar_path)
 809            }));
 810            themes_to_add.extend(extension.manifest.themes.iter().map(|theme_path| {
 811                let mut path = self.installed_dir.clone();
 812                path.extend([Path::new(extension_id.as_ref()), theme_path.as_path()]);
 813                path
 814            }));
 815        }
 816
 817        self.language_registry
 818            .register_wasm_grammars(grammars_to_add);
 819
 820        for (language_name, language) in languages_to_add {
 821            let mut language_path = self.installed_dir.clone();
 822            language_path.extend([
 823                Path::new(language.extension.as_ref()),
 824                language.path.as_path(),
 825            ]);
 826            self.language_registry.register_language(
 827                language_name.clone(),
 828                language.grammar.clone(),
 829                language.matcher.clone(),
 830                None,
 831                move || {
 832                    let config = std::fs::read_to_string(language_path.join("config.toml"))?;
 833                    let config: LanguageConfig = ::toml::from_str(&config)?;
 834                    let queries = load_plugin_queries(&language_path);
 835                    Ok((config, queries))
 836                },
 837            );
 838        }
 839
 840        let fs = self.fs.clone();
 841        let wasm_host = self.wasm_host.clone();
 842        let root_dir = self.installed_dir.clone();
 843        let theme_registry = self.theme_registry.clone();
 844        let extension_entries = extensions_to_load
 845            .iter()
 846            .filter_map(|name| new_index.extensions.get(name).cloned())
 847            .collect::<Vec<_>>();
 848
 849        self.extension_index = new_index;
 850        cx.notify();
 851        cx.emit(Event::ExtensionsUpdated);
 852
 853        cx.spawn(|this, mut cx| async move {
 854            cx.background_executor()
 855                .spawn({
 856                    let fs = fs.clone();
 857                    async move {
 858                        for theme_path in &themes_to_add {
 859                            theme_registry
 860                                .load_user_theme(&theme_path, fs.clone())
 861                                .await
 862                                .log_err();
 863                        }
 864                    }
 865                })
 866                .await;
 867
 868            let mut wasm_extensions = Vec::new();
 869            for extension in extension_entries {
 870                if extension.manifest.lib.kind.is_none() {
 871                    continue;
 872                };
 873
 874                let mut path = root_dir.clone();
 875                path.extend([extension.manifest.id.as_ref(), "extension.wasm"]);
 876                let Some(mut wasm_file) = fs
 877                    .open_sync(&path)
 878                    .await
 879                    .context("failed to open wasm file")
 880                    .log_err()
 881                else {
 882                    continue;
 883                };
 884
 885                let mut wasm_bytes = Vec::new();
 886                if wasm_file
 887                    .read_to_end(&mut wasm_bytes)
 888                    .context("failed to read wasm")
 889                    .log_err()
 890                    .is_none()
 891                {
 892                    continue;
 893                }
 894
 895                let Some(wasm_extension) = wasm_host
 896                    .load_extension(
 897                        wasm_bytes,
 898                        extension.manifest.clone(),
 899                        cx.background_executor().clone(),
 900                    )
 901                    .await
 902                    .context("failed to load wasm extension")
 903                    .log_err()
 904                else {
 905                    continue;
 906                };
 907
 908                wasm_extensions.push((extension.manifest.clone(), wasm_extension));
 909            }
 910
 911            this.update(&mut cx, |this, cx| {
 912                this.reload_complete_senders.clear();
 913
 914                for (manifest, wasm_extension) in &wasm_extensions {
 915                    for (language_server_name, language_server_config) in &manifest.language_servers
 916                    {
 917                        this.language_registry.register_lsp_adapter(
 918                            language_server_config.language.clone(),
 919                            Arc::new(ExtensionLspAdapter {
 920                                extension: wasm_extension.clone(),
 921                                host: this.wasm_host.clone(),
 922                                config: wit::LanguageServerConfig {
 923                                    name: language_server_name.0.to_string(),
 924                                    language_name: language_server_config.language.to_string(),
 925                                },
 926                            }),
 927                        );
 928                    }
 929                }
 930                this.wasm_extensions.extend(wasm_extensions);
 931                ThemeSettings::reload_current_theme(cx)
 932            })
 933            .ok();
 934        })
 935    }
 936
 937    fn rebuild_extension_index(&self, cx: &mut ModelContext<Self>) -> Task<ExtensionIndex> {
 938        let fs = self.fs.clone();
 939        let work_dir = self.wasm_host.work_dir.clone();
 940        let extensions_dir = self.installed_dir.clone();
 941        let index_path = self.index_path.clone();
 942        cx.background_executor().spawn(async move {
 943            let start_time = Instant::now();
 944            let mut index = ExtensionIndex::default();
 945
 946            fs.create_dir(&work_dir).await.log_err();
 947            fs.create_dir(&extensions_dir).await.log_err();
 948
 949            let extension_paths = fs.read_dir(&extensions_dir).await;
 950            if let Ok(mut extension_paths) = extension_paths {
 951                while let Some(extension_dir) = extension_paths.next().await {
 952                    let Ok(extension_dir) = extension_dir else {
 953                        continue;
 954                    };
 955                    Self::add_extension_to_index(fs.clone(), extension_dir, &mut index)
 956                        .await
 957                        .log_err();
 958                }
 959            }
 960
 961            if let Ok(index_json) = serde_json::to_string_pretty(&index) {
 962                fs.save(&index_path, &index_json.as_str().into(), Default::default())
 963                    .await
 964                    .context("failed to save extension index")
 965                    .log_err();
 966            }
 967
 968            log::info!("rebuilt extension index in {:?}", start_time.elapsed());
 969            index
 970        })
 971    }
 972
 973    async fn add_extension_to_index(
 974        fs: Arc<dyn Fs>,
 975        extension_dir: PathBuf,
 976        index: &mut ExtensionIndex,
 977    ) -> Result<()> {
 978        let mut extension_manifest =
 979            Self::load_extension_manifest(fs.clone(), &extension_dir).await?;
 980        let extension_id = extension_manifest.id.clone();
 981
 982        // TODO: distinguish dev extensions more explicitly, by the absence
 983        // of a checksum file that we'll create when downloading normal extensions.
 984        let is_dev = fs
 985            .metadata(&extension_dir)
 986            .await?
 987            .ok_or_else(|| anyhow!("directory does not exist"))?
 988            .is_symlink;
 989
 990        if let Ok(mut language_paths) = fs.read_dir(&extension_dir.join("languages")).await {
 991            while let Some(language_path) = language_paths.next().await {
 992                let language_path = language_path?;
 993                let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else {
 994                    continue;
 995                };
 996                let Ok(Some(fs_metadata)) = fs.metadata(&language_path).await else {
 997                    continue;
 998                };
 999                if !fs_metadata.is_dir {
1000                    continue;
1001                }
1002                let config = fs.load(&language_path.join("config.toml")).await?;
1003                let config = ::toml::from_str::<LanguageConfig>(&config)?;
1004
1005                let relative_path = relative_path.to_path_buf();
1006                if !extension_manifest.languages.contains(&relative_path) {
1007                    extension_manifest.languages.push(relative_path.clone());
1008                }
1009
1010                index.languages.insert(
1011                    config.name.clone(),
1012                    ExtensionIndexLanguageEntry {
1013                        extension: extension_id.clone(),
1014                        path: relative_path,
1015                        matcher: config.matcher,
1016                        grammar: config.grammar,
1017                    },
1018                );
1019            }
1020        }
1021
1022        if let Ok(mut theme_paths) = fs.read_dir(&extension_dir.join("themes")).await {
1023            while let Some(theme_path) = theme_paths.next().await {
1024                let theme_path = theme_path?;
1025                let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) else {
1026                    continue;
1027                };
1028
1029                let Some(theme_family) = ThemeRegistry::read_user_theme(&theme_path, fs.clone())
1030                    .await
1031                    .log_err()
1032                else {
1033                    continue;
1034                };
1035
1036                let relative_path = relative_path.to_path_buf();
1037                if !extension_manifest.themes.contains(&relative_path) {
1038                    extension_manifest.themes.push(relative_path.clone());
1039                }
1040
1041                for theme in theme_family.themes {
1042                    index.themes.insert(
1043                        theme.name.into(),
1044                        ExtensionIndexThemeEntry {
1045                            extension: extension_id.clone(),
1046                            path: relative_path.clone(),
1047                        },
1048                    );
1049                }
1050            }
1051        }
1052
1053        let extension_wasm_path = extension_dir.join("extension.wasm");
1054        if fs.is_file(&extension_wasm_path).await {
1055            extension_manifest
1056                .lib
1057                .kind
1058                .get_or_insert(ExtensionLibraryKind::Rust);
1059        }
1060
1061        index.extensions.insert(
1062            extension_id.clone(),
1063            ExtensionIndexEntry {
1064                dev: is_dev,
1065                manifest: Arc::new(extension_manifest),
1066            },
1067        );
1068
1069        Ok(())
1070    }
1071
1072    pub async fn load_extension_manifest(
1073        fs: Arc<dyn Fs>,
1074        extension_dir: &Path,
1075    ) -> Result<ExtensionManifest> {
1076        let extension_name = extension_dir
1077            .file_name()
1078            .and_then(OsStr::to_str)
1079            .ok_or_else(|| anyhow!("invalid extension name"))?;
1080
1081        let mut extension_manifest_path = extension_dir.join("extension.json");
1082        if fs.is_file(&extension_manifest_path).await {
1083            let manifest_content = fs
1084                .load(&extension_manifest_path)
1085                .await
1086                .with_context(|| format!("failed to load {extension_name} extension.json"))?;
1087            let manifest_json = serde_json::from_str::<OldExtensionManifest>(&manifest_content)
1088                .with_context(|| {
1089                    format!("invalid extension.json for extension {extension_name}")
1090                })?;
1091
1092            Ok(manifest_from_old_manifest(manifest_json, extension_name))
1093        } else {
1094            extension_manifest_path.set_extension("toml");
1095            let manifest_content = fs
1096                .load(&extension_manifest_path)
1097                .await
1098                .with_context(|| format!("failed to load {extension_name} extension.toml"))?;
1099            toml::from_str(&manifest_content)
1100                .with_context(|| format!("invalid extension.json for extension {extension_name}"))
1101        }
1102    }
1103}
1104
1105fn manifest_from_old_manifest(
1106    manifest_json: OldExtensionManifest,
1107    extension_id: &str,
1108) -> ExtensionManifest {
1109    ExtensionManifest {
1110        id: extension_id.into(),
1111        name: manifest_json.name,
1112        version: manifest_json.version,
1113        description: manifest_json.description,
1114        repository: manifest_json.repository,
1115        authors: manifest_json.authors,
1116        lib: Default::default(),
1117        themes: {
1118            let mut themes = manifest_json.themes.into_values().collect::<Vec<_>>();
1119            themes.sort();
1120            themes.dedup();
1121            themes
1122        },
1123        languages: {
1124            let mut languages = manifest_json.languages.into_values().collect::<Vec<_>>();
1125            languages.sort();
1126            languages.dedup();
1127            languages
1128        },
1129        grammars: manifest_json
1130            .grammars
1131            .into_keys()
1132            .map(|grammar_name| (grammar_name, Default::default()))
1133            .collect(),
1134        language_servers: Default::default(),
1135    }
1136}
1137
1138fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
1139    let mut result = LanguageQueries::default();
1140    if let Some(entries) = std::fs::read_dir(root_path).log_err() {
1141        for entry in entries {
1142            let Some(entry) = entry.log_err() else {
1143                continue;
1144            };
1145            let path = entry.path();
1146            if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
1147                if !remainder.ends_with(".scm") {
1148                    continue;
1149                }
1150                for (name, query) in QUERY_FILENAME_PREFIXES {
1151                    if remainder.starts_with(name) {
1152                        if let Some(contents) = std::fs::read_to_string(&path).log_err() {
1153                            match query(&mut result) {
1154                                None => *query(&mut result) = Some(contents.into()),
1155                                Some(r) => r.to_mut().push_str(contents.as_ref()),
1156                            }
1157                        }
1158                        break;
1159                    }
1160                }
1161            }
1162        }
1163    }
1164    result
1165}