Made fold inline styles be driven by the fold map

Mikayla Maki and Max created

co-authored-by: Max <max@zed.dev>

Change summary

crates/editor/src/editor.rs  | 165 -------------------------------------
crates/editor/src/element.rs |  54 ++++-------
2 files changed, 23 insertions(+), 196 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -458,8 +458,6 @@ type CompletionId = usize;
 
 type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
 type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
-type TextClickedCallback =
-    fn(&MouseClick, &Range<DisplayPoint>, &EditorSnapshot, &mut EventContext);
 
 pub struct Editor {
     handle: WeakViewHandle<Self>,
@@ -488,7 +486,6 @@ pub struct Editor {
     highlighted_rows: Option<Range<u32>>,
     #[allow(clippy::type_complexity)]
     background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
-    clickable_text: BTreeMap<TypeId, (TextClickedCallback, Vec<Range<Anchor>>)>,
     nav_history: Option<ItemNavHistory>,
     context_menu: Option<ContextMenu>,
     mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
@@ -1159,7 +1156,6 @@ impl Editor {
             placeholder_text: None,
             highlighted_rows: None,
             background_highlights: Default::default(),
-            clickable_text: Default::default(),
             nav_history: None,
             context_menu: None,
             mouse_context_menu: cx.add_view(context_menu::ContextMenu::new),
@@ -1196,19 +1192,6 @@ impl Editor {
         if mode == EditorMode::Full {
             let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
             cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
-
-            // TODO: this does not work at all
-            let display_snapshot = this.snapshot(cx).display_snapshot;
-            let editor_snapshot = this.snapshot(cx);
-            if buffer.read(cx).is_singleton() {
-                this.insert_fold_styles(
-                    display_snapshot
-                        .folds_in_range(Anchor::min()..Anchor::max())
-                        .cloned(),
-                    &editor_snapshot,
-                    cx,
-                )
-            }
         }
 
         this.report_event("open editor", cx);
@@ -5866,47 +5849,16 @@ impl Editor {
     ) {
         let mut ranges = ranges.into_iter().peekable();
         if ranges.peek().is_some() {
-            let ranges = ranges.collect_vec();
-
-            self.display_map
-                .update(cx, |map, cx| map.fold(ranges.iter().cloned(), cx));
+            self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
 
             if auto_scroll {
                 self.request_autoscroll(Autoscroll::fit(), cx);
             }
 
-            let snapshot = self.snapshot(cx);
-            let anchor_ranges = offset_to_anchors(ranges, &snapshot);
-
-            self.insert_fold_styles(anchor_ranges, &snapshot, cx);
-
             cx.notify();
         }
     }
 
-    fn insert_fold_styles(
-        &mut self,
-        anchor_ranges: impl Iterator<Item = Range<Anchor>>,
-        snapshot: &EditorSnapshot,
-        cx: &mut ViewContext<Editor>,
-    ) {
-        self.change_click_ranges::<FoldMarker>(cx, |click_ranges| {
-            for range in anchor_ranges {
-                if let Err(idx) = click_ranges.binary_search_by(|click_range| {
-                    click_range.cmp(&range, &snapshot.buffer_snapshot)
-                }) {
-                    click_ranges.insert(idx, range)
-                }
-            }
-        });
-        let click_ranges = self.clone_click_ranges::<FoldMarker>();
-        self.highlight_background::<FoldMarker>(
-            click_ranges,
-            |theme| theme.editor.document_highlight_write_background,
-            cx,
-        );
-    }
-
     pub fn unfold_ranges<T: ToOffset + Clone>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
@@ -5916,41 +5868,12 @@ impl Editor {
     ) {
         let mut ranges = ranges.into_iter().peekable();
         if ranges.peek().is_some() {
-            let ranges = ranges.collect_vec();
-
-            self.display_map.update(cx, |map, cx| {
-                map.unfold(ranges.iter().cloned(), inclusive, cx)
-            });
+            self.display_map
+                .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
             if auto_scroll {
                 self.request_autoscroll(Autoscroll::fit(), cx);
             }
 
-            let snapshot = self.snapshot(cx);
-            let anchor_ranges = offset_to_anchors(ranges, &snapshot);
-
-            self.change_click_ranges::<FoldMarker>(cx, |click_ranges| {
-                for range in anchor_ranges {
-                    let range_point = range.start.to_point(&snapshot.buffer_snapshot);
-                    // Fold and unfold ranges start at different points in the row.
-                    // But their rows do match, so we can use that to detect sameness.
-                    if let Ok(idx) = click_ranges.binary_search_by(|click_range| {
-                        click_range
-                            .start
-                            .to_point(&snapshot.buffer_snapshot)
-                            .row
-                            .cmp(&range_point.row)
-                    }) {
-                        click_ranges.remove(idx);
-                    }
-                }
-            });
-            let click_ranges = self.clone_click_ranges::<FoldMarker>();
-            self.highlight_background::<FoldMarker>(
-                click_ranges,
-                |theme| theme.editor.document_highlight_write_background,
-                cx,
-            );
-
             cx.notify();
         }
     }
@@ -6076,69 +5999,6 @@ impl Editor {
         }
     }
 
-    // FIXME: Consolidate the range styling APIs so that this clone isn't nescessary
-    pub fn clone_click_ranges<T: ClickRange>(&self) -> Vec<Range<Anchor>> {
-        self.clickable_text
-            .get(&TypeId::of::<T>())
-            .map(|click_range| click_range.1.clone())
-            .unwrap_or_default()
-    }
-
-    pub fn change_click_ranges<T: ClickRange>(
-        &mut self,
-        cx: &mut ViewContext<Self>,
-        change: impl FnOnce(&mut Vec<Range<Anchor>>),
-    ) {
-        let mut ranges = self
-            .clickable_text
-            .remove(&TypeId::of::<T>())
-            .map(|click_range| click_range.1)
-            .unwrap_or_default();
-
-        change(&mut ranges);
-
-        self.clickable_text
-            .insert(TypeId::of::<T>(), (T::click_handler, ranges));
-
-        cx.notify();
-    }
-
-    pub fn click_ranges_in_range(
-        &self,
-        search_range: Range<Anchor>,
-        display_snapshot: &DisplaySnapshot,
-    ) -> Vec<(Range<DisplayPoint>, TextClickedCallback)> {
-        let mut results = Vec::new();
-        let buffer = &display_snapshot.buffer_snapshot;
-        for (callback, ranges) in self.clickable_text.values() {
-            let start_ix = match ranges.binary_search_by(|probe| {
-                let cmp = probe.end.cmp(&search_range.start, buffer);
-                if cmp.is_gt() {
-                    Ordering::Greater
-                } else {
-                    Ordering::Less
-                }
-            }) {
-                Ok(i) | Err(i) => i,
-            };
-            for range in &ranges[start_ix..] {
-                if range.start.cmp(&search_range.end, buffer).is_ge() {
-                    break;
-                }
-                let start = range
-                    .start
-                    .to_point(buffer)
-                    .to_display_point(display_snapshot);
-                let end = range
-                    .end
-                    .to_point(buffer)
-                    .to_display_point(display_snapshot);
-                results.push((start..end, *callback))
-            }
-        }
-        results
-    }
-
     pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
         self.highlighted_rows = rows;
     }
@@ -6517,25 +6377,6 @@ fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot)
     }
 }
 
