editor: Fix bracket highlight flickering when editing (#49797)

Xin Zhao created

Closes #49653

Before you mark this PR as ready for review, make sure that you have:
- [x] screen record added
- [x] Done a self-review taking into account security and performance
aspects
- [x] No UI change

The original `refresh_matching_bracket_highlights` method always
performed a clear --> highlight procedure, even when the bracket
positions hadn't changed, causing the highlights to flicker. Normally,
for single quotes or brackets, the flickering area is small and hard to
notice (though visible if you pay close attention), but for Python
docstrings, the three quotes create a larger highlighted area, making
this issue much more apparent.

After fix:


https://github.com/user-attachments/assets/bcd9b464-0def-438d-86b3-7e6b3779b5c6



Release Notes:

- Fixed bracket highlights flickering when editing inside brackets

Change summary

crates/editor/src/highlight_matching_bracket.rs | 40 +++++++++++++-----
1 file changed, 28 insertions(+), 12 deletions(-)

Detailed changes

crates/editor/src/highlight_matching_bracket.rs 🔗

@@ -11,11 +11,10 @@ impl Editor {
         snapshot: &DisplaySnapshot,
         cx: &mut Context<Editor>,
     ) {
-        self.clear_highlights(HighlightKey::MatchingBracket, cx);
-
         let newest_selection = self.selections.newest::<MultiBufferOffset>(&snapshot);
         // Don't highlight brackets if the selection isn't empty
         if !newest_selection.is_empty() {
+            self.clear_highlights(HighlightKey::MatchingBracket, cx);
             return;
         }
 
@@ -40,16 +39,32 @@ impl Editor {
         });
         self.refresh_matching_bracket_highlights_task = cx.spawn({
             let buffer_snapshot = buffer_snapshot.clone();
-            async move |editor, cx| {
-                if let Some((opening_range, closing_range)) = task.await {
-                    editor
-                        .update(cx, |editor, cx| {
+            async move |this, cx| {
+                let bracket_ranges = task.await;
+                let current_ranges = this
+                    .read_with(cx, |editor, cx| {
+                        editor
+                            .display_map
+                            .read(cx)
+                            .text_highlights(HighlightKey::MatchingBracket)
+                            .map(|(_, ranges)| ranges.to_vec())
+                    })
+                    .ok()
+                    .flatten();
+                let new_ranges = bracket_ranges.map(|(opening_range, closing_range)| {
+                    vec![
+                        opening_range.to_anchors(&buffer_snapshot),
+                        closing_range.to_anchors(&buffer_snapshot),
+                    ]
+                });
+
+                if current_ranges != new_ranges {
+                    this.update(cx, |editor, cx| {
+                        editor.clear_highlights(HighlightKey::MatchingBracket, cx);
+                        if let Some(new_ranges) = new_ranges {
                             editor.highlight_text(
                                 HighlightKey::MatchingBracket,
-                                vec![
-                                    opening_range.to_anchors(&buffer_snapshot),
-                                    closing_range.to_anchors(&buffer_snapshot),
-                                ],
+                                new_ranges,
                                 HighlightStyle {
                                     background_color: Some(
                                         cx.theme()
@@ -60,8 +75,9 @@ impl Editor {
                                 },
                                 cx,
                             )
-                        })
-                        .ok();
+                        }
+                    })
+                    .ok();
                 }
             }
         });