Fix regression in Buffer::language_scope_at
Co-authored-by: Julia <julia@zed.dev>
This commit is contained in:
parent
08429169e2
commit
1c46749ad7
3 changed files with 104 additions and 18 deletions
|
@ -2103,12 +2103,12 @@ impl Editor {
|
||||||
for (selection, autoclose_region) in
|
for (selection, autoclose_region) in
|
||||||
self.selections_with_autoclose_regions(selections, &snapshot)
|
self.selections_with_autoclose_regions(selections, &snapshot)
|
||||||
{
|
{
|
||||||
if let Some(language) = snapshot.language_scope_at(selection.head()) {
|
if let Some(scope) = snapshot.language_scope_at(selection.head()) {
|
||||||
// Determine if the inserted text matches the opening or closing
|
// Determine if the inserted text matches the opening or closing
|
||||||
// bracket of any of this language's bracket pairs.
|
// bracket of any of this language's bracket pairs.
|
||||||
let mut bracket_pair = None;
|
let mut bracket_pair = None;
|
||||||
let mut is_bracket_pair_start = false;
|
let mut is_bracket_pair_start = false;
|
||||||
for (pair, enabled) in language.brackets() {
|
for (pair, enabled) in scope.brackets() {
|
||||||
if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
|
if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
|
||||||
bracket_pair = Some(pair.clone());
|
bracket_pair = Some(pair.clone());
|
||||||
is_bracket_pair_start = true;
|
is_bracket_pair_start = true;
|
||||||
|
@ -2130,7 +2130,7 @@ impl Editor {
|
||||||
let following_text_allows_autoclose = snapshot
|
let following_text_allows_autoclose = snapshot
|
||||||
.chars_at(selection.start)
|
.chars_at(selection.start)
|
||||||
.next()
|
.next()
|
||||||
.map_or(true, |c| language.should_autoclose_before(c));
|
.map_or(true, |c| scope.should_autoclose_before(c));
|
||||||
let preceding_text_matches_prefix = prefix_len == 0
|
let preceding_text_matches_prefix = prefix_len == 0
|
||||||
|| (selection.start.column >= (prefix_len as u32)
|
|| (selection.start.column >= (prefix_len as u32)
|
||||||
&& snapshot.contains_str_at(
|
&& snapshot.contains_str_at(
|
||||||
|
|
|
@ -2145,27 +2145,46 @@ impl BufferSnapshot {
|
||||||
|
|
||||||
pub fn language_scope_at<D: ToOffset>(&self, position: D) -> Option<LanguageScope> {
|
pub fn language_scope_at<D: ToOffset>(&self, position: D) -> Option<LanguageScope> {
|
||||||
let offset = position.to_offset(self);
|
let offset = position.to_offset(self);
|
||||||
let mut range = 0..self.len();
|
let mut scope = None;
|
||||||
let mut scope = self.language.clone().map(|language| LanguageScope {
|
let mut smallest_range: Option<Range<usize>> = None;
|
||||||
language,
|
|
||||||
override_id: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Use the layer that has the smallest node intersecting the given point.
|
// Use the layer that has the smallest node intersecting the given point.
|
||||||
for layer in self.syntax.layers_for_range(offset..offset, &self.text) {
|
for layer in self.syntax.layers_for_range(offset..offset, &self.text) {
|
||||||
let mut cursor = layer.node().walk();
|
let mut cursor = layer.node().walk();
|
||||||
while cursor.goto_first_child_for_byte(offset).is_some() {}
|
|
||||||
let node_range = cursor.node().byte_range();
|
let mut range = None;
|
||||||
if node_range.to_inclusive().contains(&offset) && node_range.len() < range.len() {
|
loop {
|
||||||
range = node_range;
|
let child_range = cursor.node().byte_range();
|
||||||
|
if !child_range.to_inclusive().contains(&offset) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
range = Some(child_range);
|
||||||
|
if cursor.goto_first_child_for_byte(offset).is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(range) = range {
|
||||||
|
if smallest_range
|
||||||
|
.as_ref()
|
||||||
|
.map_or(true, |smallest_range| range.len() < smallest_range.len())
|
||||||
|
{
|
||||||
|
smallest_range = Some(range);
|
||||||
scope = Some(LanguageScope {
|
scope = Some(LanguageScope {
|
||||||
language: layer.language.clone(),
|
language: layer.language.clone(),
|
||||||
override_id: layer.override_id(offset, &self.text),
|
override_id: layer.override_id(offset, &self.text),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scope
|
scope.or_else(|| {
|
||||||
|
self.language.clone().map(|language| LanguageScope {
|
||||||
|
language,
|
||||||
|
override_id: None,
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
|
pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
|
||||||
|
|
|
@ -1631,7 +1631,7 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_language_scope_at(cx: &mut AppContext) {
|
fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
|
||||||
init_settings(cx, |_| {});
|
init_settings(cx, |_| {});
|
||||||
|
|
||||||
cx.add_model(|cx| {
|
cx.add_model(|cx| {
|
||||||
|
@ -1718,6 +1718,73 @@ fn test_language_scope_at(cx: &mut AppContext) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_language_scope_at_with_rust(cx: &mut AppContext) {
|
||||||
|
init_settings(cx, |_| {});
|
||||||
|
|
||||||
|
cx.add_model(|cx| {
|
||||||
|
let language = Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "Rust".into(),
|
||||||
|
brackets: BracketPairConfig {
|
||||||
|
pairs: vec![
|
||||||
|
BracketPair {
|
||||||
|
start: "{".into(),
|
||||||
|
end: "}".into(),
|
||||||
|
close: true,
|
||||||
|
newline: false,
|
||||||
|
},
|
||||||
|
BracketPair {
|
||||||
|
start: "'".into(),
|
||||||
|
end: "'".into(),
|
||||||
|
close: true,
|
||||||
|
newline: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
disabled_scopes_by_bracket_ix: vec![
|
||||||
|
Vec::new(), //
|
||||||
|
vec!["string".into()],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Some(tree_sitter_rust::language()),
|
||||||
|
)
|
||||||
|
.with_override_query(
|
||||||
|
r#"
|
||||||
|
(string_literal) @string
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let text = r#"
|
||||||
|
const S: &'static str = "hello";
|
||||||
|
"#
|
||||||
|
.unindent();
|
||||||
|
|
||||||
|
let buffer = Buffer::new(0, text.clone(), cx).with_language(Arc::new(language), cx);
|
||||||
|
let snapshot = buffer.snapshot();
|
||||||
|
|
||||||
|
// By default, all brackets are enabled
|
||||||
|
let config = snapshot.language_scope_at(0).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
config.brackets().map(|e| e.1).collect::<Vec<_>>(),
|
||||||
|
&[true, true]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Within a string, the quotation brackets are disabled.
|
||||||
|
let string_config = snapshot
|
||||||
|
.language_scope_at(text.find("ello").unwrap())
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
|
||||||
|
&[true, false]
|
||||||
|
);
|
||||||
|
|
||||||
|
buffer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
|
fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
|
||||||
init_settings(cx, |_| {});
|
init_settings(cx, |_| {});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue