c3a4c4429d
This introduces the new type -- Semantics. Semantics maps SyntaxNodes to various semantic info, such as type, name resolution or macro expansions. To do so, Semantics maintains a HashMap which maps every node it saw to the file from which the node originated. This is enough to get all the necessary hir bits just from syntax.
283 lines
8.2 KiB
Rust
283 lines
8.2 KiB
Rust
//! FIXME: write short doc here
|
|
|
|
use ra_syntax::{ast::AstNode, TextRange, TextUnit};
|
|
use ra_text_edit::TextEdit;
|
|
|
|
use crate::{
|
|
completion::{
|
|
completion_context::CompletionContext,
|
|
completion_item::{Builder, CompletionKind, Completions},
|
|
},
|
|
CompletionItem,
|
|
};
|
|
|
|
pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
|
if !ctx.db.feature_flags.get("completion.enable-postfix") {
|
|
return;
|
|
}
|
|
|
|
let dot_receiver = match &ctx.dot_receiver {
|
|
Some(it) => it,
|
|
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_ty = match ctx.sema.type_of_expr(&dot_receiver) {
|
|
Some(it) => it,
|
|
None => return,
|
|
};
|
|
|
|
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,
|
|
"while",
|
|
"while expr {}",
|
|
&format!("while {} {{\n$0\n}}", receiver_text),
|
|
)
|
|
.add_to(acc);
|
|
}
|
|
|
|
postfix_snippet(ctx, "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,
|
|
"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, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
|
|
.add_to(acc);
|
|
}
|
|
|
|
fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
|
|
let edit = {
|
|
let receiver_range =
|
|
ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
|
|
let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
|
|
TextEdit::replace(delete_range, snippet.to_string())
|
|
};
|
|
CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
|
|
.detail(detail)
|
|
.snippet_edit(edit)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use insta::assert_debug_snapshot;
|
|
|
|
use crate::completion::{do_completion, CompletionItem, CompletionKind};
|
|
|
|
fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
|
|
do_completion(code, CompletionKind::Postfix)
|
|
}
|
|
|
|
#[test]
|
|
fn postfix_completion_works_for_trivial_path_expression() {
|
|
assert_debug_snapshot!(
|
|
do_postfix_completion(
|
|
r#"
|
|
fn main() {
|
|
let bar = true;
|
|
bar.<|>
|
|
}
|
|
"#,
|
|
),
|
|
@r###"
|
|
[
|
|
CompletionItem {
|
|
label: "box",
|
|
source_range: [89; 89),
|
|
delete: [85; 89),
|
|
insert: "Box::new(bar)",
|
|
detail: "Box::new(expr)",
|
|
},
|
|
CompletionItem {
|
|
label: "dbg",
|
|
source_range: [89; 89),
|
|
delete: [85; 89),
|
|
insert: "dbg!(bar)",
|
|
detail: "dbg!(expr)",
|
|
},
|
|
CompletionItem {
|
|
label: "if",
|
|
source_range: [89; 89),
|
|
delete: [85; 89),
|
|
insert: "if bar {$0}",
|
|
detail: "if expr {}",
|
|
},
|
|
CompletionItem {
|
|
label: "match",
|
|
source_range: [89; 89),
|
|
delete: [85; 89),
|
|
insert: "match bar {\n ${1:_} => {$0\\},\n}",
|
|
detail: "match expr {}",
|
|
},
|
|
CompletionItem {
|
|
label: "not",
|
|
source_range: [89; 89),
|
|
delete: [85; 89),
|
|
insert: "!bar",
|
|
detail: "!expr",
|
|
},
|
|
CompletionItem {
|
|
label: "ref",
|
|
source_range: [89; 89),
|
|
delete: [85; 89),
|
|
insert: "&bar",
|
|
detail: "&expr",
|
|
},
|
|
CompletionItem {
|
|
label: "refm",
|
|
source_range: [89; 89),
|
|
delete: [85; 89),
|
|
insert: "&mut bar",
|
|
detail: "&mut expr",
|
|
},
|
|
CompletionItem {
|
|
label: "while",
|
|
source_range: [89; 89),
|
|
delete: [85; 89),
|
|
insert: "while bar {\n$0\n}",
|
|
detail: "while expr {}",
|
|
},
|
|
]
|
|
"###
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn some_postfix_completions_ignored() {
|
|
assert_debug_snapshot!(
|
|
do_postfix_completion(
|
|
r#"
|
|
fn main() {
|
|
let bar: u8 = 12;
|
|
bar.<|>
|
|
}
|
|
"#,
|
|
),
|
|
@r###"
|
|
[
|
|
CompletionItem {
|
|
label: "box",
|
|
source_range: [91; 91),
|
|
delete: [87; 91),
|
|
insert: "Box::new(bar)",
|
|
detail: "Box::new(expr)",
|
|
},
|
|
CompletionItem {
|
|
label: "dbg",
|
|
source_range: [91; 91),
|
|
delete: [87; 91),
|
|
insert: "dbg!(bar)",
|
|
detail: "dbg!(expr)",
|
|
},
|
|
CompletionItem {
|
|
label: "match",
|
|
source_range: [91; 91),
|
|
delete: [87; 91),
|
|
insert: "match bar {\n ${1:_} => {$0\\},\n}",
|
|
detail: "match expr {}",
|
|
},
|
|
CompletionItem {
|
|
label: "not",
|
|
source_range: [91; 91),
|
|
delete: [87; 91),
|
|
insert: "!bar",
|
|
detail: "!expr",
|
|
},
|
|
CompletionItem {
|
|
label: "ref",
|
|
source_range: [91; 91),
|
|
delete: [87; 91),
|
|
insert: "&bar",
|
|
detail: "&expr",
|
|
},
|
|
CompletionItem {
|
|
label: "refm",
|
|
source_range: [91; 91),
|
|
delete: [87; 91),
|
|
insert: "&mut bar",
|
|
detail: "&mut expr",
|
|
},
|
|
]
|
|
"###
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn postfix_completion_works_for_ambiguous_float_literal() {
|
|
assert_debug_snapshot!(
|
|
do_postfix_completion(
|
|
r#"
|
|
fn main() {
|
|
42.<|>
|
|
}
|
|
"#,
|
|
),
|
|
@r###"
|
|
[
|
|
CompletionItem {
|
|
label: "box",
|
|
source_range: [52; 52),
|
|
delete: [49; 52),
|
|
insert: "Box::new(42)",
|
|
detail: "Box::new(expr)",
|
|
},
|
|
CompletionItem {
|
|
label: "dbg",
|
|
source_range: [52; 52),
|
|
delete: [49; 52),
|
|
insert: "dbg!(42)",
|
|
detail: "dbg!(expr)",
|
|
},
|
|
CompletionItem {
|
|
label: "match",
|
|
source_range: [52; 52),
|
|
delete: [49; 52),
|
|
insert: "match 42 {\n ${1:_} => {$0\\},\n}",
|
|
detail: "match expr {}",
|
|
},
|
|
CompletionItem {
|
|
label: "not",
|
|
source_range: [52; 52),
|
|
delete: [49; 52),
|
|
insert: "!42",
|
|
detail: "!expr",
|
|
},
|
|
CompletionItem {
|
|
label: "ref",
|
|
source_range: [52; 52),
|
|
delete: [49; 52),
|
|
insert: "&42",
|
|
detail: "&expr",
|
|
},
|
|
CompletionItem {
|
|
label: "refm",
|
|
source_range: [52; 52),
|
|
delete: [49; 52),
|
|
insert: "&mut 42",
|
|
detail: "&mut expr",
|
|
},
|
|
]
|
|
"###
|
|
);
|
|
}
|
|
}
|