//! FIXME: write short doc here
-use ra_syntax::{ast::AstNode, TextRange, TextUnit};
+use ra_syntax::{
+ ast::{self, AstNode},
+ TextRange, TextUnit,
+};
use ra_text_edit::TextEdit;
use crate::{
None => return,
};
- let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
- let text = dot_receiver.syntax().text();
- let without_dot = ..text.len() - TextUnit::of_char('.');
- text.slice(without_dot).to_string()
- } else {
- dot_receiver.syntax().text().to_string()
- };
+ let receiver_text =
+ get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
Some(it) => it,
};
if receiver_ty.is_bool() || receiver_ty.is_unknown() {
- postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
- .add_to(acc);
postfix_snippet(
ctx,
+ &dot_receiver,
+ "if",
+ "if expr {}",
+ &format!("if {} {{$0}}", receiver_text),
+ )
+ .add_to(acc);
+ postfix_snippet(
+ ctx,
+ &dot_receiver,
"while",
"while expr {}",
&format!("while {} {{\n$0\n}}", receiver_text),
.add_to(acc);
}
- postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
+ // !&&&42 is a compiler error, ergo process it before considering the references
+ postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
- postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
- postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);
+ postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
+ postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
+ .add_to(acc);
+
+ // The rest of the postfix completions create an expression that moves an argument,
+ // so it's better to consider references now to avoid breaking the compilation
+ let dot_receiver = include_references(dot_receiver);
+ let receiver_text =
+ get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
postfix_snippet(
ctx,
+ &dot_receiver,
"match",
"match expr {}",
&format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text),
)
.add_to(acc);
- postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);
+ postfix_snippet(
+ ctx,
+ &dot_receiver,
+ "box",
+ "Box::new(expr)",
+ &format!("Box::new({})", receiver_text),
+ )
+ .add_to(acc);
- postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
+ postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text))
.add_to(acc);
}
-fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
+fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
+ if receiver_is_ambiguous_float_literal {
+ let text = receiver.syntax().text();
+ let without_dot = ..text.len() - TextUnit::of_char('.');
+ text.slice(without_dot).to_string()
+ } else {
+ receiver.to_string()
+ }
+}
+
+fn include_references(initial_element: &ast::Expr) -> ast::Expr {
+ let mut resulting_element = initial_element.clone();
+ while let Some(parent_ref_element) =
+ resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
+ {
+ resulting_element = ast::Expr::from(parent_ref_element);
+ }
+ resulting_element
+}
+
+fn postfix_snippet(
+ ctx: &CompletionContext,
+ receiver: &ast::Expr,
+ label: &str,
+ detail: &str,
+ snippet: &str,
+) -> Builder {
let edit = {
- let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax();
+ let receiver_syntax = receiver.syntax();
let receiver_range = ctx.sema.original_range(receiver_syntax).range;
let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
TextEdit::replace(delete_range, snippet.to_string())
"###
);
}
+
+ #[test]
+ fn postfix_completion_for_references() {
+ assert_debug_snapshot!(
+ do_postfix_completion(
+ r#"
+ fn main() {
+ &&&&42.<|>
+ }
+ "#,
+ ),
+ @r###"
+ [
+ CompletionItem {
+ label: "box",
+ source_range: [56; 56),
+ delete: [49; 56),
+ insert: "Box::new(&&&&42)",
+ detail: "Box::new(expr)",
+ },
+ CompletionItem {
+ label: "dbg",
+ source_range: [56; 56),
+ delete: [49; 56),
+ insert: "dbg!(&&&&42)",
+ detail: "dbg!(expr)",
+ },
+ CompletionItem {
+ label: "match",
+ source_range: [56; 56),
+ delete: [49; 56),
+ insert: "match &&&&42 {\n ${1:_} => {$0\\},\n}",
+ detail: "match expr {}",
+ },
+ CompletionItem {
+ label: "not",
+ source_range: [56; 56),
+ delete: [53; 56),
+ insert: "!42",
+ detail: "!expr",
+ },
+ CompletionItem {
+ label: "ref",
+ source_range: [56; 56),
+ delete: [53; 56),
+ insert: "&42",
+ detail: "&expr",
+ },
+ CompletionItem {
+ label: "refm",
+ source_range: [56; 56),
+ delete: [53; 56),
+ insert: "&mut 42",
+ detail: "&mut expr",
+ },
+ ]
+ "###
+ );
+ }
}