diff --git a/Cargo.lock b/Cargo.lock index 5b458740b0..d7154857a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4821,7 +4821,6 @@ dependencies = [ "db", "editor", "extension_host", - "feature_flags", "fs", "fuzzy", "gpui", @@ -4834,6 +4833,7 @@ dependencies = [ "serde", "settings", "smallvec", + "strum", "telemetry", "theme", "ui", diff --git a/crates/extensions_ui/Cargo.toml b/crates/extensions_ui/Cargo.toml index afdb3bf0a3..cc9b9fd286 100644 --- a/crates/extensions_ui/Cargo.toml +++ b/crates/extensions_ui/Cargo.toml @@ -18,7 +18,6 @@ collections.workspace = true db.workspace = true editor.workspace = true extension_host.workspace = true -feature_flags.workspace = true fs.workspace = true fuzzy.workspace = true gpui.workspace = true @@ -31,6 +30,7 @@ semantic_version.workspace = true serde.workspace = true settings.workspace = true smallvec.workspace = true +strum.workspace = true telemetry.workspace = true theme.workspace = true ui.workspace = true diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 4c0fbf8290..afe1ce693c 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -10,7 +10,6 @@ use client::{ExtensionMetadata, ExtensionProvides}; use collections::{BTreeMap, BTreeSet}; use editor::{Editor, EditorElement, EditorStyle}; use extension_host::{ExtensionManifest, ExtensionOperation, ExtensionStore}; -use feature_flags::FeatureFlagAppExt as _; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ actions, uniform_list, Action, App, ClipboardItem, Context, Entity, EventEmitter, Flatten, @@ -21,6 +20,7 @@ use num_format::{Locale, ToFormattedString}; use project::DirectoryLister; use release_channel::ReleaseChannel; use settings::Settings; +use strum::IntoEnumIterator as _; use theme::ThemeSettings; use ui::{prelude::*, CheckboxWithLabel, ContextMenu, PopoverMenu, ToggleButton, Tooltip}; use vim_mode_setting::VimModeSetting; @@ -127,6 +127,20 @@ pub fn init(cx: &mut App) { .detach(); } +fn extension_provides_label(provides: ExtensionProvides) -> &'static str { + match provides { + ExtensionProvides::Themes => "Themes", + ExtensionProvides::IconThemes => "Icon Themes", + ExtensionProvides::Languages => "Languages", + ExtensionProvides::Grammars => "Grammars", + ExtensionProvides::LanguageServers => "Language Servers", + ExtensionProvides::ContextServers => "Context Servers", + ExtensionProvides::SlashCommands => "Slash Commands", + ExtensionProvides::IndexedDocsProviders => "Indexed Docs Providers", + ExtensionProvides::Snippets => "Snippets", + } +} + #[derive(Clone)] pub enum ExtensionStatus { NotInstalled, @@ -608,25 +622,6 @@ impl ExtensionsPage { .provides .iter() .map(|provides| { - let label = match provides { - ExtensionProvides::Themes => "Themes", - ExtensionProvides::IconThemes => "Icon Themes", - ExtensionProvides::Languages => "Languages", - ExtensionProvides::Grammars => "Grammars", - ExtensionProvides::LanguageServers => { - "Language Servers" - } - ExtensionProvides::ContextServers => { - "Context Servers" - } - ExtensionProvides::SlashCommands => { - "Slash Commands" - } - ExtensionProvides::IndexedDocsProviders => { - "Indexed Docs Providers" - } - ExtensionProvides::Snippets => "Snippets", - }; div() .bg(cx.theme().colors().element_background) .px_0p5() @@ -634,7 +629,10 @@ impl ExtensionsPage { .border_color(cx.theme().colors().border) .rounded_sm() .child( - Label::new(label).size(LabelSize::XSmall), + Label::new(extension_provides_label( + *provides, + )) + .size(LabelSize::XSmall), ) }) .collect::>(), @@ -1140,6 +1138,53 @@ impl ExtensionsPage { upsell.when(ix < upsells_count, |upsell| upsell.border_b_1()) })) } + + fn build_extension_provides_filter_menu( + &self, + window: &mut Window, + cx: &mut Context, + ) -> Entity { + let this = cx.entity(); + ContextMenu::build(window, cx, |mut menu, _window, _cx| { + menu = menu.header("Extension Category").toggleable_entry( + "All", + self.provides_filter.is_none(), + IconPosition::End, + None, + { + let this = this.clone(); + move |_window, cx| { + this.update(cx, |this, cx| { + this.provides_filter = None; + this.refresh_search(cx); + }); + } + }, + ); + + for provides in ExtensionProvides::iter() { + let label = extension_provides_label(provides); + + menu = menu.toggleable_entry( + label, + self.provides_filter == Some(provides), + IconPosition::End, + None, + { + let this = this.clone(); + move |_window, cx| { + this.update(cx, |this, cx| { + this.provides_filter = Some(provides); + this.refresh_search(cx); + }); + } + }, + ) + } + + menu + }) + } } impl Render for ExtensionsPage { @@ -1174,41 +1219,27 @@ impl Render for ExtensionsPage { .w_full() .gap_2() .justify_between() - .child( - h_flex() - .gap_2() - .child(self.render_search(cx)) - .map(|parent| { - // Note: Staff-only until this gets design input. - if !cx.is_staff() { - return parent; - } - - parent.child(CheckboxWithLabel::new( - "icon-themes-filter", - Label::new("Icon themes"), - match self.provides_filter { - Some(ExtensionProvides::IconThemes) => { - ToggleState::Selected - } - _ => ToggleState::Unselected, - }, - cx.listener(|this, checked, _window, cx| { - match checked { - ToggleState::Unselected - | ToggleState::Indeterminate => { - this.provides_filter = None - } - ToggleState::Selected => { - this.provides_filter = - Some(ExtensionProvides::IconThemes) - } - }; - this.refresh_search(cx); - }), - )) - }), - ) + .child(h_flex().gap_2().child(self.render_search(cx)).child({ + let this = cx.entity().clone(); + PopoverMenu::new("extension-provides-filter") + .menu(move |window, cx| { + Some(this.update(cx, |this, cx| { + this.build_extension_provides_filter_menu(window, cx) + })) + }) + .trigger_with_tooltip( + Button::new( + "extension-provides-filter-button", + self.provides_filter + .map(extension_provides_label) + .unwrap_or("All"), + ) + .icon(IconName::Filter) + .icon_position(IconPosition::Start), + Tooltip::text("Filter extensions by category"), + ) + .anchor(gpui::Corner::TopLeft) + })) .child( h_flex() .child( diff --git a/crates/rpc/src/extension.rs b/crates/rpc/src/extension.rs index e8dd22b1bb..a0e00e9e47 100644 --- a/crates/rpc/src/extension.rs +++ b/crates/rpc/src/extension.rs @@ -31,6 +31,7 @@ pub struct ExtensionApiManifest { Deserialize, EnumString, strum::Display, + strum::EnumIter, )] #[serde(rename_all = "kebab-case")] #[strum(serialize_all = "kebab-case")]