Skip to content

Commit 491e6ea

Browse files
committed
fix: escape ${receiver} when completing with custom snippets
1 parent b74e96f commit 491e6ea

File tree

1 file changed

+62
-14
lines changed

1 file changed

+62
-14
lines changed

crates/ide-completion/src/completions/postfix.rs

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,21 @@ pub(crate) fn complete_postfix(
193193
}
194194

195195
fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
196-
if receiver_is_ambiguous_float_literal {
196+
let text = if receiver_is_ambiguous_float_literal {
197197
let text = receiver.syntax().text();
198198
let without_dot = ..text.len() - TextSize::of('.');
199199
text.slice(without_dot).to_string()
200200
} else {
201201
receiver.to_string()
202-
}
202+
};
203+
204+
// The receiver texts should be interpreted as-is, as they are expected to be
205+
// normal Rust expressions. We escape '\' and '$' so they don't get treated as
206+
// snippet-specific constructs.
207+
//
208+
// Note that we don't need to escape the other characters that can be escaped,
209+
// because they wouldn't be treated as snippet-specific constructs without '$'.
210+
text.replace('\\', "\\\\").replace('$', "\\$")
203211
}
204212

205213
fn include_references(initial_element: &ast::Expr) -> ast::Expr {
@@ -494,19 +502,21 @@ fn main() {
494502

495503
#[test]
496504
fn custom_postfix_completion() {
505+
let config = CompletionConfig {
506+
snippets: vec![Snippet::new(
507+
&[],
508+
&["break".into()],
509+
&["ControlFlow::Break(${receiver})".into()],
510+
"",
511+
&["core::ops::ControlFlow".into()],
512+
crate::SnippetScope::Expr,
513+
)
514+
.unwrap()],
515+
..TEST_CONFIG
516+
};
517+
497518
check_edit_with_config(
498-
CompletionConfig {
499-
snippets: vec![Snippet::new(
500-
&[],
501-
&["break".into()],
502-
&["ControlFlow::Break(${receiver})".into()],
503-
"",
504-
&["core::ops::ControlFlow".into()],
505-
crate::SnippetScope::Expr,
506-
)
507-
.unwrap()],
508-
..TEST_CONFIG
509-
},
519+
config.clone(),
510520
"break",
511521
r#"
512522
//- minicore: try
@@ -516,6 +526,44 @@ fn main() { 42.$0 }
516526
use core::ops::ControlFlow;
517527
518528
fn main() { ControlFlow::Break(42) }
529+
"#,
530+
);
531+
532+
check_edit_with_config(
533+
config.clone(),
534+
"break",
535+
r#"
536+
//- minicore: try
537+
fn main() { '\\'.$0 }
538+
"#,
539+
r#"
540+
use core::ops::ControlFlow;
541+
542+
fn main() { ControlFlow::Break('\\\\') }
543+
"#,
544+
);
545+
546+
check_edit_with_config(
547+
config.clone(),
548+
"break",
549+
r#"
550+
//- minicore: try
551+
fn main() {
552+
match true {
553+
true => "${1:placeholder}",
554+
false => "\$",
555+
}.$0
556+
}
557+
"#,
558+
r#"
559+
use core::ops::ControlFlow;
560+
561+
fn main() {
562+
ControlFlow::Break(match true {
563+
true => "\${1:placeholder}",
564+
false => "\\\$",
565+
})
566+
}
519567
"#,
520568
);
521569
}

0 commit comments

Comments
 (0)