Log prettified element debug JSON to on cmd-alt-i

Nathan Sobo created

Change summary

gpui/src/app.rs                     | 47 ++++++++++++++++++++++++------
gpui/src/presenter.rs               | 16 +++++++++-
zed/src/workspace/workspace_view.rs | 21 +++++++++++-
3 files changed, 69 insertions(+), 15 deletions(-)

Detailed changes

gpui/src/app.rs 🔗

@@ -310,6 +310,7 @@ pub struct MutableAppContext {
     window_invalidations: HashMap<usize, WindowInvalidation>,
     invalidation_callbacks:
         HashMap<usize, Box<dyn FnMut(WindowInvalidation, &mut MutableAppContext)>>,
+    debug_elements_callbacks: HashMap<usize, Box<dyn Fn(&AppContext) -> crate::json::Value>>,
     foreground: Rc<executor::Foreground>,
     future_handlers: Rc<RefCell<HashMap<usize, FutureHandler>>>,
     stream_handlers: Rc<RefCell<HashMap<usize, StreamHandler>>>,
@@ -347,6 +348,7 @@ impl MutableAppContext {
             observations: HashMap::new(),
             window_invalidations: HashMap::new(),
             invalidation_callbacks: HashMap::new(),
+            debug_elements_callbacks: HashMap::new(),
             foreground,
             future_handlers: Default::default(),
             stream_handlers: Default::default(),
@@ -373,16 +375,29 @@ impl MutableAppContext {
         &self.ctx.background
     }
 
-    pub fn on_window_invalidated<F: 'static + FnMut(WindowInvalidation, &mut MutableAppContext)>(
-        &mut self,
-        window_id: usize,
-        callback: F,
-    ) {
+    pub fn on_window_invalidated<F>(&mut self, window_id: usize, callback: F)
+    where
+        F: 'static + FnMut(WindowInvalidation, &mut MutableAppContext),
+    {
         self.invalidation_callbacks
             .insert(window_id, Box::new(callback));
         self.update_windows();
     }
 
+    pub fn on_debug_elements<F>(&mut self, window_id: usize, callback: F)
+    where
+        F: 'static + Fn(&AppContext) -> crate::json::Value,
+    {
+        self.debug_elements_callbacks
+            .insert(window_id, Box::new(callback));
+    }
+
+    pub fn debug_elements(&self, window_id: usize) -> Option<crate::json::Value> {
+        self.debug_elements_callbacks
+            .get(&window_id)
+            .map(|debug_elements| debug_elements(&self.ctx))
+    }
+
     pub fn add_action<S, V, T, F>(&mut self, name: S, mut handler: F)
     where
         S: Into<String>,
@@ -692,11 +707,19 @@ impl MutableAppContext {
                     }));
                 }
 
-                self.on_window_invalidated(window_id, move |invalidation, ctx| {
-                    let mut presenter = presenter.borrow_mut();
-                    presenter.invalidate(invalidation, ctx.downgrade());
-                    let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx);
-                    window.present_scene(scene);
+                {
+                    let presenter = presenter.clone();
+                    self.on_window_invalidated(window_id, move |invalidation, ctx| {
+                        let mut presenter = presenter.borrow_mut();
+                        presenter.invalidate(invalidation, ctx.downgrade());
+                        let scene =
+                            presenter.build_scene(window.size(), window.scale_factor(), ctx);
+                        window.present_scene(scene);
+                    });
+                }
+
+                self.on_debug_elements(window_id, move |ctx| {
+                    presenter.borrow().debug_elements(ctx).unwrap()
                 });
             }
         }
@@ -1573,6 +1596,10 @@ impl<'a, T: View> ViewContext<'a, T> {
         &self.app.ctx.background
     }
 
+    pub fn debug_elements(&self) -> crate::json::Value {
+        self.app.debug_elements(self.window_id).unwrap()
+    }
+
     pub fn focus<S>(&mut self, handle: S)
     where
         S: Into<AnyViewHandle>,

gpui/src/presenter.rs 🔗

@@ -2,7 +2,7 @@ use crate::{
     app::{AppContext, MutableAppContext, WindowInvalidation},
     elements::Element,
     font_cache::FontCache,
-    json::ToJson,
+    json::{self, ToJson},
     platform::Event,
     text_layout::TextLayoutCache,
     AssetCache, ElementBox, Scene,
@@ -130,6 +130,18 @@ impl Presenter {
             Vec::new()
         }
     }
+
+    pub fn debug_elements(&self, ctx: &AppContext) -> Option<json::Value> {
+        ctx.root_view_id(self.window_id)
+            .and_then(|root_view_id| self.rendered_views.get(&root_view_id))
+            .map(|root_element| {
+                root_element.debug(&DebugContext {
+                    rendered_views: &self.rendered_views,
+                    font_cache: &self.font_cache,
+                    app: ctx,
+                })
+            })
+    }
 }
 
 pub struct ActionToDispatch {
@@ -227,7 +239,7 @@ impl<'a> EventContext<'a> {
 }
 
 pub struct DebugContext<'a> {
-    rendered_views: &'a mut HashMap<usize, ElementBox>,
+    rendered_views: &'a HashMap<usize, ElementBox>,
     pub font_cache: &'a FontCache,
     pub app: &'a AppContext,
 }

zed/src/workspace/workspace_view.rs 🔗

@@ -2,15 +2,19 @@ use super::{pane, Pane, PaneGroup, SplitDirection, Workspace};
 use crate::{settings::Settings, watch};
 use futures_core::future::LocalBoxFuture;
 use gpui::{
-    color::rgbu, elements::*, keymap::Binding, AnyViewHandle, App, AppContext, Entity, ModelHandle,
-    MutableAppContext, View, ViewContext, ViewHandle,
+    color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, App,
+    AppContext, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle,
 };
 use log::{error, info};
 use std::{collections::HashSet, path::PathBuf};
 
 pub fn init(app: &mut App) {
     app.add_action("workspace:save", WorkspaceView::save_active_item);
-    app.add_bindings(vec![Binding::new("cmd-s", "workspace:save", None)]);
+    app.add_action("workspace:debug_elements", WorkspaceView::debug_elements);
+    app.add_bindings(vec![
+        Binding::new("cmd-s", "workspace:save", None),
+        Binding::new("cmd-alt-i", "workspace:debug_elements", None),
+    ]);
 }
 
 pub trait ItemView: View {
@@ -251,6 +255,17 @@ impl WorkspaceView {
         });
     }
 
+    pub fn debug_elements(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        match to_string_pretty(&ctx.debug_elements()) {
+            Ok(json) => {
+                log::info!("{}", json);
+            }
+            Err(error) => {
+                log::error!("error debugging elements: {}", error);
+            }
+        };
+    }
+
     fn workspace_updated(&mut self, _: ModelHandle<Workspace>, ctx: &mut ViewContext<Self>) {
         ctx.notify();
     }