Show a brighter border around folded blocks with selections (#22114)

Kirill Bulatov created

Follow-up of https://github.com/zed-industries/zed/pull/22046

Properly [un]fold blocks based on the selections

<img width="1728" alt="image"
src="https://github.com/user-attachments/assets/73f319ee-3005-4a3b-95ee-4c6deb5cd0b8"
/>


Release Notes:

- N/A

Change summary

crates/editor/src/editor.rs  | 91 +++++++++++++------------------------
crates/editor/src/element.rs | 27 ++++++++++-
2 files changed, 57 insertions(+), 61 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -10329,31 +10329,20 @@ impl Editor {
                 self.fold(&Default::default(), cx)
             }
         } else {
-            let (display_snapshot, selections) = self.selections.all_adjusted_display(cx);
+            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
             let mut toggled_buffers = HashSet::default();
-            for selection in selections {
-                if let Some(buffer_id) = display_snapshot
-                    .display_point_to_anchor(selection.head(), Bias::Right)
-                    .buffer_id
-                {
-                    if toggled_buffers.insert(buffer_id) {
-                        if self.buffer_folded(buffer_id, cx) {
-                            self.unfold_buffer(buffer_id, cx);
-                        } else {
-                            self.fold_buffer(buffer_id, cx);
-                        }
-                    }
-                }
-                if let Some(buffer_id) = display_snapshot
-                    .display_point_to_anchor(selection.tail(), Bias::Left)
-                    .buffer_id
-                {
-                    if toggled_buffers.insert(buffer_id) {
-                        if self.buffer_folded(buffer_id, cx) {
-                            self.unfold_buffer(buffer_id, cx);
-                        } else {
-                            self.fold_buffer(buffer_id, cx);
-                        }
+            for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
+                self.selections
+                    .disjoint_anchors()
+                    .into_iter()
+                    .map(|selection| selection.range()),
+            ) {
+                let buffer_id = buffer_snapshot.remote_id();
+                if toggled_buffers.insert(buffer_id) {
+                    if self.buffer_folded(buffer_id, cx) {
+                        self.unfold_buffer(buffer_id, cx);
+                    } else {
+                        self.fold_buffer(buffer_id, cx);
                     }
                 }
             }
@@ -10426,24 +10415,17 @@ impl Editor {
 
             self.fold_creases(to_fold, true, cx);
         } else {
-            let (display_snapshot, selections) = self.selections.all_adjusted_display(cx);
+            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
             let mut folded_buffers = HashSet::default();
-            for selection in selections {
-                if let Some(buffer_id) = display_snapshot
-                    .display_point_to_anchor(selection.head(), Bias::Right)
-                    .buffer_id
-                {
-                    if folded_buffers.insert(buffer_id) {
-                        self.fold_buffer(buffer_id, cx);
-                    }
-                }
-                if let Some(buffer_id) = display_snapshot
-                    .display_point_to_anchor(selection.tail(), Bias::Left)
-                    .buffer_id
-                {
-                    if folded_buffers.insert(buffer_id) {
-                        self.fold_buffer(buffer_id, cx);
-                    }
+            for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
+                self.selections
+                    .disjoint_anchors()
+                    .into_iter()
+                    .map(|selection| selection.range()),
+            ) {
+                let buffer_id = buffer_snapshot.remote_id();
+                if folded_buffers.insert(buffer_id) {
+                    self.fold_buffer(buffer_id, cx);
                 }
             }
         }
@@ -10599,24 +10581,17 @@ impl Editor {
 
             self.unfold_ranges(&ranges, true, true, cx);
         } else {
-            let (display_snapshot, selections) = self.selections.all_adjusted_display(cx);
+            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
             let mut unfolded_buffers = HashSet::default();
-            for selection in selections {
-                if let Some(buffer_id) = display_snapshot
-                    .display_point_to_anchor(selection.head(), Bias::Right)
-                    .buffer_id
-                {
-                    if unfolded_buffers.insert(buffer_id) {
-                        self.unfold_buffer(buffer_id, cx);
-                    }
-                }
-                if let Some(buffer_id) = display_snapshot
-                    .display_point_to_anchor(selection.tail(), Bias::Left)
-                    .buffer_id
-                {
-                    if unfolded_buffers.insert(buffer_id) {
-                        self.unfold_buffer(buffer_id, cx);
-                    }
+            for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
+                self.selections
+                    .disjoint_anchors()
+                    .into_iter()
+                    .map(|selection| selection.range()),
+            ) {
+                let buffer_id = buffer_snapshot.remote_id();
+                if unfolded_buffers.insert(buffer_id) {
+                    self.unfold_buffer(buffer_id, cx);
                 }
             }
         }

crates/editor/src/element.rs 🔗

@@ -2151,8 +2151,20 @@ impl EditorElement {
                 prev_excerpt,
                 show_excerpt_controls,
                 height,
-                ..
             } => {
+                let block_start = DisplayPoint::new(block_row_start, 0).to_point(snapshot);
+                let block_end = DisplayPoint::new(block_row_start + *height, 0).to_point(snapshot);
+                let selected = selections
+                    .binary_search_by(|selection| {
+                        if selection.end <= block_start {
+                            Ordering::Less
+                        } else if selection.start >= block_end {
+                            Ordering::Greater
+                        } else {
+                            Ordering::Equal
+                        }
+                    })
+                    .is_ok();
                 let icon_offset = gutter_dimensions.width
                     - (gutter_dimensions.left_padding + gutter_dimensions.margin);
 
@@ -2181,6 +2193,7 @@ impl EditorElement {
                         first_excerpt,
                         header_padding,
                         true,
+                        selected,
                         jump_data,
                         cx,
                     ))
@@ -2192,7 +2205,6 @@ impl EditorElement {
                 show_excerpt_controls,
                 height,
                 starts_new_buffer,
-                ..
             } => {
                 let icon_offset = gutter_dimensions.width
                     - (gutter_dimensions.left_padding + gutter_dimensions.margin);
@@ -2223,6 +2235,7 @@ impl EditorElement {
                             next_excerpt,
                             header_padding,
                             false,
+                            false,
                             jump_data,
                             cx,
                         ));
@@ -2380,6 +2393,7 @@ impl EditorElement {
         for_excerpt: &ExcerptInfo,
         header_padding: Pixels,
         is_folded: bool,
+        is_selected: bool,
         jump_data: JumpData,
         cx: &mut WindowContext,
     ) -> Div {
@@ -2415,7 +2429,14 @@ impl EditorElement {
                     .rounded_md()
                     .shadow_md()
                     .border_1()
-                    .border_color(cx.theme().colors().border)
+                    .map(|div| {
+                        let border_color = if is_selected {
+                            cx.theme().colors().text_accent
+                        } else {
+                            cx.theme().colors().border
+                        };
+                        div.border_color(border_color)
+                    })
                     .bg(cx.theme().colors().editor_subheader_background)
                     .hover(|style| style.bg(cx.theme().colors().element_hover))
                     .map(|header| {