Merge pull request #673 from zed-industries/unfold-on-select-match

Antonio Scandurra created

Unfold when selecting next match

Change summary

crates/editor/src/display_map.rs          |  5 ++-
crates/editor/src/display_map/fold_map.rs | 12 ++++++--
crates/editor/src/editor.rs               | 36 +++++++++++++++---------
crates/search/src/buffer_search.rs        |  8 ++---
crates/search/src/project_search.rs       |  1 
5 files changed, 38 insertions(+), 24 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -114,6 +114,7 @@ impl DisplayMap {
     pub fn unfold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
+        inclusive: bool,
         cx: &mut ModelContext<Self>,
     ) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
@@ -124,7 +125,7 @@ impl DisplayMap {
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
         self.block_map.read(snapshot, edits);
-        let (snapshot, edits) = fold_map.unfold(ranges);
+        let (snapshot, edits) = fold_map.unfold(ranges, inclusive);
         let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
         let (snapshot, edits) = self
             .wrap_map
@@ -632,7 +633,7 @@ mod tests {
                     if rng.gen() && fold_count > 0 {
                         log::info!("unfolding ranges: {:?}", ranges);
                         map.update(cx, |map, cx| {
-                            map.unfold(ranges, cx);
+                            map.unfold(ranges, true, cx);
                         });
                     } else {
                         log::info!("folding ranges: {:?}", ranges);

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

@@ -140,13 +140,14 @@ impl<'a> FoldMapWriter<'a> {
     pub fn unfold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
+        inclusive: bool,
     ) -> (FoldSnapshot, Vec<FoldEdit>) {
         let mut edits = Vec::new();
         let mut fold_ixs_to_delete = Vec::new();
         let buffer = self.0.buffer.lock().clone();
         for range in ranges.into_iter() {
             // Remove intersecting folds and add their ranges to edits that are passed to sync.
-            let mut folds_cursor = intersecting_folds(&buffer, &self.0.folds, range, true);
+            let mut folds_cursor = intersecting_folds(&buffer, &self.0.folds, range, inclusive);
             while let Some(fold) = folds_cursor.item() {
                 let offset_range = fold.0.start.to_offset(&buffer)..fold.0.end.to_offset(&buffer);
                 if offset_range.end > offset_range.start {
@@ -1278,9 +1279,14 @@ mod tests {
         assert_eq!(snapshot4.text(), "123a…c123456eee");
 
         let (mut writer, _, _) = map.write(buffer_snapshot.clone(), vec![]);
-        writer.unfold(Some(Point::new(0, 4)..Point::new(0, 5)));
+        writer.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), false);
         let (snapshot5, _) = map.read(buffer_snapshot.clone(), vec![]);
-        assert_eq!(snapshot5.text(), "123aaaaa\nbbbbbb\nccc123456eee");
+        assert_eq!(snapshot5.text(), "123a…c123456eee");
+
+        let (mut writer, _, _) = map.write(buffer_snapshot.clone(), vec![]);
+        writer.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), true);
+        let (snapshot6, _) = map.read(buffer_snapshot.clone(), vec![]);
+        assert_eq!(snapshot6.text(), "123aaaaa\nbbbbbb\nccc123456eee");
     }
 
     #[gpui::test]

crates/editor/src/editor.rs 🔗

@@ -122,7 +122,7 @@ action!(ConfirmRename);
 action!(PageUp);
 action!(PageDown);
 action!(Fold);
-action!(Unfold);
+action!(UnfoldLines);
 action!(FoldSelectedRanges);
 action!(Scroll, Vector2F);
 action!(Select, SelectPhase);
@@ -263,7 +263,7 @@ pub fn init(cx: &mut MutableAppContext) {
         Binding::new("pageup", PageUp, Some("Editor")),
         Binding::new("pagedown", PageDown, Some("Editor")),
         Binding::new("alt-cmd-[", Fold, Some("Editor")),
-        Binding::new("alt-cmd-]", Unfold, Some("Editor")),
+        Binding::new("alt-cmd-]", UnfoldLines, Some("Editor")),
         Binding::new("alt-cmd-f", FoldSelectedRanges, Some("Editor")),
         Binding::new("ctrl-space", ShowCompletions, Some("Editor")),
         Binding::new("cmd-.", ToggleCodeActions(false), Some("Editor")),
@@ -329,7 +329,7 @@ pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(Editor::page_up);
     cx.add_action(Editor::page_down);
     cx.add_action(Editor::fold);
-    cx.add_action(Editor::unfold);
+    cx.add_action(Editor::unfold_lines);
     cx.add_action(Editor::fold_selected_ranges);
     cx.add_action(Editor::show_completions);
     cx.add_action(Editor::toggle_code_actions);
@@ -3138,7 +3138,7 @@ impl Editor {
         }
 
         self.transact(cx, |this, cx| {
-            this.unfold_ranges(unfold_ranges, cx);
+            this.unfold_ranges(unfold_ranges, true, cx);
             this.buffer.update(cx, |buffer, cx| {
                 for (range, text) in edits {
                     buffer.edit([range], text, cx);
@@ -3241,7 +3241,7 @@ impl Editor {
         }
 
         self.transact(cx, |this, cx| {
-            this.unfold_ranges(unfold_ranges, cx);
+            this.unfold_ranges(unfold_ranges, true, cx);
             this.buffer.update(cx, |buffer, cx| {
                 for (range, text) in edits {
                     buffer.edit([range], text, cx);
@@ -3766,7 +3766,7 @@ impl Editor {
                 to_unfold.push(selection.start..selection.end);
             }
         }
-        self.unfold_ranges(to_unfold, cx);
+        self.unfold_ranges(to_unfold, true, cx);
         self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
     }
 
@@ -3925,6 +3925,7 @@ impl Editor {
                         reversed: false,
                         goal: SelectionGoal::None,
                     });
+                    self.unfold_ranges([next_selected_range], false, cx);
                     self.update_selections(selections, Some(Autoscroll::Newest), cx);
                 } else {
                     select_next_state.done = true;
@@ -3952,6 +3953,7 @@ impl Editor {
                     wordwise: true,
                     done: false,
                 };
+                self.unfold_ranges([selection.start..selection.end], false, cx);
                 self.update_selections(selections, Some(Autoscroll::Newest), cx);
                 self.select_next_state = Some(select_state);
             } else {
@@ -5196,7 +5198,7 @@ impl Editor {
         self.fold_ranges(fold_ranges, cx);
     }
 
-    pub fn unfold(&mut self, _: &Unfold, cx: &mut ViewContext<Self>) {
+    pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
         let selections = self.local_selections::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = &display_map.buffer_snapshot;
@@ -5211,7 +5213,7 @@ impl Editor {
                 start..end
             })
             .collect::<Vec<_>>();
-        self.unfold_ranges(ranges, cx);
+        self.unfold_ranges(ranges, true, cx);
     }
 
     fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
@@ -5262,7 +5264,7 @@ impl Editor {
         self.fold_ranges(ranges, cx);
     }
 
-    fn fold_ranges<T: ToOffset>(
+    pub fn fold_ranges<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
         cx: &mut ViewContext<Self>,
@@ -5275,10 +5277,16 @@ impl Editor {
         }
     }
 
-    fn unfold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, cx: &mut ViewContext<Self>) {
-        if !ranges.is_empty() {
+    pub fn unfold_ranges<T: ToOffset>(
+        &mut self,
+        ranges: impl IntoIterator<Item = Range<T>>,
+        inclusive: bool,
+        cx: &mut ViewContext<Self>,
+    ) {
+        let mut ranges = ranges.into_iter().peekable();
+        if ranges.peek().is_some() {
             self.display_map
-                .update(cx, |map, cx| map.unfold(ranges, cx));
+                .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
             self.request_autoscroll(Autoscroll::Fit, cx);
             cx.notify();
         }
@@ -6581,7 +6589,7 @@ mod tests {
                 .unindent(),
             );
 
-            view.unfold(&Unfold, cx);
+            view.unfold_lines(&UnfoldLines, cx);
             assert_eq!(
                 view.display_text(cx),
                 "
@@ -6602,7 +6610,7 @@ mod tests {
                 .unindent(),
             );
 
-            view.unfold(&Unfold, cx);
+            view.unfold_lines(&UnfoldLines, cx);
             assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
         });
     }

crates/search/src/buffer_search.rs 🔗

@@ -336,11 +336,9 @@ impl SearchBar {
                             direction,
                             &editor.buffer().read(cx).read(cx),
                         );
-                        editor.select_ranges(
-                            [ranges[new_index].clone()],
-                            Some(Autoscroll::Fit),
-                            cx,
-                        );
+                        let range_to_select = ranges[new_index].clone();
+                        editor.unfold_ranges([range_to_select.clone()], false, cx);
+                        editor.select_ranges([range_to_select], Some(Autoscroll::Fit), cx);
                     }
                 });
             }

crates/search/src/project_search.rs 🔗

@@ -489,6 +489,7 @@ impl ProjectSearchView {
             );
             let range_to_select = model.match_ranges[new_index].clone();
             self.results_editor.update(cx, |editor, cx| {
+                editor.unfold_ranges([range_to_select.clone()], false, cx);
                 editor.select_ranges([range_to_select], Some(Autoscroll::Fit), cx);
             });
         }