about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs24
1 files changed, 18 insertions, 6 deletions
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index 3d3bb8e83da..6d4bae5206b 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -1000,23 +1000,35 @@ fn merge_text_and_snippet_edits(
 
             let mut new_text = current_indel.insert;
 
-            // escape out snippet text
-            stdx::replace(&mut new_text, '\\', r"\\");
-            stdx::replace(&mut new_text, '$', r"\$");
+            // find which snippet bits need to be escaped
+            let escape_places =
+                new_text.rmatch_indices(['\\', '$']).map(|(insert, _)| insert).collect_vec();
+            let mut escape_places = escape_places.into_iter().peekable();
+            let mut escape_prior_bits = |new_text: &mut String, up_to: usize| {
+                for before in escape_places.peeking_take_while(|insert| *insert >= up_to) {
+                    new_text.insert(before, '\\');
+                }
+            };
 
-            // ...and apply!
+            // insert snippets, and escaping any needed bits along the way
             for (index, range) in all_snippets.iter().rev() {
-                let start = (range.start() - new_range.start()).into();
-                let end = (range.end() - new_range.start()).into();
+                let text_range = range - new_range.start();
+                let (start, end) = (text_range.start().into(), text_range.end().into());
 
                 if range.is_empty() {
+                    escape_prior_bits(&mut new_text, start);
                     new_text.insert_str(start, &format!("${index}"));
                 } else {
+                    escape_prior_bits(&mut new_text, end);
                     new_text.insert(end, '}');
+                    escape_prior_bits(&mut new_text, start);
                     new_text.insert_str(start, &format!("${{{index}:"));
                 }
             }
 
+            // escape any remaining bits
+            escape_prior_bits(&mut new_text, 0);
+
             edits.push(snippet_text_edit(
                 line_index,
                 true,