diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 01ff0e50a1..41ea579047 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1,11 +1,13 @@ use crate::{ - Grammar, Language, LanguageRegistry, QueryCursorHandle, TextProvider, ToTreeSitterPoint, + Grammar, InjectionConfig, Language, LanguageRegistry, QueryCursorHandle, TextProvider, + ToTreeSitterPoint, }; -use collections::VecDeque; -use gpui::executor::Background; -use std::{borrow::Cow, cell::RefCell, cmp::Ordering, ops::Range, sync::Arc}; -use sum_tree::{SeekTarget, SumTree}; -use text::{Anchor, BufferSnapshot, Point, Rope, ToOffset}; +use collections::HashMap; +use std::{ + borrow::Cow, cell::RefCell, cmp::Ordering, collections::BinaryHeap, ops::Range, sync::Arc, +}; +use sum_tree::{Bias, SeekTarget, SumTree}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset}; use tree_sitter::{Parser, Tree}; use util::post_inc; @@ -15,175 +17,399 @@ thread_local! { #[derive(Default)] pub struct SyntaxMap { - next_layer_id: usize, - snapshot: SyntaxMapSnapshot, + version: clock::Global, + snapshot: SyntaxSnapshot, + language_registry: Option>, } #[derive(Clone, Default)] -pub struct SyntaxMapSnapshot { - version: clock::Global, +pub struct SyntaxSnapshot { layers: SumTree, } #[derive(Clone)] struct SyntaxLayer { - id: usize, - parent_id: Option, - range: SyntaxLayerRange, + depth: usize, + range: Range, tree: tree_sitter::Tree, language: Arc, } #[derive(Debug, Clone)] struct SyntaxLayerSummary { + max_depth: usize, range: Range, last_layer_range: Range, } #[derive(Clone, Debug)] -struct SyntaxLayerRange(Range); +struct Depth(usize); + +#[derive(Clone, Debug)] +struct MaxPosition(Anchor); + +enum ReparseStep { + CreateLayer { + depth: usize, + language: Arc, + ranges: Vec, + }, + EnterChangedRange { + id: usize, + depth: usize, + range: Range, + }, + LeaveChangedRange { + id: usize, + depth: usize, + range: Range, + }, +} impl SyntaxMap { - pub fn new( - executor: Arc, - registry: Arc, - language: Arc, - text: BufferSnapshot, - prev_set: Option, - ) -> Self { - let mut next_layer_id = 0; - let mut layers = Vec::new(); - let mut injections = VecDeque::<(Option, _, Vec)>::new(); - - injections.push_back((None, language, vec![])); - while let Some((parent_id, language, ranges)) = injections.pop_front() { - if let Some(grammar) = &language.grammar.as_deref() { - let id = post_inc(&mut next_layer_id); - let range = if let Some((first, last)) = ranges.first().zip(ranges.last()) { - text.anchor_before(first.start_byte)..text.anchor_after(last.end_byte) - } else { - Anchor::MIN..Anchor::MAX - }; - let tree = Self::parse_text(grammar, text.as_rope(), None, ranges); - Self::get_injections(grammar, &text, &tree, id, ®istry, &mut injections); - layers.push(SyntaxLayer { - id, - parent_id, - range: SyntaxLayerRange(range), - tree, - language, - }); - } - } - - layers.sort_unstable_by(|a, b| SeekTarget::cmp(&a.range, &b.range, &text)); - - Self { - next_layer_id, - snapshot: SyntaxMapSnapshot { - layers: SumTree::from_iter(layers, &text), - version: text.version, - }, - } + pub fn new() -> Self { + Self::default() } - pub fn snapshot(&self) -> SyntaxMapSnapshot { + pub fn set_language_registry(&mut self, registry: Arc) { + self.language_registry = Some(registry); + } + + pub fn snapshot(&self) -> SyntaxSnapshot { self.snapshot.clone() } - fn interpolate(&mut self, text: &BufferSnapshot) { + pub fn interpolate(&mut self, text: &BufferSnapshot) { + self.snapshot.interpolate(&self.version, text); + self.version = text.version.clone(); + } + + pub fn reparse(&mut self, language: Arc, text: &BufferSnapshot) { + self.version = text.version.clone(); + self.snapshot + .reparse(self.language_registry.clone(), language, text); + } +} + +// 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 { + pub fn interpolate(&mut self, current_version: &clock::Global, text: &BufferSnapshot) { let edits = text - .edits_since::<(Point, usize)>(&self.version) - .map(|edit| { - let (lines, bytes) = edit.flatten(); - tree_sitter::InputEdit { - start_byte: bytes.new.start, - old_end_byte: bytes.new.start + bytes.old.len(), - new_end_byte: bytes.new.end, - start_position: lines.new.start.to_ts_point(), - old_end_position: (lines.new.start + (lines.old.end - lines.old.start)) - .to_ts_point(), - new_end_position: lines.new.end.to_ts_point(), - } - }) + .edits_since::<(usize, Point)>(¤t_version) .collect::>(); if edits.is_empty() { return; } + + let mut layers = SumTree::new(); + let max_depth = self.layers.summary().max_depth; + let mut cursor = self.layers.cursor::(); + cursor.next(&text); + + for depth in 0..max_depth { + let mut edits = &edits[..]; + layers.push_tree(cursor.slice(&Depth(depth), Bias::Left, text), text); + + while let Some(layer) = cursor.item() { + let mut endpoints = text.summaries_for_anchors::<(usize, Point), _>([ + &layer.range.start, + &layer.range.end, + ]); + let layer_range = endpoints.next().unwrap()..endpoints.next().unwrap(); + let start_byte = layer_range.start.0; + let start_point = layer_range.start.1; + + // Preserve any layers at this depth that precede the first edit. + let first_edit = if let Some(edit) = edits.first() { + edit + } else { + break; + }; + if first_edit.new.start.0 > layer_range.end.0 { + layers.push_tree( + cursor.slice( + &( + Depth(depth), + MaxPosition(text.anchor_before(first_edit.new.start.0)), + ), + Bias::Left, + text, + ), + text, + ); + continue; + } + + // Preserve any layers at this depth that follow the last edit. + let last_edit = edits.last().unwrap(); + if last_edit.new.end.0 < layer_range.start.0 { + break; + } + + let mut layer = layer.clone(); + for (i, edit) in edits.iter().enumerate().rev() { + // Ignore any edits that start after the end of this layer. + if edit.new.start.0 > layer_range.end.0 { + continue; + } + + // Ignore edits that end before the start of this layer, and don't consider them + // for any subsequent layers at this same depth. + if edit.new.end.0 <= start_byte { + edits = &edits[i + 1..]; + break; + } + + // Apply any edits that intersect this layer to the layer's syntax tree. + if edit.new.start.0 >= start_byte { + layer.tree.edit(&tree_sitter::InputEdit { + start_byte: edit.new.start.0 - start_byte, + old_end_byte: edit.new.start.0 - start_byte + + (edit.old.end.0 - edit.old.start.0), + new_end_byte: edit.new.end.0 - start_byte, + start_position: (edit.new.start.1 - start_point).to_ts_point(), + old_end_position: (edit.new.start.1 - start_point + + (edit.old.end.1 - edit.old.start.1)) + .to_ts_point(), + new_end_position: (edit.new.end.1 - start_point).to_ts_point(), + }); + } else { + layer.tree.edit(&tree_sitter::InputEdit { + start_byte: 0, + old_end_byte: edit.new.end.0 - start_byte, + new_end_byte: 0, + start_position: Default::default(), + old_end_position: (edit.new.end.1 - start_point).to_ts_point(), + new_end_position: Default::default(), + }); + break; + } + } + + layers.push(layer, text); + cursor.next(text); + } + } + + layers.push_tree(cursor.suffix(&text), &text); + drop(cursor); + self.layers = layers; } - fn get_injections( - grammar: &Grammar, + pub fn reparse( + &mut self, + registry: Option>, + language: Arc, text: &BufferSnapshot, - tree: &Tree, - id: usize, - registry: &Arc, - output: &mut VecDeque<(Option, Arc, Vec)>, ) { - let config = if let Some(config) = &grammar.injection_config { - config - } else { - return; - }; + let mut cursor = self.layers.cursor::(); + cursor.next(&text); + let mut layers = SumTree::new(); - let mut query_cursor = QueryCursorHandle::new(); - for mat in query_cursor.matches( - &config.query, - tree.root_node(), - TextProvider(text.as_rope()), - ) { - let content_ranges = mat - .nodes_for_capture_index(config.content_capture_ix) - .map(|node| node.range()) - .collect::>(); - if content_ranges.is_empty() { - continue; - } - let language_name = config.languages_by_pattern_ix[mat.pattern_index] - .as_ref() - .map(|s| Cow::Borrowed(s.as_ref())) - .or_else(|| { - let ix = config.language_capture_ix?; - let node = mat.nodes_for_capture_index(ix).next()?; - Some(Cow::Owned(text.text_for_range(node.byte_range()).collect())) - }); - if let Some(language_name) = language_name { - if let Some(language) = registry.get_language(language_name.as_ref()) { - output.push_back((Some(id), language, content_ranges)) + let mut next_change_id = 0; + let mut current_changes = HashMap::default(); + let mut queue = BinaryHeap::new(); + queue.push(ReparseStep::CreateLayer { + depth: 0, + language: language.clone(), + ranges: Vec::new(), + }); + + while let Some(step) = queue.pop() { + match step { + ReparseStep::CreateLayer { + depth, + language, + ranges, + } => { + let range; + let start_point; + let start_byte; + let end_byte; + if let Some((first, last)) = ranges.first().zip(ranges.last()) { + start_point = first.start_point; + start_byte = first.start_byte; + end_byte = last.end_byte; + range = text.anchor_before(start_byte)..text.anchor_after(end_byte); + } else { + start_point = Point::zero().to_ts_point(); + start_byte = 0; + end_byte = text.len(); + range = Anchor::MIN..Anchor::MAX; + }; + + let target = (Depth(depth), range.clone()); + if target.cmp(cursor.start(), &text).is_gt() { + if current_changes.is_empty() { + let slice = cursor.slice(&target, Bias::Left, text); + layers.push_tree(slice, &text); + } else { + while let Some(layer) = cursor.item() { + if layer.depth > depth + || layer.depth == depth + && layer.range.start.cmp(&range.end, text).is_ge() + { + break; + } + if !layer_is_changed(layer, text, ¤t_changes) { + layers.push(layer.clone(), text); + } + cursor.next(text); + } + } + } + + let mut old_layer = cursor.item(); + if let Some(layer) = old_layer { + if layer.range.to_offset(text) == (start_byte..end_byte) { + cursor.next(&text); + } else { + old_layer = None; + } + } + + let grammar = if let Some(grammar) = language.grammar.as_deref() { + grammar + } else { + continue; + }; + + let tree; + let changed_ranges; + if let Some(old_layer) = old_layer { + tree = parse_text( + grammar, + text.as_rope(), + Some(old_layer.tree.clone()), + ranges, + ); + + changed_ranges = old_layer + .tree + .changed_ranges(&tree) + .map(|r| r.start_byte..r.end_byte) + .collect(); + } else { + tree = parse_text(grammar, text.as_rope(), None, ranges); + changed_ranges = vec![0..end_byte - start_byte]; + } + + layers.push( + SyntaxLayer { + depth, + range, + tree: tree.clone(), + language: language.clone(), + }, + &text, + ); + + if let (Some((config, registry)), false) = ( + grammar.injection_config.as_ref().zip(registry.as_ref()), + changed_ranges.is_empty(), + ) { + let depth = depth + 1; + queue.extend(changed_ranges.iter().flat_map(|range| { + let id = post_inc(&mut next_change_id); + let range = start_byte + range.start..start_byte + range.end; + [ + ReparseStep::EnterChangedRange { + id, + depth, + range: range.clone(), + }, + ReparseStep::LeaveChangedRange { + id, + depth, + range: range.clone(), + }, + ] + })); + + get_injections( + config, + text, + &tree, + registry, + depth, + start_byte, + Point::from_ts_point(start_point), + &changed_ranges, + &mut queue, + ); + } + } + ReparseStep::EnterChangedRange { id, depth, range } => { + let range = text.anchor_before(range.start)..text.anchor_after(range.end); + if current_changes.is_empty() { + let target = (Depth(depth), range.start..Anchor::MAX); + let slice = cursor.slice(&target, Bias::Left, text); + layers.push_tree(slice, text); + } else { + while let Some(layer) = cursor.item() { + if layer.depth > depth + || layer.depth == depth + && layer.range.end.cmp(&range.start, text).is_gt() + { + break; + } + if !layer_is_changed(layer, text, ¤t_changes) { + layers.push(layer.clone(), text); + } + cursor.next(text); + } + } + + current_changes.insert(id, range); + } + ReparseStep::LeaveChangedRange { id, depth, range } => { + let range = text.anchor_before(range.start)..text.anchor_after(range.end); + while let Some(layer) = cursor.item() { + if layer.depth > depth + || layer.depth == depth + && layer.range.start.cmp(&range.end, text).is_ge() + { + break; + } + if !layer_is_changed(layer, text, ¤t_changes) { + layers.push(layer.clone(), text); + } + cursor.next(text); + } + + current_changes.remove(&id); } } } + + let slice = cursor.suffix(&text); + layers.push_tree(slice, &text); + drop(cursor); + self.layers = layers; } - fn parse_text( - grammar: &Grammar, - text: &Rope, - old_tree: Option, - ranges: Vec, - ) -> Tree { - PARSER.with(|parser| { - let mut parser = parser.borrow_mut(); - let mut chunks = text.chunks_in_range(0..text.len()); - parser - .set_included_ranges(&ranges) - .expect("overlapping ranges"); - parser - .set_language(grammar.ts_language) - .expect("incompatible grammar"); - parser - .parse_with( - &mut move |offset, _| { - chunks.seek(offset); - chunks.next().unwrap_or("").as_bytes() - }, - old_tree.as_ref(), - ) - .expect("invalid language") - }) - } -} - -impl SyntaxMapSnapshot { pub fn layers_for_range<'a, T: ToOffset>( &self, range: Range, @@ -211,17 +437,184 @@ impl SyntaxMapSnapshot { } } +fn parse_text( + grammar: &Grammar, + text: &Rope, + old_tree: Option, + mut ranges: Vec, +) -> Tree { + let (start_byte, start_point) = ranges + .first() + .map(|range| (range.start_byte, Point::from_ts_point(range.start_point))) + .unwrap_or_default(); + + for range in &mut ranges { + range.start_byte -= start_byte; + range.end_byte -= start_byte; + range.start_point = (Point::from_ts_point(range.start_point) - start_point).to_ts_point(); + range.end_point = (Point::from_ts_point(range.end_point) - start_point).to_ts_point(); + } + + PARSER.with(|parser| { + let mut parser = parser.borrow_mut(); + let mut chunks = text.chunks_in_range(start_byte..text.len()); + parser + .set_included_ranges(&ranges) + .expect("overlapping ranges"); + parser + .set_language(grammar.ts_language) + .expect("incompatible grammar"); + parser + .parse_with( + &mut move |offset, _| { + chunks.seek(start_byte + offset); + chunks.next().unwrap_or("").as_bytes() + }, + old_tree.as_ref(), + ) + .expect("invalid language") + }) +} + +fn get_injections( + config: &InjectionConfig, + text: &BufferSnapshot, + tree: &Tree, + language_registry: &LanguageRegistry, + depth: usize, + start_byte: usize, + start_point: Point, + query_ranges: &[Range], + stack: &mut BinaryHeap, +) -> bool { + let mut result = false; + let mut query_cursor = QueryCursorHandle::new(); + let mut prev_match = None; + for query_range in query_ranges { + query_cursor.set_byte_range(query_range.start..query_range.end); + for mat in query_cursor.matches( + &config.query, + tree.root_node(), + TextProvider(text.as_rope()), + ) { + let content_ranges = mat + .nodes_for_capture_index(config.content_capture_ix) + .map(|node| tree_sitter::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::>(); + if content_ranges.is_empty() { + continue; + } + + // Avoid duplicate matches if two changed ranges intersect the same injection. + let content_range = + content_ranges.first().unwrap().start_byte..content_ranges.last().unwrap().end_byte; + if let Some((last_pattern_ix, last_range)) = &prev_match { + if mat.pattern_index == *last_pattern_ix && content_range == *last_range { + continue; + } + } + prev_match = Some((mat.pattern_index, content_range)); + + let language_name = config.languages_by_pattern_ix[mat.pattern_index] + .as_ref() + .map(|s| Cow::Borrowed(s.as_ref())) + .or_else(|| { + let ix = config.language_capture_ix?; + let node = mat.nodes_for_capture_index(ix).next()?; + Some(Cow::Owned( + 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) = language_registry.get_language(language_name.as_ref()) { + result = true; + stack.push(ReparseStep::CreateLayer { + depth, + language, + ranges: content_ranges, + }) + } + } + } + } + result +} + +fn layer_is_changed( + layer: &SyntaxLayer, + text: &BufferSnapshot, + changed_ranges: &HashMap>, +) -> bool { + changed_ranges.values().any(|range| { + let is_before_layer = range.end.cmp(&layer.range.start, text).is_le(); + let is_after_layer = range.start.cmp(&layer.range.end, text).is_ge(); + !is_before_layer && !is_after_layer + }) +} + impl std::ops::Deref for SyntaxMap { - type Target = SyntaxMapSnapshot; + type Target = SyntaxSnapshot; fn deref(&self) -> &Self::Target { &self.snapshot } } +impl ReparseStep { + fn sort_key(&self) -> (usize, Range) { + match self { + ReparseStep::CreateLayer { depth, ranges, .. } => ( + *depth, + ranges.first().map_or(0, |r| r.start_byte) + ..ranges.last().map_or(usize::MAX, |r| r.end_byte), + ), + ReparseStep::EnterChangedRange { depth, range, .. } => { + (*depth, range.start..usize::MAX) + } + ReparseStep::LeaveChangedRange { depth, range, .. } => (*depth, range.end..usize::MAX), + } + } +} + +impl PartialEq for ReparseStep { + fn eq(&self, _: &Self) -> bool { + false + } +} + +impl Eq for ReparseStep {} + +impl PartialOrd for ReparseStep { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +impl Ord for ReparseStep { + fn cmp(&self, other: &Self) -> Ordering { + let (depth_a, range_a) = self.sort_key(); + let (depth_b, range_b) = other.sort_key(); + Ord::cmp(&depth_b, &depth_a) + .then_with(|| Ord::cmp(&range_b.start, &range_a.start)) + .then_with(|| Ord::cmp(&range_a.end, &range_b.end)) + } +} + impl Default for SyntaxLayerSummary { fn default() -> Self { Self { + max_depth: 0, range: Anchor::MAX..Anchor::MIN, last_layer_range: Anchor::MIN..Anchor::MAX, } @@ -232,38 +625,49 @@ impl sum_tree::Summary for SyntaxLayerSummary { type Context = BufferSnapshot; fn add_summary(&mut self, other: &Self, buffer: &Self::Context) { - if other.range.start.cmp(&self.range.start, buffer).is_lt() { - self.range.start = other.range.start; + if other.max_depth > self.max_depth { + *self = other.clone(); + } else { + if other.range.start.cmp(&self.range.start, buffer).is_lt() { + self.range.start = other.range.start; + } + if other.range.end.cmp(&self.range.end, buffer).is_gt() { + self.range.end = other.range.end; + } + self.last_layer_range = other.last_layer_range.clone(); } - if other.range.end.cmp(&self.range.end, buffer).is_gt() { - self.range.end = other.range.end; - } - self.last_layer_range = other.last_layer_range.clone(); } } -impl Default for SyntaxLayerRange { - fn default() -> Self { - Self(Anchor::MIN..Anchor::MAX) +impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for Depth { + fn cmp(&self, cursor_location: &SyntaxLayerSummary, _: &BufferSnapshot) -> Ordering { + Ord::cmp(&self.0, &cursor_location.max_depth) } } -impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerRange> for SyntaxLayerRange { - fn cmp(&self, cursor_location: &Self, buffer: &BufferSnapshot) -> Ordering { +impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for (Depth, MaxPosition) { + fn cmp(&self, cursor_location: &SyntaxLayerSummary, text: &BufferSnapshot) -> Ordering { self.0 - .start - .cmp(&cursor_location.0.start, buffer) - .then_with(|| cursor_location.0.end.cmp(&self.0.end, buffer)) + .cmp(&cursor_location, text) + .then_with(|| (self.1).0.cmp(&cursor_location.range.end, text)) } } -impl<'a> sum_tree::Dimension<'a, SyntaxLayerSummary> for SyntaxLayerRange { - fn add_summary( - &mut self, - summary: &'a SyntaxLayerSummary, - _: &::Context, - ) { - self.0 = summary.last_layer_range.clone(); +impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for (Depth, Range) { + fn cmp(&self, cursor_location: &SyntaxLayerSummary, buffer: &BufferSnapshot) -> Ordering { + self.0 + .cmp(&cursor_location, buffer) + .then_with(|| { + self.1 + .start + .cmp(&cursor_location.last_layer_range.start, buffer) + }) + .then_with(|| { + cursor_location + .last_layer_range + .end + .cmp(&self.1.end, buffer) + }) } } @@ -272,8 +676,9 @@ impl sum_tree::Item for SyntaxLayer { fn summary(&self) -> Self::Summary { SyntaxLayerSummary { - range: self.range.0.clone(), - last_layer_range: self.range.0.clone(), + max_depth: self.depth, + range: self.range.clone(), + last_layer_range: self.range.clone(), } } } @@ -281,8 +686,7 @@ impl sum_tree::Item for SyntaxLayer { impl std::fmt::Debug for SyntaxLayer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("SyntaxLayer") - .field("id", &self.id) - .field("parent_id", &self.parent_id) + .field("depth", &self.depth) .field("range", &self.range) .field("tree", &self.tree) .finish() @@ -293,13 +697,16 @@ impl std::fmt::Debug for SyntaxLayer { mod tests { use super::*; use crate::LanguageConfig; - use gpui::MutableAppContext; use text::{Buffer, Point}; use unindent::Unindent as _; #[gpui::test] - fn test_syntax_map(cx: &mut MutableAppContext) { - let buffer = Buffer::new( + fn test_syntax_map_layers_for_range() { + let registry = Arc::new(LanguageRegistry::test()); + let language = Arc::new(rust_lang()); + registry.add(language.clone()); + + let mut buffer = Buffer::new( 0, 0, r#" @@ -314,57 +721,80 @@ mod tests { .unindent(), ); - let executor = cx.background().clone(); - let registry = Arc::new(LanguageRegistry::test()); - let language = Arc::new(rust_lang()); - let snapshot = buffer.snapshot(); - registry.add(language.clone()); + let mut syntax_map = SyntaxMap::new(); + syntax_map.set_language_registry(registry.clone()); + syntax_map.reparse(language.clone(), &buffer); - let syntax_map = SyntaxMap::new(executor, registry, language, snapshot.clone(), None); - - let layers = syntax_map.layers_for_range(Point::new(0, 0)..Point::new(0, 1), &snapshot); - assert_layers( - &layers, - &["(source_file (function_item name: (identifier)..."], - ); - - let layers = syntax_map.layers_for_range(Point::new(2, 0)..Point::new(2, 0), &snapshot); - assert_layers( - &layers, + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(2, 0)..Point::new(2, 0), &[ "...(function_item ... (block (expression_statement (macro_invocation...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", ], ); - - let layers = syntax_map.layers_for_range(Point::new(2, 14)..Point::new(2, 16), &snapshot); - assert_layers( - &layers, + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(2, 14)..Point::new(2, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", "...(array_expression (struct_expression ...", ], ); - - let layers = syntax_map.layers_for_range(Point::new(3, 14)..Point::new(3, 16), &snapshot); - assert_layers( - &layers, + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(3, 14)..Point::new(3, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", "...(array_expression (field_expression ...", ], ); - - let layers = syntax_map.layers_for_range(Point::new(5, 12)..Point::new(5, 16), &snapshot); - assert_layers( - &layers, + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(5, 12)..Point::new(5, 16), &[ "...(function_item ...", "...(call_expression ... (arguments (closure_expression ...", ], ); + + // Replace a vec! macro invocation with a plain slice, removing a syntactic layer. + let macro_name_range = range_for_text(&buffer, "vec!"); + buffer.edit([(macro_name_range, "&")]); + syntax_map.interpolate(&buffer); + syntax_map.reparse(language.clone(), &buffer); + + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(2, 14)..Point::new(2, 16), + &[ + "...(function_item ...", + "...(tuple_expression (call_expression ... arguments: (arguments (reference_expression value: (array_expression...", + ], + ); + + // Put the vec! macro back, adding back the syntactic layer. + buffer.undo(); + syntax_map.interpolate(&buffer); + syntax_map.reparse(language.clone(), &buffer); + + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(2, 14)..Point::new(2, 16), + &[ + "...(function_item ...", + "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", + "...(array_expression (struct_expression ...", + ], + ); } fn rust_lang() -> Language { @@ -386,7 +816,18 @@ mod tests { .unwrap() } - fn assert_layers(layers: &[(Tree, &Grammar)], expected_layers: &[&str]) { + fn range_for_text(buffer: &Buffer, text: &str) -> Range { + let start = buffer.as_rope().to_string().find(text).unwrap(); + start..start + text.len() + } + + fn assert_layers_for_range( + syntax_map: &SyntaxMap, + buffer: &BufferSnapshot, + range: Range, + expected_layers: &[&str], + ) { + let layers = syntax_map.layers_for_range(range, &buffer); assert_eq!( layers.len(), expected_layers.len(),