1use std::sync::Arc;
2
3use collections::{BTreeSet, HashMap};
4use derive_more::{Deref, DerefMut};
5use gpui::Global;
6use gpui::{App, ReadGlobal};
7use parking_lot::RwLock;
8
9use crate::SlashCommand;
10
11#[derive(Default, Deref, DerefMut)]
12struct GlobalSlashCommandRegistry(Arc<SlashCommandRegistry>);
13
14impl Global for GlobalSlashCommandRegistry {}
15
16#[derive(Default)]
17struct SlashCommandRegistryState {
18 commands: HashMap<Arc<str>, Arc<dyn SlashCommand>>,
19 featured_commands: BTreeSet<Arc<str>>,
20}
21
22#[derive(Default)]
23pub struct SlashCommandRegistry {
24 state: RwLock<SlashCommandRegistryState>,
25}
26
27impl SlashCommandRegistry {
28 /// Returns the global [`SlashCommandRegistry`].
29 pub fn global(cx: &App) -> Arc<Self> {
30 GlobalSlashCommandRegistry::global(cx).0.clone()
31 }
32
33 /// Returns the global [`SlashCommandRegistry`].
34 ///
35 /// Inserts a default [`SlashCommandRegistry`] if one does not yet exist.
36 pub fn default_global(cx: &mut App) -> Arc<Self> {
37 cx.default_global::<GlobalSlashCommandRegistry>().0.clone()
38 }
39
40 pub fn new() -> Arc<Self> {
41 Arc::new(Self {
42 state: RwLock::new(SlashCommandRegistryState {
43 commands: HashMap::default(),
44 featured_commands: BTreeSet::default(),
45 }),
46 })
47 }
48
49 /// Registers the provided [`SlashCommand`].
50 pub fn register_command(&self, command: impl SlashCommand, is_featured: bool) {
51 let mut state = self.state.write();
52 let command_name: Arc<str> = command.name().into();
53 if is_featured {
54 state.featured_commands.insert(command_name.clone());
55 }
56 state.commands.insert(command_name, Arc::new(command));
57 }
58
59 /// Unregisters the provided [`SlashCommand`].
60 pub fn unregister_command(&self, command: impl SlashCommand) {
61 self.unregister_command_by_name(command.name().as_str())
62 }
63
64 /// Unregisters the command with the given name.
65 pub fn unregister_command_by_name(&self, command_name: &str) {
66 let mut state = self.state.write();
67 state.featured_commands.remove(command_name);
68 state.commands.remove(command_name);
69 }
70
71 /// Returns the names of registered [`SlashCommand`]s.
72 pub fn command_names(&self) -> Vec<Arc<str>> {
73 self.state.read().commands.keys().cloned().collect()
74 }
75
76 /// Returns the names of registered, featured [`SlashCommand`]s.
77 pub fn featured_command_names(&self) -> Vec<Arc<str>> {
78 self.state
79 .read()
80 .featured_commands
81 .iter()
82 .cloned()
83 .collect()
84 }
85
86 /// Returns the [`SlashCommand`] with the given name.
87 pub fn command(&self, name: &str) -> Option<Arc<dyn SlashCommand>> {
88 self.state.read().commands.get(name).cloned()
89 }
90}