editor: Fix extra characters were being written at the end of an HTML tag (#28529)

Smit Barmase created

Closes #25586

It is caused due to assumption all character being typed are word
characters and linked edit ranges can be used even when first non-word
character is typed. Because next character passes all the criteria like
being word character, anchor matching the previous range before typing
started, wrong edit take place.

This PR fixes it by clearing linked edit ranges when non-word character
is typed.

Before:

`<div cx^></div>cx` when typing fast.

After:

`<div cx^></div>` always.


Release Notes:

- Fixed a case where extra characters were being written at the end of
an HTML tag.

Change summary

crates/editor/src/editor.rs                | 6 ++++++
crates/editor/src/linked_editing_ranges.rs | 4 ++++
2 files changed, 10 insertions(+)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -3130,6 +3130,7 @@ impl Editor {
         let mut new_selections = Vec::with_capacity(selections.len());
         let mut new_autoclose_regions = Vec::new();
         let snapshot = self.buffer.read(cx).read(cx);
+        let mut clear_linked_edit_ranges = false;
 
         for (selection, autoclose_region) in
             self.selections_with_autoclose_regions(selections, &snapshot)
@@ -3357,6 +3358,8 @@ impl Editor {
                                 .extend(edits.into_iter().map(|range| (range, text.clone())));
                         }
                     }
+                } else {
+                    clear_linked_edit_ranges = true;
                 }
             }
 
@@ -3367,6 +3370,9 @@ impl Editor {
         drop(snapshot);
 
         self.transact(window, cx, |this, window, cx| {
+            if clear_linked_edit_ranges {
+                this.linked_edit_ranges.clear();
+            }
             let initial_buffer_versions =
                 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 

crates/editor/src/linked_editing_ranges.rs 🔗

@@ -34,6 +34,10 @@ impl LinkedEditingRanges {
     pub(super) fn is_empty(&self) -> bool {
         self.0.is_empty()
     }
+
+    pub(super) fn clear(&mut self) {
+        self.0.clear();
+    }
 }
 
 const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);