From acf9b22466fc0f1aa26c4a403278bef6a1560aaa Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 12 Mar 2025 13:01:52 -0400 Subject: [PATCH] extension: Add `ExtensionEvents` for listening to extension-related events (#26562) This PR adds a new `ExtensionEvents` event bus that can be used to listen for extension-related events throughout the app. Today you need to have a handle to the `ExtensionStore` (which entails depending on `extension_host`) in order to listen for extension events. With this change subscribers only need to depend on `extension`, which has a leaner dependency graph. Release Notes: - N/A --- Cargo.lock | 1 + crates/extension/src/extension.rs | 3 ++ crates/extension/src/extension_events.rs | 35 +++++++++++++++++++ crates/extension_host/src/extension_host.rs | 7 ++-- .../src/extension_store_test.rs | 1 + crates/extensions_ui/Cargo.toml | 1 + crates/extensions_ui/src/extensions_ui.rs | 17 ++++++--- 7 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 crates/extension/src/extension_events.rs diff --git a/Cargo.lock b/Cargo.lock index 6754daa51a..603e648706 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4639,6 +4639,7 @@ dependencies = [ "collections", "db", "editor", + "extension", "extension_host", "feature_flags", "fs", diff --git a/crates/extension/src/extension.rs b/crates/extension/src/extension.rs index b0c070fedb..e222d49718 100644 --- a/crates/extension/src/extension.rs +++ b/crates/extension/src/extension.rs @@ -1,4 +1,5 @@ pub mod extension_builder; +mod extension_events; mod extension_host_proxy; mod extension_manifest; mod types; @@ -14,12 +15,14 @@ use gpui::{App, Task}; use language::LanguageName; use semantic_version::SemanticVersion; +pub use crate::extension_events::*; pub use crate::extension_host_proxy::*; pub use crate::extension_manifest::*; pub use crate::types::*; /// Initializes the `extension` crate. pub fn init(cx: &mut App) { + extension_events::init(cx); ExtensionHostProxy::default_global(cx); } diff --git a/crates/extension/src/extension_events.rs b/crates/extension/src/extension_events.rs new file mode 100644 index 0000000000..17a6545cf1 --- /dev/null +++ b/crates/extension/src/extension_events.rs @@ -0,0 +1,35 @@ +use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Global, ReadGlobal as _}; + +pub fn init(cx: &mut App) { + let extension_events = cx.new(ExtensionEvents::new); + cx.set_global(GlobalExtensionEvents(extension_events)); +} + +struct GlobalExtensionEvents(Entity); + +impl Global for GlobalExtensionEvents {} + +/// An event bus for broadcasting extension-related events throughout the app. +pub struct ExtensionEvents; + +impl ExtensionEvents { + /// Returns the global [`ExtensionEvents`]. + pub fn global(cx: &App) -> Entity { + GlobalExtensionEvents::global(cx).0.clone() + } + + fn new(_cx: &mut Context) -> Self { + Self + } + + pub fn emit(&mut self, event: Event, cx: &mut Context) { + cx.emit(event) + } +} + +#[derive(Clone)] +pub enum Event { + ExtensionsUpdated, +} + +impl EventEmitter for ExtensionEvents {} diff --git a/crates/extension_host/src/extension_host.rs b/crates/extension_host/src/extension_host.rs index bd438aa2c8..a9ad8dc9e9 100644 --- a/crates/extension_host/src/extension_host.rs +++ b/crates/extension_host/src/extension_host.rs @@ -14,7 +14,7 @@ use collections::{btree_map, BTreeMap, BTreeSet, HashMap, HashSet}; use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder}; pub use extension::ExtensionManifest; use extension::{ - ExtensionContextServerProxy, ExtensionGrammarProxy, ExtensionHostProxy, + ExtensionContextServerProxy, ExtensionEvents, ExtensionGrammarProxy, ExtensionHostProxy, ExtensionIndexedDocsProviderProxy, ExtensionLanguageProxy, ExtensionLanguageServerProxy, ExtensionSlashCommandProxy, ExtensionSnippetProxy, ExtensionThemeProxy, }; @@ -127,7 +127,6 @@ pub enum ExtensionOperation { #[derive(Clone)] pub enum Event { - ExtensionsUpdated, StartedReloading, ExtensionInstalled(Arc), ExtensionFailedToLoad(Arc), @@ -1214,7 +1213,9 @@ impl ExtensionStore { self.extension_index = new_index; cx.notify(); - cx.emit(Event::ExtensionsUpdated); + ExtensionEvents::global(cx).update(cx, |this, cx| { + this.emit(extension::Event::ExtensionsUpdated, cx) + }); cx.spawn(|this, mut cx| async move { cx.background_spawn({ diff --git a/crates/extension_host/src/extension_store_test.rs b/crates/extension_host/src/extension_store_test.rs index b497bbf9af..ad28676b37 100644 --- a/crates/extension_host/src/extension_store_test.rs +++ b/crates/extension_host/src/extension_store_test.rs @@ -780,6 +780,7 @@ fn init_test(cx: &mut TestAppContext) { let store = SettingsStore::test(cx); cx.set_global(store); release_channel::init(SemanticVersion::default(), cx); + extension::init(cx); theme::init(theme::LoadThemes::JustBase, cx); Project::init_settings(cx); ExtensionSettings::register(cx); diff --git a/crates/extensions_ui/Cargo.toml b/crates/extensions_ui/Cargo.toml index afdb3bf0a3..7a67d98a8c 100644 --- a/crates/extensions_ui/Cargo.toml +++ b/crates/extensions_ui/Cargo.toml @@ -17,6 +17,7 @@ client.workspace = true collections.workspace = true db.workspace = true editor.workspace = true +extension.workspace = true extension_host.workspace = true feature_flags.workspace = true fs.workspace = true diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 4c0fbf8290..0b79d654b0 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -9,6 +9,7 @@ use std::{ops::Range, sync::Arc}; use client::{ExtensionMetadata, ExtensionProvides}; use collections::{BTreeMap, BTreeSet}; use editor::{Editor, EditorElement, EditorStyle}; +use extension::ExtensionEvents; use extension_host::{ExtensionManifest, ExtensionOperation, ExtensionStore}; use feature_flags::FeatureFlagAppExt as _; use fuzzy::{match_strings, StringMatchCandidate}; @@ -212,7 +213,7 @@ pub struct ExtensionsPage { query_editor: Entity, query_contains_error: bool, provides_filter: Option, - _subscriptions: [gpui::Subscription; 2], + _subscriptions: Vec, extension_fetch_task: Option>, upsells: BTreeSet, } @@ -226,15 +227,12 @@ impl ExtensionsPage { cx.new(|cx| { let store = ExtensionStore::global(cx); let workspace_handle = workspace.weak_handle(); - let subscriptions = [ + let subscriptions = vec![ cx.observe(&store, |_: &mut Self, _, cx| cx.notify()), cx.subscribe_in( &store, window, move |this, _, event, window, cx| match event { - extension_host::Event::ExtensionsUpdated => { - this.fetch_extensions_debounced(cx) - } extension_host::Event::ExtensionInstalled(extension_id) => this .on_extension_installed( workspace_handle.clone(), @@ -245,6 +243,15 @@ impl ExtensionsPage { _ => {} }, ), + cx.subscribe_in( + &ExtensionEvents::global(cx), + window, + move |this, _, event, _window, cx| match event { + extension::Event::ExtensionsUpdated => { + this.fetch_extensions_debounced(cx); + } + }, + ), ]; let query_editor = cx.new(|cx| {