Pass a `&mut BlockContext` when rendering blocks

Antonio Scandurra created

This wraps and derefs to `RenderContext<Editor>`, so that we can
easily use `MouseEventHandler`s in blocks.

Change summary

crates/diagnostics/src/diagnostics.rs      | 86 ++++++++++++-----------
crates/editor/src/display_map/block_map.rs | 51 +++++--------
crates/editor/src/editor.rs                |  4 
crates/editor/src/element.rs               | 24 ++++--
4 files changed, 83 insertions(+), 82 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -702,7 +702,7 @@ mod tests {
     use super::*;
     use editor::{
         display_map::{BlockContext, TransformBlock},
-        DisplayPoint, EditorSnapshot,
+        DisplayPoint,
     };
     use gpui::TestAppContext;
     use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16};
@@ -835,10 +835,8 @@ mod tests {
 
         view.next_notification(&cx).await;
         view.update(cx, |view, cx| {
-            let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
-
             assert_eq!(
-                editor_blocks(&editor, cx),
+                editor_blocks(&view.editor, cx),
                 [
                     (0, "path header block".into()),
                     (2, "diagnostic header".into()),
@@ -848,7 +846,7 @@ mod tests {
                 ]
             );
             assert_eq!(
-                editor.text(),
+                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
                 concat!(
                     //
                     // main.rs
@@ -923,10 +921,8 @@ mod tests {
 
         view.next_notification(&cx).await;
         view.update(cx, |view, cx| {
-            let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
-
             assert_eq!(
-                editor_blocks(&editor, cx),
+                editor_blocks(&view.editor, cx),
                 [
                     (0, "path header block".into()),
                     (2, "diagnostic header".into()),
@@ -938,7 +934,7 @@ mod tests {
                 ]
             );
             assert_eq!(
-                editor.text(),
+                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
                 concat!(
                     //
                     // consts.rs
@@ -1038,10 +1034,8 @@ mod tests {
 
         view.next_notification(&cx).await;
         view.update(cx, |view, cx| {
-            let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
-
             assert_eq!(
-                editor_blocks(&editor, cx),
+                editor_blocks(&view.editor, cx),
                 [
                     (0, "path header block".into()),
                     (2, "diagnostic header".into()),
@@ -1055,7 +1049,7 @@ mod tests {
                 ]
             );
             assert_eq!(
-                editor.text(),
+                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
                 concat!(
                     //
                     // consts.rs
@@ -1115,36 +1109,44 @@ mod tests {
         });
     }
 
-    fn editor_blocks(editor: &EditorSnapshot, cx: &AppContext) -> Vec<(u32, String)> {
-        editor
-            .blocks_in_range(0..editor.max_point().row())
-            .filter_map(|(row, block)| {
-                let name = match block {
-                    TransformBlock::Custom(block) => block
-                        .render(&BlockContext {
-                            cx,
-                            anchor_x: 0.,
-                            scroll_x: 0.,
-                            gutter_padding: 0.,
-                            gutter_width: 0.,
-                            line_height: 0.,
-                            em_width: 0.,
-                        })
-                        .name()?
-                        .to_string(),
-                    TransformBlock::ExcerptHeader {
-                        starts_new_buffer, ..
-                    } => {
-                        if *starts_new_buffer {
-                            "path header block".to_string()
-                        } else {
-                            "collapsed context".to_string()
+    fn editor_blocks(
+        editor: &ViewHandle<Editor>,
+        cx: &mut MutableAppContext,
+    ) -> Vec<(u32, String)> {
+        let mut presenter = cx.build_presenter(editor.id(), 0.);
+        let mut cx = presenter.build_layout_context(Default::default(), false, cx);
+        cx.render(editor, |editor, cx| {
+            let snapshot = editor.snapshot(cx);
+            snapshot
+                .blocks_in_range(0..snapshot.max_point().row())
+                .filter_map(|(row, block)| {
+                    let name = match block {
+                        TransformBlock::Custom(block) => block
+                            .render(&mut BlockContext {
+                                cx,
+                                anchor_x: 0.,
+                                scroll_x: 0.,
+                                gutter_padding: 0.,
+                                gutter_width: 0.,
+                                line_height: 0.,
+                                em_width: 0.,
+                            })
+                            .name()?
+                            .to_string(),
+                        TransformBlock::ExcerptHeader {
+                            starts_new_buffer, ..
+                        } => {
+                            if *starts_new_buffer {
+                                "path header block".to_string()
+                            } else {
+                                "collapsed context".to_string()
+                            }
                         }
-                    }
-                };
+                    };
 
-                Some((row, name))
-            })
-            .collect()
+                    Some((row, name))
+                })
+                .collect()
+        })
     }
 }

crates/editor/src/display_map/block_map.rs 🔗

@@ -4,14 +4,14 @@ use super::{
 };
 use crate::{Anchor, ToPoint as _};
 use collections::{Bound, HashMap, HashSet};
-use gpui::{AppContext, ElementBox};
+use gpui::{ElementBox, RenderContext};
 use language::{BufferSnapshot, Chunk, Patch};
 use parking_lot::Mutex;
 use std::{
     cell::RefCell,
     cmp::{self, Ordering},
     fmt::Debug,
-    ops::{Deref, Range},
+    ops::{Deref, DerefMut, Range},
     sync::{
         atomic::{AtomicUsize, Ordering::SeqCst},
         Arc,
@@ -50,7 +50,7 @@ struct BlockRow(u32);
 #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 struct WrapRow(u32);
 
-pub type RenderBlock = Arc<dyn Fn(&BlockContext) -> ElementBox>;
+pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> ElementBox>;
 
 pub struct Block {
     id: BlockId,
@@ -67,12 +67,12 @@ where
 {
     pub position: P,
     pub height: u8,
-    pub render: Arc<dyn Fn(&BlockContext) -> ElementBox>,
+    pub render: Arc<dyn Fn(&mut BlockContext) -> ElementBox>,
     pub disposition: BlockDisposition,
 }
 
-pub struct BlockContext<'a> {
-    pub cx: &'a AppContext,
+pub struct BlockContext<'a, 'b> {
+    pub cx: &'b mut RenderContext<'a, crate::Editor>,
     pub anchor_x: f32,
     pub scroll_x: f32,
     pub gutter_width: f32,
@@ -916,16 +916,22 @@ impl BlockDisposition {
     }
 }
 
-impl<'a> Deref for BlockContext<'a> {
-    type Target = AppContext;
+impl<'a, 'b> Deref for BlockContext<'a, 'b> {
+    type Target = RenderContext<'a, crate::Editor>;
 
     fn deref(&self) -> &Self::Target {
-        &self.cx
+        self.cx
+    }
+}
+
+impl<'a, 'b> DerefMut for BlockContext<'a, 'b> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.cx
     }
 }
 
 impl Block {
-    pub fn render(&self, cx: &BlockContext) -> ElementBox {
+    pub fn render(&self, cx: &mut BlockContext) -> ElementBox {
         self.render.lock()(cx)
     }
 
@@ -1008,7 +1014,7 @@ mod tests {
         let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
 
         let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
-        writer.insert(vec![
+        let block_ids = writer.insert(vec![
             BlockProperties {
                 position: buffer_snapshot.anchor_after(Point::new(1, 0)),
                 height: 1,
@@ -1036,22 +1042,7 @@ mod tests {
             .blocks_in_range(0..8)
             .map(|(start_row, block)| {
                 let block = block.as_custom().unwrap();
-                (
-                    start_row..start_row + block.height as u32,
-                    block
-                        .render(&BlockContext {
-                            cx,
-                            anchor_x: 0.,
-                            gutter_padding: 0.,
-                            scroll_x: 0.,
-                            gutter_width: 0.,
-                            line_height: 0.,
-                            em_width: 0.,
-                        })
-                        .name()
-                        .unwrap()
-                        .to_string(),
-                )
+                (start_row..start_row + block.height as u32, block.id)
             })
             .collect::<Vec<_>>();
 
@@ -1059,9 +1050,9 @@ mod tests {
         assert_eq!(
             blocks,
             &[
-                (1..2, "block 1".to_string()),
-                (2..4, "block 2".to_string()),
-                (7..10, "block 3".to_string()),
+                (1..2, block_ids[0]),
+                (2..4, block_ids[1]),
+                (7..10, block_ids[2]),
             ]
         );
 

crates/editor/src/editor.rs 🔗

@@ -4745,7 +4745,7 @@ impl Editor {
                             height: 1,
                             render: Arc::new({
                                 let editor = rename_editor.clone();
-                                move |cx: &BlockContext| {
+                                move |cx: &mut BlockContext| {
                                     ChildView::new(editor.clone())
                                         .contained()
                                         .with_padding_left(cx.anchor_x)
@@ -5866,7 +5866,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
         highlighted_lines.push(highlight_diagnostic_message(line));
     }
 
-    Arc::new(move |cx: &BlockContext| {
+    Arc::new(move |cx: &mut BlockContext| {
         let settings = cx.global::<Settings>();
         let theme = &settings.theme.editor;
         let style = diagnostic_style(diagnostic.severity, is_valid, theme);

crates/editor/src/element.rs 🔗

@@ -755,6 +755,12 @@ impl EditorElement {
         line_layouts: &[text_layout::Line],
         cx: &mut LayoutContext,
     ) -> Vec<(u32, ElementBox)> {
+        let editor = if let Some(editor) = self.view.upgrade(cx) {
+            editor
+        } else {
+            return Default::default();
+        };
+
         let scroll_x = snapshot.scroll_position.x();
         snapshot
             .blocks_in_range(rows.clone())
@@ -774,14 +780,16 @@ impl EditorElement {
                                     .x_for_index(align_to.column() as usize)
                             };
 
-                        block.render(&BlockContext {
-                            cx,
-                            anchor_x,
-                            gutter_padding,
-                            line_height,
-                            scroll_x,
-                            gutter_width,
-                            em_width,
+                        cx.render(&editor, |_, cx| {
+                            block.render(&mut BlockContext {
+                                cx,
+                                anchor_x,
+                                gutter_padding,
+                                line_height,
+                                scroll_x,
+                                gutter_width,
+                                em_width,
+                            })
                         })
                     }
                     TransformBlock::ExcerptHeader {