-fn offset_to_anchors<
-    'snapshot,
-    'iter: 'snapshot,
-    T: ToOffset,
-    I: IntoIterator<Item = Range<T>> + 'iter,
->(
-    ranges: I,
-    snapshot: &'snapshot EditorSnapshot,
-) -> impl Iterator<Item = Range<Anchor>> + 'snapshot {
-    ranges.into_iter().map(|range| {
-        snapshot
-            .buffer_snapshot
-            .anchor_at(range.start.to_offset(&snapshot.buffer_snapshot), Bias::Left)
-            ..snapshot
-                .buffer_snapshot
-                .anchor_at(range.end.to_offset(&snapshot.buffer_snapshot), Bias::Right)
-    })
-}
-
 impl EditorSnapshot {
     pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
         self.display_snapshot.buffer_snapshot.language_at(position)

crates/editor/src/element.rs 🔗

@@ -14,7 +14,7 @@ use crate::{
     },
     mouse_context_menu::DeployMouseContextMenu,
     scroll::actions::Scroll,
-    EditorStyle, GutterHover, TextClickedCallback, UnfoldAt,
+    EditorStyle, GutterHover, UnfoldAt,
 };
 use clock::ReplicaId;
 use collections::{BTreeMap, HashMap};
@@ -30,7 +30,6 @@ use gpui::{
     },
     json::{self, ToJson},
     platform::CursorStyle,
-    scene::MouseClick,
     text_layout::{self, Line, RunStyle, TextLayoutCache},
     AppContext, Axis, Border, CursorRegion, Element, ElementBox, EventContext, LayoutContext,
     Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent, MouseRegion, MutableAppContext,
