Test more editing patterns of SyntaxMap, fix bugs

This commit is contained in:
Max Brunsfeld 2022-08-22 14:49:16 -07:00
parent e8548e7732
commit 58fda5ac1c
3 changed files with 341 additions and 197 deletions

2
Cargo.lock generated
View file

@ -5842,7 +5842,7 @@ dependencies = [
[[package]] [[package]]
name = "tree-sitter" name = "tree-sitter"
version = "0.20.8" version = "0.20.8"
source = "git+https://github.com/tree-sitter/tree-sitter?rev=1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0#1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0" source = "git+https://github.com/tree-sitter/tree-sitter?rev=477b6677537e89c7bdff14ce84dad6d23a6415bb#477b6677537e89c7bdff14ce84dad6d23a6415bb"
dependencies = [ dependencies = [
"cc", "cc",
"regex", "regex",

View file

@ -4,7 +4,7 @@ default-members = ["crates/zed"]
resolver = "2" resolver = "2"
[patch.crates-io] [patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0" } tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "477b6677537e89c7bdff14ce84dad6d23a6415bb" }
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" } async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457 # TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457

View file

@ -7,7 +7,7 @@ use std::{
}; };
use sum_tree::{Bias, SeekTarget, SumTree}; use sum_tree::{Bias, SeekTarget, SumTree};
use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint}; use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint};
use tree_sitter::{Parser, Tree}; use tree_sitter::{Node, Parser, Tree};
thread_local! { thread_local! {
static PARSER: RefCell<Parser> = RefCell::new(Parser::new()); static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
@ -15,7 +15,8 @@ thread_local! {
#[derive(Default)] #[derive(Default)]
pub struct SyntaxMap { pub struct SyntaxMap {
version: clock::Global, parsed_version: clock::Global,
interpolated_version: clock::Global,
snapshot: SyntaxSnapshot, snapshot: SyntaxSnapshot,
language_registry: Option<Arc<LanguageRegistry>>, language_registry: Option<Arc<LanguageRegistry>>,
} }
@ -40,14 +41,14 @@ struct SyntaxLayerSummary {
last_layer_range: Range<Anchor>, last_layer_range: Range<Anchor>,
} }
#[derive(Debug)] #[derive(Clone, Debug)]
struct DepthAndRange(usize, Range<Anchor>); struct DepthAndRange(usize, Range<Anchor>);
#[derive(Debug)] #[derive(Clone, Debug)]
struct DepthAndMaxPosition(usize, Anchor); struct DepthAndMaxPosition(usize, Anchor);
#[derive(Debug)] #[derive(Clone, Debug)]
struct DepthAndRangeOrMaxPosition(usize, Range<Anchor>, Anchor); struct DepthAndRangeOrMaxPosition(DepthAndRange, DepthAndMaxPosition);
struct ReparseStep { struct ReparseStep {
depth: usize, depth: usize,
@ -76,44 +77,29 @@ impl SyntaxMap {
} }
pub fn interpolate(&mut self, text: &BufferSnapshot) { pub fn interpolate(&mut self, text: &BufferSnapshot) {
self.snapshot.interpolate(&self.version, text); self.snapshot.interpolate(&self.interpolated_version, text);
self.version = text.version.clone(); self.interpolated_version = text.version.clone();
} }
pub fn reparse(&mut self, language: Arc<Language>, text: &BufferSnapshot) { pub fn reparse(&mut self, language: Arc<Language>, text: &BufferSnapshot) {
self.version = text.version.clone(); if !self.interpolated_version.observed_all(&text.version) {
self.snapshot self.interpolate(text);
.reparse(self.language_registry.clone(), language, text); }
self.snapshot.reparse(
&self.parsed_version,
text,
self.language_registry.clone(),
language,
);
self.parsed_version = text.version.clone();
} }
} }
// Assumptions:
// * The maximum depth is small (< 5)
// * For a given depth, the number of layers that touch a given range
// is small (usually only 1)
// |change|
// 0 (............................................................)
// 1 (...............................................)
// 1 (................)
// 1 (.......)
// 2 (....)
// 2 (....)
// 2 (.......)
// 2 (...)
// 2 (.........)
// 2 (...)
// 3 (.)
// 3 (.)
// 3 (..)
// 3 (..)
// 3 (..)
// 3 (.)
impl SyntaxSnapshot { impl SyntaxSnapshot {
pub fn interpolate(&mut self, current_version: &clock::Global, text: &BufferSnapshot) { pub fn interpolate(&mut self, from_version: &clock::Global, text: &BufferSnapshot) {
let edits = text let edits = text
.edits_since::<(usize, Point)>(&current_version) .edits_since::<(usize, Point)>(&from_version)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if edits.is_empty() { if edits.is_empty() {
return; return;
@ -152,16 +138,9 @@ impl SyntaxSnapshot {
} else { } else {
break; break;
}; };
if first_edit.new.start.0 > layer_range.end.0 { let target = DepthAndMaxPosition(depth, text.anchor_before(first_edit.new.start.0));
layers.push_tree( if target.cmp(&cursor.start(), text).is_gt() {
cursor.slice( layers.push_tree(cursor.slice(&target, Bias::Left, text), text);
&DepthAndMaxPosition(depth, text.anchor_before(first_edit.new.start.0)),
Bias::Left,
text,
),
text,
);
continue;
} }
// Preserve any layers at this depth that follow the last edit. // Preserve any layers at this depth that follow the last edit.
@ -226,10 +205,17 @@ impl SyntaxSnapshot {
pub fn reparse( pub fn reparse(
&mut self, &mut self,
from_version: &clock::Global,
text: &BufferSnapshot,
registry: Option<Arc<LanguageRegistry>>, registry: Option<Arc<LanguageRegistry>>,
language: Arc<Language>, language: Arc<Language>,
text: &BufferSnapshot,
) { ) {
let edits = text.edits_since::<usize>(from_version).collect::<Vec<_>>();
if edits.is_empty() {
return;
}
let max_depth = self.layers.summary().max_depth;
let mut cursor = self.layers.cursor::<SyntaxLayerSummary>(); let mut cursor = self.layers.cursor::<SyntaxLayerSummary>();
cursor.next(&text); cursor.next(&text);
let mut layers = SumTree::new(); let mut layers = SumTree::new();
@ -248,44 +234,55 @@ impl SyntaxSnapshot {
let (depth, range) = if let Some(step) = &step { let (depth, range) = if let Some(step) = &step {
(step.depth, step.range.clone()) (step.depth, step.range.clone())
} else { } else {
(cursor.start().max_depth, Anchor::MAX..Anchor::MAX) (max_depth + 1, Anchor::MAX..Anchor::MAX)
}; };
let target = DepthAndRange(depth, range.clone()); let target = DepthAndRange(depth, range.clone());
if target.cmp(cursor.start(), &text).is_gt() { let mut done = cursor.item().is_none();
let change_start_anchor = changed_regions while !done && target.cmp(cursor.start(), &text).is_gt() {
.first() let bounded_target = DepthAndRangeOrMaxPosition(
.map_or(Anchor::MAX, |region| region.range.start); target.clone(),
let seek_target = changed_regions
DepthAndRangeOrMaxPosition(depth, range.clone(), change_start_anchor); .first()
let slice = cursor.slice(&seek_target, Bias::Left, text); .map_or(DepthAndMaxPosition(usize::MAX, Anchor::MAX), |region| {
layers.push_tree(slice, &text); DepthAndMaxPosition(region.depth, region.range.start)
}),
);
if bounded_target.cmp(&cursor.start(), &text).is_gt() {
let slice = cursor.slice(&bounded_target, Bias::Left, text);
layers.push_tree(slice, &text);
}
while let Some(layer) = cursor.item() { while target.cmp(&cursor.end(text), text).is_gt() {
if target.cmp(&cursor.end(text), text).is_le() { let layer = if let Some(layer) = cursor.item() {
layer
} else {
break; break;
} };
if layer_is_changed(layer, text, &changed_regions) { if layer_is_changed(layer, text, &changed_regions) {
let region = ChangedRegion { ChangedRegion {
depth: depth + 1, depth: depth + 1,
range: layer.range.clone(), range: layer.range.clone(),
};
if let Err(i) =
changed_regions.binary_search_by(|probe| probe.cmp(&region, text))
{
changed_regions.insert(i, region);
} }
.insert(text, &mut changed_regions);
} else { } else {
layers.push(layer.clone(), text); layers.push(layer.clone(), text);
} }
cursor.next(text); cursor.next(text);
} }
done = true;
changed_regions.retain(|region| { changed_regions.retain(|region| {
region.depth > depth if region.depth > depth
|| (region.depth == depth || (region.depth == depth
&& region.range.end.cmp(&range.start, text).is_gt()) && region.range.end.cmp(&range.start, text).is_gt())
{
true
} else {
done = false;
false
}
}); });
} }
@ -332,15 +329,19 @@ impl SyntaxSnapshot {
Some(old_layer.tree.clone()), Some(old_layer.tree.clone()),
ranges, ranges,
); );
changed_ranges = join_ranges(
changed_ranges = old_layer edits
.tree .iter()
.changed_ranges(&tree) .map(|e| e.new.clone())
.map(|r| r.start_byte..r.end_byte) .filter(|range| range.start < end_byte && range.end > start_byte),
.collect(); old_layer
.tree
.changed_ranges(&tree)
.map(|r| start_byte + r.start_byte..start_byte + r.end_byte),
);
} else { } else {
tree = parse_text(grammar, text.as_rope(), None, ranges); tree = parse_text(grammar, text.as_rope(), None, ranges);
changed_ranges = vec![0..end_byte - start_byte]; changed_ranges = vec![start_byte..end_byte];
} }
layers.push( layers.push(
@ -358,27 +359,19 @@ impl SyntaxSnapshot {
changed_ranges.is_empty(), changed_ranges.is_empty(),
) { ) {
let depth = depth + 1; let depth = depth + 1;
for range in &changed_ranges { for range in &changed_ranges {
let region = ChangedRegion { ChangedRegion {
depth, depth,
range: text.anchor_before(range.start)..text.anchor_after(range.end), range: text.anchor_before(range.start)..text.anchor_after(range.end),
};
if let Err(i) =
changed_regions.binary_search_by(|probe| probe.cmp(&region, text))
{
changed_regions.insert(i, region);
} }
.insert(text, &mut changed_regions);
} }
get_injections( get_injections(
config, config,
text, text,
&tree, tree.root_node_with_offset(start_byte, start_point),
registry, registry,
depth, depth,
start_byte,
Point::from_ts_point(start_point),
&changed_ranges, &changed_ranges,
&mut queue, &mut queue,
); );
@ -389,17 +382,16 @@ impl SyntaxSnapshot {
self.layers = layers; self.layers = layers;
} }
pub fn layers(&self, buffer: &BufferSnapshot) -> Vec<(&Grammar, &Tree, (usize, Point))> { pub fn layers(&self, buffer: &BufferSnapshot) -> Vec<(&Grammar, Node)> {
self.layers self.layers
.iter() .iter()
.filter_map(|layer| { .filter_map(|layer| {
if let Some(grammar) = &layer.language.grammar { if let Some(grammar) = &layer.language.grammar {
Some(( Some((
grammar.as_ref(), grammar.as_ref(),
&layer.tree, layer.tree.root_node_with_offset(
(
layer.range.start.to_offset(buffer), layer.range.start.to_offset(buffer),
layer.range.start.to_point(buffer), layer.range.start.to_point(buffer).to_ts_point(),
), ),
)) ))
} else { } else {
@ -413,7 +405,7 @@ impl SyntaxSnapshot {
&self, &self,
range: Range<T>, range: Range<T>,
buffer: &BufferSnapshot, buffer: &BufferSnapshot,
) -> Vec<(&Grammar, &Tree, (usize, Point))> { ) -> Vec<(&Grammar, Node)> {
let start = buffer.anchor_before(range.start.to_offset(buffer)); let start = buffer.anchor_before(range.start.to_offset(buffer));
let end = buffer.anchor_after(range.end.to_offset(buffer)); let end = buffer.anchor_after(range.end.to_offset(buffer));
@ -429,10 +421,9 @@ impl SyntaxSnapshot {
if let Some(grammar) = &layer.language.grammar { if let Some(grammar) = &layer.language.grammar {
result.push(( result.push((
grammar.as_ref(), grammar.as_ref(),
&layer.tree, layer.tree.root_node_with_offset(
(
layer.range.start.to_offset(buffer), layer.range.start.to_offset(buffer),
layer.range.start.to_point(buffer), layer.range.start.to_point(buffer).to_ts_point(),
), ),
)); ));
} }
@ -443,6 +434,38 @@ impl SyntaxSnapshot {
} }
} }
fn join_ranges(
a: impl Iterator<Item = Range<usize>>,
b: impl Iterator<Item = Range<usize>>,
) -> Vec<Range<usize>> {
let mut result = Vec::<Range<usize>>::new();
let mut a = a.peekable();
let mut b = b.peekable();
loop {
let range = match (a.peek(), b.peek()) {
(Some(range_a), Some(range_b)) => {
if range_a.start < range_b.start {
a.next().unwrap()
} else {
b.next().unwrap()
}
}
(None, Some(_)) => b.next().unwrap(),
(Some(_), None) => a.next().unwrap(),
(None, None) => break,
};
if let Some(last) = result.last_mut() {
if range.start <= last.end {
last.end = last.end.max(range.end);
continue;
}
}
result.push(range);
}
result
}
fn parse_text( fn parse_text(
grammar: &Grammar, grammar: &Grammar,
text: &Rope, text: &Rope,
@ -485,11 +508,9 @@ fn parse_text(
fn get_injections( fn get_injections(
config: &InjectionConfig, config: &InjectionConfig,
text: &BufferSnapshot, text: &BufferSnapshot,
tree: &Tree, node: Node,
language_registry: &LanguageRegistry, language_registry: &LanguageRegistry,
depth: usize, depth: usize,
start_byte: usize,
start_point: Point,
query_ranges: &[Range<usize>], query_ranges: &[Range<usize>],
queue: &mut BinaryHeap<ReparseStep>, queue: &mut BinaryHeap<ReparseStep>,
) -> bool { ) -> bool {
@ -498,21 +519,10 @@ fn get_injections(
let mut prev_match = None; let mut prev_match = None;
for query_range in query_ranges { for query_range in query_ranges {
query_cursor.set_byte_range(query_range.start..query_range.end); query_cursor.set_byte_range(query_range.start..query_range.end);
for mat in query_cursor.matches( for mat in query_cursor.matches(&config.query, node, TextProvider(text.as_rope())) {
&config.query,
tree.root_node(),
TextProvider(text.as_rope()),
) {
let content_ranges = mat let content_ranges = mat
.nodes_for_capture_index(config.content_capture_ix) .nodes_for_capture_index(config.content_capture_ix)
.map(|node| tree_sitter::Range { .map(|node| node.range())
start_byte: start_byte + node.start_byte(),
end_byte: start_byte + node.end_byte(),
start_point: (start_point + Point::from_ts_point(node.start_position()))
.to_ts_point(),
end_point: (start_point + Point::from_ts_point(node.end_position()))
.to_ts_point(),
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if content_ranges.is_empty() { if content_ranges.is_empty() {
continue; continue;
@ -534,12 +544,7 @@ fn get_injections(
.or_else(|| { .or_else(|| {
let ix = config.language_capture_ix?; let ix = config.language_capture_ix?;
let node = mat.nodes_for_capture_index(ix).next()?; let node = mat.nodes_for_capture_index(ix).next()?;
Some(Cow::Owned( Some(Cow::Owned(text.text_for_range(node.byte_range()).collect()))
text.text_for_range(
start_byte + node.start_byte()..start_byte + node.end_byte(),
)
.collect(),
))
}); });
if let Some(language_name) = language_name { if let Some(language_name) = language_name {
@ -566,9 +571,10 @@ fn layer_is_changed(
changed_regions: &[ChangedRegion], changed_regions: &[ChangedRegion],
) -> bool { ) -> bool {
changed_regions.iter().any(|region| { changed_regions.iter().any(|region| {
let same_depth = region.depth == layer.depth;
let is_before_layer = region.range.end.cmp(&layer.range.start, text).is_le(); let is_before_layer = region.range.end.cmp(&layer.range.start, text).is_le();
let is_after_layer = region.range.start.cmp(&layer.range.end, text).is_ge(); let is_after_layer = region.range.start.cmp(&layer.range.end, text).is_ge();
!is_before_layer && !is_after_layer same_depth && !is_before_layer && !is_after_layer
}) })
} }
@ -613,6 +619,12 @@ impl ReparseStep {
} }
impl ChangedRegion { impl ChangedRegion {
fn insert(self, text: &BufferSnapshot, set: &mut Vec<Self>) {
if let Err(ix) = set.binary_search_by(|probe| probe.cmp(&self, text)) {
set.insert(ix, self);
}
}
fn cmp(&self, other: &Self, buffer: &BufferSnapshot) -> Ordering { fn cmp(&self, other: &Self, buffer: &BufferSnapshot) -> Ordering {
let range_a = &self.range; let range_a = &self.range;
let range_b = &other.range; let range_b = &other.range;
@ -676,25 +688,11 @@ impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for DepthAndMaxP
impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for DepthAndRangeOrMaxPosition { impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for DepthAndRangeOrMaxPosition {
fn cmp(&self, cursor_location: &SyntaxLayerSummary, buffer: &BufferSnapshot) -> Ordering { fn cmp(&self, cursor_location: &SyntaxLayerSummary, buffer: &BufferSnapshot) -> Ordering {
let cmp = Ord::cmp(&self.0, &cursor_location.max_depth); if self.1.cmp(cursor_location, buffer).is_le() {
if cmp.is_ne() { return Ordering::Less;
return cmp; } else {
self.0.cmp(cursor_location, buffer)
} }
let cmp = self.2.cmp(&cursor_location.range.end, buffer);
if cmp.is_gt() {
return Ordering::Greater;
}
self.1
.start
.cmp(&cursor_location.last_layer_range.start, buffer)
.then_with(|| {
cursor_location
.last_layer_range
.end
.cmp(&self.1.end, buffer)
})
} }
} }
@ -827,37 +825,22 @@ mod tests {
} }
#[gpui::test] #[gpui::test]
fn test_syntax_map_edits() { fn test_typing_multiple_new_injections() {
let registry = Arc::new(LanguageRegistry::test()); let (buffer, syntax_map) = test_edit_sequence(&[
let language = Arc::new(rust_lang()); "fn a() { dbg }",
let mut syntax_map = SyntaxMap::new(); "fn a() { dbg«!» }",
syntax_map.set_language_registry(registry.clone()); "fn a() { dbg!«()» }",
registry.add(language.clone()); "fn a() { dbg!(«b») }",
"fn a() { dbg!(b«.») }",
let mut buffer = Buffer::new(0, 0, "".into()); "fn a() { dbg!(b.«c») }",
syntax_map.reparse(language.clone(), &buffer); "fn a() { dbg!(b.c«()») }",
"fn a() { dbg!(b.c(«vec»)) }",
edit_buffer_n( "fn a() { dbg!(b.c(vec«!»)) }",
&mut buffer, "fn a() { dbg!(b.c(vec!«[]»)) }",
&[ "fn a() { dbg!(b.c(vec![«d»])) }",
"«fn a() { dbg }»", "fn a() { dbg!(b.c(vec![d«.»])) }",
"fn a() { dbg«!» }", "fn a() { dbg!(b.c(vec![d.«e»])) }",
"fn a() { dbg!«()» }", ]);
"fn a() { dbg!(«b») }",
"fn a() { dbg!(b«.») }",
"fn a() { dbg!(b.«c») }",
"fn a() { dbg!(b.c«()») }",
"fn a() { dbg!(b.c(«vec»)) }",
"fn a() { dbg!(b.c(vec«!»)) }",
"fn a() { dbg!(b.c(vec!«[]»)) }",
"fn a() { dbg!(b.c(vec![«d»])) }",
"fn a() { dbg!(b.c(vec![d«.»])) }",
"fn a() { dbg!(b.c(vec![d.«e»])) }",
],
);
syntax_map.interpolate(&buffer);
syntax_map.reparse(language.clone(), &buffer);
assert_node_ranges( assert_node_ranges(
&syntax_map, &syntax_map,
@ -867,6 +850,163 @@ mod tests {
); );
} }
#[gpui::test]
fn test_pasting_new_injection_line_between_others() {
let (buffer, syntax_map) = test_edit_sequence(&[
"
fn a() {
b!(B {});
c!(C {});
d!(D {});
e!(E {});
f!(F {});
}
",
"
fn a() {
b!(B {});
c!(C {});
«g!(G {});
»d!(D {});
e!(E {});
f!(F {});
}
",
]);
assert_node_ranges(
&syntax_map,
&buffer,
"(struct_expression) @_",
"
fn a() {
b!(«B {}»);
c!(«C {}»);
g!(«G {}»);
d!(«D {}»);
e!(«E {}»);
f!(«F {}»);
}
",
);
}
#[gpui::test]
fn test_joining_injections_with_child_injections() {
let (buffer, syntax_map) = test_edit_sequence(&[
"
fn a() {
b!(
c![one.two.three],
d![four.five.six],
);
e!(
f![seven.eight],
);
}
",
"
fn a() {
b!(
c![one.two.three],
d![four.five.six],
ˇ f![seven.eight],
);
}
",
]);
assert_node_ranges(
&syntax_map,
&buffer,
"(field_identifier) @_",
"
fn a() {
b!(
c![one.«two».«three»],
d![four.«five».«six»],
f![seven.«eight»],
);
}
",
);
}
#[gpui::test]
fn test_editing_edges_of_injection() {
test_edit_sequence(&[
"
fn a() {
b!(c!())
}
",
"
fn a() {
«d»!(c!())
}
",
"
fn a() {
«e»d!(c!())
}
",
"
fn a() {
ed!«[»c!()«]»
}
",
]);
}
fn test_edit_sequence(steps: &[&str]) -> (Buffer, SyntaxMap) {
let registry = Arc::new(LanguageRegistry::test());
let language = Arc::new(rust_lang());
registry.add(language.clone());
let mut buffer = Buffer::new(0, 0, Default::default());
let mut mutated_syntax_map = SyntaxMap::new();
mutated_syntax_map.set_language_registry(registry.clone());
mutated_syntax_map.reparse(language.clone(), &buffer);
for (i, marked_string) in steps.into_iter().enumerate() {
edit_buffer(&mut buffer, &marked_string.unindent());
// Reparse the syntax map
mutated_syntax_map.interpolate(&buffer);
mutated_syntax_map.reparse(language.clone(), &buffer);
// Create a second syntax map from scratch
let mut reference_syntax_map = SyntaxMap::new();
reference_syntax_map.set_language_registry(registry.clone());
reference_syntax_map.reparse(language.clone(), &buffer);
// Compare the mutated syntax map to the new syntax map
let mutated_layers = mutated_syntax_map.layers(&buffer);
let reference_layers = reference_syntax_map.layers(&buffer);
assert_eq!(
mutated_layers.len(),
reference_layers.len(),
"wrong number of layers at step {i}"
);
for (edited_layer, reference_layer) in
mutated_layers.into_iter().zip(reference_layers.into_iter())
{
assert_eq!(
edited_layer.1.to_sexp(),
reference_layer.1.to_sexp(),
"different layer at step {i}"
);
assert_eq!(
edited_layer.1.range(),
reference_layer.1.range(),
"different layer at step {i}"
);
}
}
(buffer, mutated_syntax_map)
}
fn rust_lang() -> Language { fn rust_lang() -> Language {
Language::new( Language::new(
LanguageConfig { LanguageConfig {
@ -903,10 +1043,10 @@ mod tests {
expected_layers.len(), expected_layers.len(),
"wrong number of layers" "wrong number of layers"
); );
for (i, ((_, tree, _), expected_s_exp)) in for (i, ((_, node), expected_s_exp)) in
layers.iter().zip(expected_layers.iter()).enumerate() layers.iter().zip(expected_layers.iter()).enumerate()
{ {
let actual_s_exp = tree.root_node().to_sexp(); let actual_s_exp = node.to_sexp();
assert!( assert!(
string_contains_sequence( string_contains_sequence(
&actual_s_exp, &actual_s_exp,
@ -925,50 +1065,54 @@ mod tests {
) { ) {
let mut cursor = QueryCursorHandle::new(); let mut cursor = QueryCursorHandle::new();
let mut actual_ranges = Vec::<Range<usize>>::new(); let mut actual_ranges = Vec::<Range<usize>>::new();
for (grammar, tree, (start_byte, _)) in syntax_map.layers(buffer) { for (grammar, node) in syntax_map.layers(buffer) {
let query = Query::new(grammar.ts_language, query).unwrap(); let query = Query::new(grammar.ts_language, query).unwrap();
for (mat, ix) in for (mat, ix) in cursor.captures(&query, node, TextProvider(buffer.as_rope())) {
cursor.captures(&query, tree.root_node(), TextProvider(buffer.as_rope())) actual_ranges.push(mat.captures[ix].node.byte_range());
{
let range = mat.captures[ix].node.byte_range();
actual_ranges.push(start_byte + range.start..start_byte + range.end);
} }
} }
let (text, expected_ranges) = marked_text_ranges(marked_string, false); let (text, expected_ranges) = marked_text_ranges(&marked_string.unindent(), false);
assert_eq!(text, buffer.text()); assert_eq!(text, buffer.text());
assert_eq!(actual_ranges, expected_ranges); assert_eq!(actual_ranges, expected_ranges);
} }
fn edit_buffer_n(buffer: &mut Buffer, marked_strings: &[&str]) {
for marked_string in marked_strings {
edit_buffer(buffer, marked_string);
}
}
fn edit_buffer(buffer: &mut Buffer, marked_string: &str) { fn edit_buffer(buffer: &mut Buffer, marked_string: &str) {
let old_text = buffer.text(); let old_text = buffer.text();
let (new_text, mut ranges) = marked_text_ranges(marked_string, false); let (new_text, mut ranges) = marked_text_ranges(marked_string, false);
assert_eq!(ranges.len(), 1); if ranges.is_empty() {
ranges.push(0..new_text.len());
}
let inserted_range = ranges.pop().unwrap(); let mut delta = 0;
let inserted_text = new_text[inserted_range.clone()].to_string(); let mut edits = Vec::new();
let deleted_len = (inserted_range.len() as isize + old_text.len() as isize let mut ranges = ranges.into_iter().peekable();
- new_text.len() as isize) as usize;
let deleted_range = inserted_range.start..inserted_range.start + deleted_len; while let Some(inserted_range) = ranges.next() {
let old_start = (inserted_range.start as isize - delta) as usize;
let following_text = if let Some(next_range) = ranges.peek() {
&new_text[inserted_range.end..next_range.start]
} else {
&new_text[inserted_range.end..]
};
let inserted_len = inserted_range.len();
let deleted_len = old_text[old_start..]
.find(following_text)
.expect("invalid edit");
let old_range = old_start..old_start + deleted_len;
edits.push((old_range, new_text[inserted_range].to_string()));
delta += inserted_len as isize - deleted_len as isize;
}
assert_eq!( assert_eq!(
old_text[..deleted_range.start], old_text.len() as isize + delta,
new_text[..inserted_range.start], new_text.len() as isize,
"invalid edit", "invalid edit"
);
assert_eq!(
old_text[deleted_range.end..],
new_text[inserted_range.end..],
"invalid edit",
); );
buffer.edit([(deleted_range, inserted_text)]); buffer.edit(edits);
} }
pub fn string_contains_sequence(text: &str, parts: &[&str]) -> bool { pub fn string_contains_sequence(text: &str, parts: &[&str]) -> bool {