Surround selections with brackets when they are non-empty

Antonio Scandurra created

Change summary

crates/editor/src/editor.rs | 71 +++++++++++++++++++++++++++++++++++++-
1 file changed, 69 insertions(+), 2 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -1640,8 +1640,10 @@ impl Editor {
         let text = action.0.as_ref();
         if !self.skip_autoclose_end(text, cx) {
             self.start_transaction(cx);
-            self.insert(text, cx);
-            self.autoclose_pairs(cx);
+            if !self.surround_with_pair(text, cx) {
+                self.insert(text, cx);
+                self.autoclose_pairs(cx);
+            }
             self.end_transaction(cx);
             self.trigger_completion_on_input(text, cx);
         }
@@ -1824,6 +1826,53 @@ impl Editor {
         }
     }
 
+    fn surround_with_pair(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
+        let snapshot = self.buffer.read(cx).snapshot(cx);
+        if let Some(pair) = snapshot
+            .language()
+            .and_then(|language| language.brackets().iter().find(|b| b.start == text))
+            .cloned()
+        {
+            if self
+                .local_selections::<usize>(cx)
+                .iter()
+                .any(|selection| selection.is_empty())
+            {
+                false
+            } else {
+                let mut selections = self.selections.to_vec();
+                for selection in &mut selections {
+                    selection.end = selection.end.bias_left(&snapshot);
+                }
+                drop(snapshot);
+
+                self.buffer.update(cx, |buffer, cx| {
+                    buffer.edit(
+                        selections.iter().map(|s| s.start.clone()..s.start.clone()),
+                        &pair.start,
+                        cx,
+                    );
+                    buffer.edit(
+                        selections.iter().map(|s| s.end.clone()..s.end.clone()),
+                        &pair.end,
+                        cx,
+                    );
+                });
+
+                let snapshot = self.buffer.read(cx).read(cx);
+                for selection in &mut selections {
+                    selection.end = selection.end.bias_right(&snapshot);
+                }
+                drop(snapshot);
+
+                self.set_selections(selections.into(), None, cx);
+                true
+            }
+        } else {
+            false
+        }
+    }
+
     fn autoclose_pairs(&mut self, cx: &mut ViewContext<Self>) {
         let selections = self.local_selections::<usize>(cx);
         let mut bracket_pair_state = None;
@@ -8115,6 +8164,24 @@ mod tests {
                 "
                 .unindent()
             );
+
+            view.undo(&Undo, cx);
+            view.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)], cx);
+            view.handle_input(&Input("{".to_string()), cx);
+            assert_eq!(
+                view.text(cx),
+                "
+                {a}
+
+                /*
+                *
+                "
+                .unindent()
+            );
+            assert_eq!(
+                view.selected_display_ranges(cx),
+                [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
+            );
         });
     }