@@ -116,7 +115,7 @@ impl EditorElement {
     fn attach_mouse_handlers(
         view: &WeakViewHandle<Editor>,
         position_map: &Arc<PositionMap>,
-        click_ranges: Arc<Vec<(Range<DisplayPoint>, TextClickedCallback)>>,
+        fold_ranges: Arc<[Range<DisplayPoint>]>,
         has_popovers: bool,
         visible_bounds: RectF,
         text_bounds: RectF,
@@ -220,10 +219,12 @@ impl EditorElement {
                         let point =
                             position_to_display_point(e.position, text_bounds, &position_map);
                         if let Some(point) = point {
-                            for (range, callback) in click_ranges.iter() {
+                            for range in fold_ranges.iter() {
                                 // Range -> RangeInclusive
                                 if range.contains(&point) || range.end == point {
-                                    callback(&e, range, &position_map.snapshot, cx)
+                                    cx.dispatch_action(UnfoldAt {
+                                        display_row: point.row(),
+                                    })
                                 }
                             }
                         }
@@ -723,7 +724,7 @@ impl EditorElement {
             },
         });
 
-        for (range, _) in layout.click_ranges.iter() {
+        for range in layout.fold_ranges.iter() {
             for bound in range_to_bounds(
                 range,
                 content_origin,
@@ -1676,7 +1677,7 @@ impl Element for EditorElement {
         let mut active_rows = BTreeMap::new();
         let mut highlighted_rows = None;
         let mut highlighted_ranges = Vec::new();
-        let mut click_ranges = Vec::new();
+        let mut fold_ranges = Vec::new();
         let mut show_scrollbars = false;
         let mut include_root = false;
         let mut is_singleton = false;
@@ -1689,7 +1690,15 @@ impl Element for EditorElement {
             let theme = cx.global::<Settings>().theme.as_ref();
             highlighted_ranges =
                 view.background_highlights_in_range(start_anchor..end_anchor, &display_map, theme);
-            click_ranges = view.click_ranges_in_range(start_anchor..end_anchor, &display_map);
+
+            fold_ranges.extend(
+                snapshot
+                    .folds_in_range(start_anchor..end_anchor)
+                    .map(|anchor| {
+                        anchor.start.to_display_point(&snapshot.display_snapshot)
+                            ..anchor.end.to_display_point(&snapshot.display_snapshot)
+                    }),
+            );
 
             let mut remote_selections = HashMap::default();
             for (replica_id, line_mode, cursor_shape, selection) in display_map
@@ -1946,7 +1955,7 @@ impl Element for EditorElement {
                 active_rows,
                 highlighted_rows,
                 highlighted_ranges,
-                click_ranges: Arc::new(click_ranges),
+                fold_ranges: fold_ranges.into(),
                 line_number_layouts,
                 display_hunks,
                 blocks,
@@ -1978,7 +1987,7 @@ impl Element for EditorElement {
         Self::attach_mouse_handlers(
             &self.view,
             &layout.position_map,
-            layout.click_ranges.clone(), // No need to clone the vec
+            layout.fold_ranges.clone(), // No need to clone the vec
             layout.hover_popovers.is_some(),
             visible_bounds,
             text_bounds,
@@ -2076,7 +2085,7 @@ pub struct LayoutState {
     display_hunks: Vec<DisplayDiffHunk>,
     blocks: Vec<BlockLayout>,
     highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
-    click_ranges: Arc<Vec<(Range<DisplayPoint>, TextClickedCallback)>>,
+    fold_ranges: Arc<[Range<DisplayPoint>]>,
     selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
     scrollbar_row_range: Range<f32>,
     show_scrollbars: bool,
@@ -2383,29 +2392,6 @@ impl HighlightedRange {
     }
 }
 
-pub trait ClickRange: 'static {
-    fn click_handler(
-        click: &MouseClick,
-        range: &Range<DisplayPoint>,
-        snapshot: &EditorSnapshot,
-        cx: &mut EventContext,
-    );
-}
-
-pub enum FoldMarker {}
-impl ClickRange for FoldMarker {
-    fn click_handler(
-        _click: &MouseClick,
-        range: &Range<DisplayPoint>,
-        _snapshot: &EditorSnapshot,
-        cx: &mut EventContext,
-    ) {
-        cx.dispatch_action(UnfoldAt {
-            display_row: range.start.row(),
-        })
-    }
-}
-
 pub fn position_to_display_point(
     position: Vector2F,
     text_bounds: RectF,