editor: Fix adding extraneous closing tags within TSX (#38534)

This commit is contained in:
Smit Barmase
2025-09-20 04:40:22 +05:30
committed by GitHub
parent 8df616e28b
commit be77682a3f
13 changed files with 218 additions and 84 deletions
+27 -12
View File
@@ -546,6 +546,23 @@ pub enum CharKind {
Word,
}
/// Context for character classification within a specific scope.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum CharScopeContext {
/// Character classification for completion queries.
///
/// This context treats certain characters as word constituents that would
/// normally be considered punctuation, such as '-' in Tailwind classes
/// ("bg-yellow-100") or '.' in import paths ("foo.ts").
Completion,
/// Character classification for linked edits.
///
/// This context handles characters that should be treated as part of
/// identifiers during linked editing operations, such as '.' in JSX
/// component names like `<Animated.View>`.
LinkedEdit,
}
/// A runnable is a set of data about a region that could be resolved into a task
pub struct Runnable {
pub tags: SmallVec<[RunnableTag; 1]>,
@@ -3449,16 +3466,14 @@ impl BufferSnapshot {
pub fn surrounding_word<T: ToOffset>(
&self,
start: T,
for_completion: bool,
scope_context: Option<CharScopeContext>,
) -> (Range<usize>, Option<CharKind>) {
let mut start = start.to_offset(self);
let mut end = start;
let mut next_chars = self.chars_at(start).take(128).peekable();
let mut prev_chars = self.reversed_chars_at(start).take(128).peekable();
let classifier = self
.char_classifier_at(start)
.for_completion(for_completion);
let classifier = self.char_classifier_at(start).scope_context(scope_context);
let word_kind = cmp::max(
prev_chars.peek().copied().map(|c| classifier.kind(c)),
next_chars.peek().copied().map(|c| classifier.kind(c)),
@@ -5212,7 +5227,7 @@ pub(crate) fn contiguous_ranges(
#[derive(Default, Debug)]
pub struct CharClassifier {
scope: Option<LanguageScope>,
for_completion: bool,
scope_context: Option<CharScopeContext>,
ignore_punctuation: bool,
}
@@ -5220,14 +5235,14 @@ impl CharClassifier {
pub fn new(scope: Option<LanguageScope>) -> Self {
Self {
scope,
for_completion: false,
scope_context: None,
ignore_punctuation: false,
}
}
pub fn for_completion(self, for_completion: bool) -> Self {
pub fn scope_context(self, scope_context: Option<CharScopeContext>) -> Self {
Self {
for_completion,
scope_context,
..self
}
}
@@ -5257,10 +5272,10 @@ impl CharClassifier {
}
if let Some(scope) = &self.scope {
let characters = if self.for_completion {
scope.completion_query_characters()
} else {
scope.word_characters()
let characters = match self.scope_context {
Some(CharScopeContext::Completion) => scope.completion_query_characters(),
Some(CharScopeContext::LinkedEdit) => scope.linked_edit_characters(),
None => scope.word_characters(),
};
if let Some(characters) = characters
&& characters.contains(&c)
+15
View File
@@ -780,6 +780,9 @@ pub struct LanguageConfig {
/// A list of characters that Zed should treat as word characters for completion queries.
#[serde(default)]
pub completion_query_characters: HashSet<char>,
/// A list of characters that Zed should treat as word characters for linked edit operations.
#[serde(default)]
pub linked_edit_characters: HashSet<char>,
/// A list of preferred debuggers for this language.
#[serde(default)]
pub debuggers: IndexSet<SharedString>,
@@ -916,6 +919,8 @@ pub struct LanguageConfigOverride {
#[serde(default)]
pub completion_query_characters: Override<HashSet<char>>,
#[serde(default)]
pub linked_edit_characters: Override<HashSet<char>>,
#[serde(default)]
pub opt_into_language_servers: Vec<LanguageServerName>,
#[serde(default)]
pub prefer_label_for_snippet: Option<bool>,
@@ -974,6 +979,7 @@ impl Default for LanguageConfig {
hidden: false,
jsx_tag_auto_close: None,
completion_query_characters: Default::default(),
linked_edit_characters: Default::default(),
debuggers: Default::default(),
}
}
@@ -2011,6 +2017,15 @@ impl LanguageScope {
)
}
/// Returns a list of language-specific characters that are considered part of
/// identifiers during linked editing operations.
pub fn linked_edit_characters(&self) -> Option<&HashSet<char>> {
Override::as_option(
self.config_override().map(|o| &o.linked_edit_characters),
Some(&self.language.config.linked_edit_characters),
)
}
/// Returns whether to prefer snippet `label` over `new_text` to replace text when
/// completion is accepted.
///
+3 -2
View File
@@ -1,4 +1,4 @@
use crate::{CharClassifier, CharKind, LanguageScope};
use crate::{CharClassifier, CharKind, CharScopeContext, LanguageScope};
use anyhow::{Context, anyhow};
use imara_diff::{
Algorithm, UnifiedDiffBuilder, diff,
@@ -181,7 +181,8 @@ fn diff_internal(
}
fn tokenize(text: &str, language_scope: Option<LanguageScope>) -> impl Iterator<Item = &str> {
let classifier = CharClassifier::new(language_scope).for_completion(true);
let classifier =
CharClassifier::new(language_scope).scope_context(Some(CharScopeContext::Completion));
let mut chars = text.char_indices();
let mut prev = None;
let mut start_ix = 0;