editor: Fix adding extraneous closing tags within TSX (#38534)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user