Fix more bugs in syntax map interpolation

Max Brunsfeld created

Change summary

crates/language/src/syntax_map.rs | 179 ++++++++++++++++++++------------
1 file changed, 109 insertions(+), 70 deletions(-)

Detailed changes

crates/language/src/syntax_map.rs 🔗

@@ -106,96 +106,102 @@ impl SyntaxSnapshot {
         }
 
         let mut layers = SumTree::new();
-        let max_depth = self.layers.summary().max_depth;
+        let mut edits_for_depth = &edits[..];
         let mut cursor = self.layers.cursor::<SyntaxLayerSummary>();
-        cursor.next(&text);
+        cursor.next(text);
 
-        for depth in 0..=max_depth {
-            let mut edits = &edits[..];
-            if cursor.start().max_depth < depth {
+        'outer: loop {
+            let depth = cursor.end(text).max_depth;
+
+            // Preserve any layers at this depth that precede the first edit.
+            if let Some(first_edit) = edits_for_depth.first() {
+                let target = DepthAndMaxPosition(depth, text.anchor_before(first_edit.new.start.0));
+                if target.cmp(&cursor.start(), text).is_gt() {
+                    let slice = cursor.slice(&target, Bias::Left, text);
+                    layers.push_tree(slice, text);
+                }
+            }
+            // If this layer follows all of the edits, then preserve it and any
+            // subsequent layers at this same depth.
+            else {
                 layers.push_tree(
                     cursor.slice(
-                        &DepthAndRange(depth, Anchor::MIN..Anchor::MAX),
+                        &DepthAndRange(depth + 1, Anchor::MIN..Anchor::MAX),
                         Bias::Left,
                         text,
                     ),
                     text,
                 );
-            }
+                edits_for_depth = &edits[..];
+                continue;
+            };
+
+            let layer = if let Some(layer) = cursor.item() {
+                layer
+            } else {
+                break;
+            };
 
-            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
+            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;
+            let end_byte = layer_range.end.0;
+
+            // Ignore edits that end before the start of this layer, and don't consider them
+            // for any subsequent layers at this same depth.
+            loop {
+                if let Some(edit) = edits_for_depth.first() {
+                    if edit.new.end.0 < start_byte {
+                        edits_for_depth = &edits_for_depth[1..];
+                    } else {
+                        break;
+                    }
                 } else {
-                    break;
-                };
-                let target = DepthAndMaxPosition(depth, text.anchor_before(first_edit.new.start.0));
-                if target.cmp(&cursor.start(), text).is_gt() {
-                    layers.push_tree(cursor.slice(&target, Bias::Left, text), text);
+                    continue 'outer;
                 }
+            }
 
-                // 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 {
+            let mut layer = layer.clone();
+            for edit in edits_for_depth {
+                // Ignore any edits that follow this layer.
+                if edit.new.start.0 > end_byte {
                     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;
+                // Apply any edits that intersect this layer to the layer's syntax tree.
+                let tree_edit = if edit.new.start.0 >= start_byte {
+                    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(),
                     }
-
-                    // 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;
+                } else {
+                    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(),
                     }
+                };
 
-                    // Apply any edits that intersect this layer to the layer's syntax tree.
-                    let tree_edit = if edit.new.start.0 >= start_byte {
-                        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 {
-                        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(),
-                        }
-                    };
-
-                    layer.tree.edit(&tree_edit);
-                    if edit.new.start.0 < start_byte {
-                        break;
-                    }
+                layer.tree.edit(&tree_edit);
+                if edit.new.start.0 < start_byte {
+                    break;
                 }
-
-                layers.push(layer, text);
-                cursor.next(text);
             }
+
+            layers.push(layer, text);
+            cursor.next(text);
         }
 
         layers.push_tree(cursor.suffix(&text), &text);
@@ -958,6 +964,31 @@ mod tests {
         ]);
     }
 
+    #[gpui::test]
+    fn test_edits_preceding_and_intersecting_injection() {
+        test_edit_sequence(&[
+            //
+            "const aaaaaaaaaaaa: B = c!(d(e.f));",
+            "const aˇa: B = c!(d(eˇ));",
+        ]);
+    }
+
+    #[gpui::test]
+    fn test_non_local_changes_create_injections() {
+        test_edit_sequence(&[
+            "
+                // a! {
+                    static B: C = d;
+                // }
+            ",
+            "
+                ˇa! {
+                    static B: C = d;
+                ˇ}
+            ",
+        ]);
+    }
+
     fn test_edit_sequence(steps: &[&str]) -> (Buffer, SyntaxMap) {
         let registry = Arc::new(LanguageRegistry::test());
         let language = Arc::new(rust_lang());
@@ -1084,12 +1115,20 @@ mod tests {
             ranges.push(0..new_text.len());
         }
 
+        assert_eq!(
+            old_text[..ranges[0].start],
+            new_text[..ranges[0].start],
+            "invalid edit"
+        );
+
         let mut delta = 0;
         let mut edits = Vec::new();
         let mut ranges = ranges.into_iter().peekable();
 
         while let Some(inserted_range) = ranges.next() {
-            let old_start = (inserted_range.start as isize - delta) as usize;
+            let new_start = inserted_range.start;
+            let old_start = (new_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 {