about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/ide-completion/src/completions/postfix.rs76
1 files changed, 62 insertions, 14 deletions
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index 5af44aa4b68..762d7cddb97 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -193,13 +193,21 @@ pub(crate) fn complete_postfix(
 }
 
 fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
-    if receiver_is_ambiguous_float_literal {
+    let text = if receiver_is_ambiguous_float_literal {
         let text = receiver.syntax().text();
         let without_dot = ..text.len() - TextSize::of('.');
         text.slice(without_dot).to_string()
     } else {
         receiver.to_string()
-    }
+    };
+
+    // The receiver texts should be interpreted as-is, as they are expected to be
+    // normal Rust expressions. We escape '\' and '$' so they don't get treated as
+    // snippet-specific constructs.
+    //
+    // Note that we don't need to escape the other characters that can be escaped,
+    // because they wouldn't be treated as snippet-specific constructs without '$'.
+    text.replace('\\', "\\\\").replace('$', "\\$")
 }
 
 fn include_references(initial_element: &ast::Expr) -> ast::Expr {
@@ -494,19 +502,21 @@ fn main() {
 
     #[test]
     fn custom_postfix_completion() {
+        let config = CompletionConfig {
+            snippets: vec![Snippet::new(
+                &[],
+                &["break".into()],
+                &["ControlFlow::Break(${receiver})".into()],
+                "",
+                &["core::ops::ControlFlow".into()],
+                crate::SnippetScope::Expr,
+            )
+            .unwrap()],
+            ..TEST_CONFIG
+        };
+
         check_edit_with_config(
-            CompletionConfig {
-                snippets: vec![Snippet::new(
-                    &[],
-                    &["break".into()],
-                    &["ControlFlow::Break(${receiver})".into()],
-                    "",
-                    &["core::ops::ControlFlow".into()],
-                    crate::SnippetScope::Expr,
-                )
-                .unwrap()],
-                ..TEST_CONFIG
-            },
+            config.clone(),
             "break",
             r#"
 //- minicore: try
@@ -518,6 +528,44 @@ use core::ops::ControlFlow;
 fn main() { ControlFlow::Break(42) }
 "#,
         );
+
+        check_edit_with_config(
+            config.clone(),
+            "break",
+            r#"
+//- minicore: try
+fn main() { '\\'.$0 }
+"#,
+            r#"
+use core::ops::ControlFlow;
+
+fn main() { ControlFlow::Break('\\\\') }
+"#,
+        );
+
+        check_edit_with_config(
+            config.clone(),
+            "break",
+            r#"
+//- minicore: try
+fn main() {
+    match true {
+        true => "${1:placeholder}",
+        false => "\$",
+    }.$0
+}
+"#,
+            r#"
+use core::ops::ControlFlow;
+
+fn main() {
+    ControlFlow::Break(match true {
+        true => "\${1:placeholder}",
+        false => "\\\$",
+    })
+}
+"#,
+        );
     }
 
     #[test]