component: Replace `linkme` with `inventory` (#30705)

Marshall Bowers created

This PR replaces the use of `linkme` with `inventory` for the component
preview registration.

Release Notes:

- N/A

Change summary

Cargo.lock                                        |  8 ----
Cargo.toml                                        |  1 
crates/assistant_tools/Cargo.toml                 |  1 
crates/component/Cargo.toml                       |  2 
crates/component/src/component.rs                 | 29 +++++++++++-----
crates/diagnostics/Cargo.toml                     |  1 
crates/git_ui/Cargo.toml                          |  1 
crates/notifications/Cargo.toml                   |  1 
crates/ui_input/Cargo.toml                        |  1 
crates/ui_macros/src/derive_register_component.rs |  9 +++-
crates/welcome/Cargo.toml                         |  1 
11 files changed, 28 insertions(+), 27 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -674,7 +674,6 @@ dependencies = [
  "language",
  "language_model",
  "language_models",
- "linkme",
  "log",
  "markdown",
  "open",
@@ -3177,7 +3176,7 @@ version = "0.1.0"
 dependencies = [
  "collections",
  "gpui",
- "linkme",
+ "inventory",
  "parking_lot",
  "strum 0.27.1",
  "theme",
@@ -4327,7 +4326,6 @@ dependencies = [
  "gpui",
  "indoc",
  "language",
- "linkme",
  "log",
  "lsp",
  "markdown",
@@ -6026,7 +6024,6 @@ dependencies = [
  "language",
  "language_model",
  "linkify",
- "linkme",
  "log",
  "markdown",
  "menu",
@@ -9105,7 +9102,6 @@ dependencies = [
  "component",
  "db",
  "gpui",
- "linkme",
  "rpc",
  "settings",
  "sum_tree",
@@ -15707,7 +15703,6 @@ dependencies = [
  "component",
  "editor",
  "gpui",
- "linkme",
  "settings",
  "theme",
  "ui",
@@ -16940,7 +16935,6 @@ dependencies = [
  "gpui",
  "install_cli",
  "language",
- "linkme",
  "picker",
  "project",
  "schemars",

Cargo.toml 🔗

@@ -795,7 +795,6 @@ ignored = [
     "prost_build",
     "serde",
     "component",
-    "linkme",
     "documented",
     "workspace-hack",
 ]

crates/assistant_tools/Cargo.toml 🔗

@@ -35,7 +35,6 @@ indoc.workspace = true
 itertools.workspace = true
 language.workspace = true
 language_model.workspace = true
-linkme.workspace = true
 log.workspace = true
 markdown.workspace = true
 open.workspace = true

crates/component/Cargo.toml 🔗

@@ -14,7 +14,7 @@ path = "src/component.rs"
 [dependencies]
 collections.workspace = true
 gpui.workspace = true
-linkme.workspace = true
+inventory.workspace = true
 parking_lot.workspace = true
 strum.workspace = true
 theme.workspace = true

crates/component/src/component.rs 🔗

@@ -9,13 +9,12 @@
 
 mod component_layout;
 
-pub use component_layout::*;
-
 use std::sync::LazyLock;
 
+pub use component_layout::*;
+
 use collections::HashMap;
 use gpui::{AnyElement, App, SharedString, Window};
-use linkme::distributed_slice;
 use parking_lot::RwLock;
 use strum::{Display, EnumString};
 
@@ -24,12 +23,27 @@ pub fn components() -> ComponentRegistry {
 }
 
 pub fn init() {
-    let component_fns: Vec<_> = __ALL_COMPONENTS.iter().cloned().collect();
-    for f in component_fns {
-        f();
+    for f in inventory::iter::<ComponentFn>() {
+        (f.0)();
+    }
+}
+
+pub struct ComponentFn(fn());
+
+impl ComponentFn {
+    pub const fn new(f: fn()) -> Self {
+        Self(f)
     }
 }
 
+inventory::collect!(ComponentFn);
+
+/// Private internals for macros.
+#[doc(hidden)]
+pub mod __private {
+    pub use inventory;
+}
+
 pub fn register_component<T: Component>() {
     let id = T::id();
     let metadata = ComponentMetadata {
@@ -46,9 +60,6 @@ pub fn register_component<T: Component>() {
     data.components.insert(id, metadata);
 }
 
-#[distributed_slice]
-pub static __ALL_COMPONENTS: [fn()] = [..];
-
 pub static COMPONENT_DATA: LazyLock<RwLock<ComponentRegistry>> =
     LazyLock::new(|| RwLock::new(ComponentRegistry::default()));
 

crates/diagnostics/Cargo.toml 🔗

@@ -23,7 +23,6 @@ futures.workspace = true
 gpui.workspace = true
 indoc.workspace = true
 language.workspace = true
-linkme.workspace = true
 log.workspace = true
 lsp.workspace = true
 markdown.workspace = true

crates/git_ui/Cargo.toml 🔗

@@ -35,7 +35,6 @@ itertools.workspace = true
 language.workspace = true
 language_model.workspace = true
 linkify.workspace = true
-linkme.workspace = true
 log.workspace = true
 markdown.workspace = true
 menu.workspace = true

crates/notifications/Cargo.toml 🔗

@@ -28,7 +28,6 @@ collections.workspace = true
 component.workspace = true
 db.workspace = true
 gpui.workspace = true
-linkme.workspace = true
 rpc.workspace = true
 sum_tree.workspace = true
 time.workspace = true

crates/ui_input/Cargo.toml 🔗

@@ -15,7 +15,6 @@ path = "src/ui_input.rs"
 component.workspace = true
 editor.workspace = true
 gpui.workspace = true
-linkme.workspace = true
 settings.workspace = true
 theme.workspace = true
 ui.workspace = true

crates/ui_macros/src/derive_register_component.rs 🔗

@@ -5,7 +5,7 @@ use syn::{DeriveInput, parse_macro_input};
 pub fn derive_register_component(input: TokenStream) -> TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
     let name = input.ident;
-    let reg_fn_name = syn::Ident::new(
+    let register_fn_name = syn::Ident::new(
         &format!("__component_registry_internal_register_{}", name),
         name.span(),
     );
@@ -16,10 +16,13 @@ pub fn derive_register_component(input: TokenStream) -> TokenStream {
         };
 
         #[allow(non_snake_case)]
-        #[linkme::distributed_slice(component::__ALL_COMPONENTS)]
-        fn #reg_fn_name() {
+        fn #register_fn_name() {
             component::register_component::<#name>();
         }
+
+        component::__private::inventory::submit! {
+            component::ComponentFn::new(#register_fn_name)
+        }
     };
     expanded.into()
 }

crates/welcome/Cargo.toml 🔗

@@ -24,7 +24,6 @@ fuzzy.workspace = true
 gpui.workspace = true
 install_cli.workspace = true
 language.workspace = true
-linkme.workspace = true
 picker.workspace = true
 project.workspace = true
 schemars.workspace = true