Add a gpui method for listing the available actions

Max Brunsfeld created

Change summary

crates/gpui/src/app.rs | 44 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 40 insertions(+), 4 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -726,7 +726,7 @@ pub struct MutableAppContext {
     foreground_platform: Rc<dyn platform::ForegroundPlatform>,
     assets: Arc<AssetCache>,
     cx: AppContext,
-    action_deserializers: HashMap<&'static str, DeserializeActionCallback>,
+    action_deserializers: HashMap<&'static str, (TypeId, DeserializeActionCallback)>,
     capture_actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
     actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
     global_actions: HashMap<TypeId, Box<GlobalActionCallback>>,
@@ -877,7 +877,8 @@ impl MutableAppContext {
         let callback = self
             .action_deserializers
             .get(name)
-            .ok_or_else(|| anyhow!("unknown action {}", name))?;
+            .ok_or_else(|| anyhow!("unknown action {}", name))?
+            .1;
         callback(argument.unwrap_or("{}"))
             .with_context(|| format!("invalid data for action {}", name))
     }
@@ -926,7 +927,7 @@ impl MutableAppContext {
 
         self.action_deserializers
             .entry(A::qualified_name())
-            .or_insert(A::from_json_str);
+            .or_insert((TypeId::of::<A>(), A::from_json_str));
 
         let actions = if capture {
             &mut self.capture_actions
@@ -965,7 +966,7 @@ impl MutableAppContext {
 
         self.action_deserializers
             .entry(A::qualified_name())
-            .or_insert(A::from_json_str);
+            .or_insert((TypeId::of::<A>(), A::from_json_str));
 
         if self
             .global_actions
@@ -1304,6 +1305,41 @@ impl MutableAppContext {
         }
     }
 
+    pub fn available_actions(
+        &self,
+        window_id: usize,
+        view_id: usize,
+    ) -> Vec<(&'static str, Box<dyn Action>)> {
+        let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect();
+
+        let presenter = self
+            .presenters_and_platform_windows
+            .get(&window_id)
+            .unwrap()
+            .0
+            .clone();
+        let dispatch_path = presenter.borrow().dispatch_path_from(view_id);
+        for view_id in dispatch_path {
+            if let Some(view) = self.views.get(&(window_id, view_id)) {
+                let view_type = view.as_any().type_id();
+                if let Some(actions) = self.actions.get(&view_type) {
+                    action_types.extend(actions.keys().copied());
+                }
+            }
+        }
+
+        self.action_deserializers
+            .iter()
+            .filter_map(|(name, (type_id, deserialize))| {
+                if action_types.contains(type_id) {
+                    Some((*name, deserialize("{}").ok()?))
+                } else {
+                    None
+                }
+            })
+            .collect()
+    }
+
     pub fn dispatch_action<A: Action>(
         &mut self,
         window_id: usize,