Fix renaming with `.` in JSX tags (#50373)

Om Chillure created

Fixes #50245

### Summary :
This PR fixes linked tag renaming when typing . in tag names like
<Table.Headers>...</Table.Headers>.

Previously, linked editing treated . as punctuation (unless a language
explicitly configured it as a linked-edit character), so renaming could
stop syncing at the dot and produce mismatched closing tags.

### What changed
Updated linked-edit input handling to preserve linked edits when the
typed input is exactly ".", even if the active language does not
explicitly include dot in linked_edit_characters.
Added a regression test covering dot typing in linked edits without
language override.

Kept existing punctuation behavior (e.g. >) unchanged.

### Files changed
[editor.rs]
[editor_tests.rs]

### Why this approach
Minimal, targeted fix in shared linked-edit path.
Works for .svelte and similar markup contexts where dot-separated
component names are valid in practice.
Avoids requiring every language/extension to add dot config
individually.

### Validation
Manual repro confirmed: opening tag rename with dot now keeps closing
tag synced.
Added test: test_linked_edits_on_typing_dot_without_language_override.
Existing related test remains relevant:
test_linked_edits_on_typing_punctuation.

Change summary

crates/editor/src/editor.rs       |  4 ++
crates/editor/src/editor_tests.rs | 42 +++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+), 1 deletion(-)

Detailed changes

crates/editor/src/editor.rs ๐Ÿ”—

@@ -4898,8 +4898,10 @@ impl Editor {
                         .scope_context(Some(CharScopeContext::LinkedEdit));
                     classifier.is_word(char)
                 });
+                let is_dot = text.as_ref() == ".";
+                let should_apply_linked_edit = is_word_char || is_dot;
 
-                if is_word_char {
+                if should_apply_linked_edit {
                     let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
                     linked_edits.push(&self, anchor_range, text.clone(), cx);
                 } else {

crates/editor/src/editor_tests.rs ๐Ÿ”—

@@ -26623,6 +26623,48 @@ async fn test_linked_edits_on_typing_punctuation(cx: &mut TestAppContext) {
     cx.assert_editor_state("<Animated.Vห‡></Animated.V>");
 }
 
+#[gpui::test]
+async fn test_linked_edits_on_typing_dot_without_language_override(cx: &mut TestAppContext) {
+    init_test(cx, |_| {});
+
+    let mut cx = EditorTestContext::new(cx).await;
+    let language = Arc::new(Language::new(
+        LanguageConfig {
+            name: "HTML".into(),
+            matcher: LanguageMatcher {
+                path_suffixes: vec!["html".to_string()],
+                ..LanguageMatcher::default()
+            },
+            brackets: BracketPairConfig {
+                pairs: vec![BracketPair {
+                    start: "<".into(),
+                    end: ">".into(),
+                    close: true,
+                    ..Default::default()
+                }],
+                ..Default::default()
+            },
+            ..Default::default()
+        },
+        Some(tree_sitter_html::LANGUAGE.into()),
+    ));
+    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+
+    cx.set_state("<Tableห‡></Table>");
+    cx.update_editor(|editor, _, cx| {
+        set_linked_edit_ranges(
+            (Point::new(0, 1), Point::new(0, 6)),
+            (Point::new(0, 9), Point::new(0, 14)),
+            editor,
+            cx,
+        );
+    });
+    cx.update_editor(|editor, window, cx| {
+        editor.handle_input(".", window, cx);
+    });
+    cx.assert_editor_state("<Table.ห‡></Table.>");
+}
+
 #[gpui::test]
 async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
     init_test(cx, |_| {});