613 lines
14 KiB
Rust
Raw Normal View History

//! Completes keywords.
2020-08-12 18:26:51 +02:00
use syntax::{ast, SyntaxKind};
2020-07-03 23:46:36 +02:00
use test_utils::mark;
2019-01-08 22:33:36 +03:00
use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
2019-01-08 22:33:36 +03:00
2020-10-25 10:59:15 +03:00
pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
2019-01-08 22:33:36 +03:00
// complete keyword "crate" in use stmt
let source_range = ctx.source_range();
2020-08-13 22:41:55 +02:00
if ctx.use_item_syntax.is_some() {
if ctx.path_qual.is_none() {
CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
2019-01-08 22:33:36 +03:00
.kind(CompletionItemKind::Keyword)
.insert_text("crate::")
2019-01-08 22:33:36 +03:00
.add_to(acc);
}
2020-08-13 22:41:55 +02:00
CompletionItem::new(CompletionKind::Keyword, source_range, "self")
.kind(CompletionItemKind::Keyword)
.add_to(acc);
CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
.kind(CompletionItemKind::Keyword)
.insert_text("super::")
.add_to(acc);
2019-01-08 22:33:36 +03:00
}
2020-07-04 10:36:12 +02:00
// Suggest .await syntax for types that implement Future trait
if let Some(receiver) = &ctx.dot_receiver {
if let Some(ty) = ctx.sema.type_of_expr(receiver) {
if ty.impls_future(ctx.db) {
CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
.kind(CompletionItemKind::Keyword)
.detail("expr.await")
.insert_text("await")
.add_to(acc);
}
};
}
2019-01-08 22:33:36 +03:00
}
2020-10-25 10:59:15 +03:00
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
if ctx.token.kind() == SyntaxKind::COMMENT {
2020-07-03 23:46:36 +02:00
mark::hit!(no_keyword_completion_in_comments);
return;
}
if ctx.record_lit_syntax.is_some() {
mark::hit!(no_keyword_completion_in_record_lit);
return;
}
let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "where", "where ");
return;
}
if ctx.unsafe_is_prev {
2020-06-13 13:47:30 +02:00
if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
add_keyword(ctx, acc, "fn", "fn $0() {}")
}
2020-07-30 11:42:51 +02:00
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "trait", "trait $0 {}");
add_keyword(ctx, acc, "impl", "impl $0 {}");
}
return;
}
2020-07-30 11:42:51 +02:00
if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
{
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "fn", "fn $0() {}");
}
2020-07-30 11:42:51 +02:00
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "use", "use ");
add_keyword(ctx, acc, "impl", "impl $0 {}");
add_keyword(ctx, acc, "trait", "trait $0 {}");
}
2020-07-30 11:42:51 +02:00
if ctx.has_item_list_or_source_file_parent {
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "enum", "enum $0 {}");
add_keyword(ctx, acc, "struct", "struct $0");
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "union", "union $0 {}");
}
2020-07-10 17:56:55 +02:00
if ctx.is_expr {
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "match", "match $0 {}");
add_keyword(ctx, acc, "while", "while $0 {}");
2020-07-10 17:41:43 +02:00
add_keyword(ctx, acc, "loop", "loop {$0}");
add_keyword(ctx, acc, "if", "if ");
add_keyword(ctx, acc, "if let", "if let ");
2020-06-13 13:47:30 +02:00
}
2020-07-10 17:41:43 +02:00
2020-06-13 13:47:30 +02:00
if ctx.if_is_prev || ctx.block_expr_parent {
add_keyword(ctx, acc, "let", "let ");
}
2020-07-10 17:41:43 +02:00
2020-06-13 13:47:30 +02:00
if ctx.after_if {
add_keyword(ctx, acc, "else", "else {$0}");
add_keyword(ctx, acc, "else if", "else if $0 {}");
}
2020-07-30 11:42:51 +02:00
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "mod", "mod $0 {}");
}
if ctx.bind_pat_parent || ctx.ref_pat_parent {
add_keyword(ctx, acc, "mut", "mut ");
}
2020-07-30 11:42:51 +02:00
if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
{
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "const", "const ");
add_keyword(ctx, acc, "type", "type ");
}
2020-07-30 11:42:51 +02:00
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "static", "static ");
};
2020-07-30 11:42:51 +02:00
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "extern", "extern ");
}
2020-07-30 11:42:51 +02:00
if ctx.has_item_list_or_source_file_parent
|| has_trait_or_impl_parent
|| ctx.block_expr_parent
|| ctx.is_match_arm
{
2020-06-13 13:47:30 +02:00
add_keyword(ctx, acc, "unsafe", "unsafe ");
}
if ctx.in_loop_body {
if ctx.can_be_stmt {
add_keyword(ctx, acc, "continue", "continue;");
add_keyword(ctx, acc, "break", "break;");
} else {
add_keyword(ctx, acc, "continue", "continue");
add_keyword(ctx, acc, "break", "break");
}
}
2020-08-25 17:20:29 +02:00
if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent {
add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
add_keyword(ctx, acc, "pub", "pub ");
2020-06-13 13:47:30 +02:00
}
2019-01-08 22:33:36 +03:00
2020-06-13 01:21:48 +02:00
if !ctx.is_trivial_path {
return;
}
2019-07-19 12:56:47 +03:00
let fn_def = match &ctx.function_syntax {
2019-01-08 22:33:36 +03:00
Some(it) => it,
None => return,
};
2019-07-19 12:56:47 +03:00
acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
2019-01-08 22:33:36 +03:00
}
2020-06-13 13:47:30 +02:00
fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
.kind(CompletionItemKind::Keyword);
match ctx.config.snippet_cap {
Some(cap) => res.insert_snippet(cap, snippet),
_ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
}
.build()
}
fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
acc.add(keyword(ctx, kw, snippet));
}
fn complete_return(
ctx: &CompletionContext,
2020-07-30 14:51:08 +02:00
fn_def: &ast::Fn,
can_be_stmt: bool,
) -> Option<CompletionItem> {
2019-01-08 22:33:36 +03:00
let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
(true, true) => "return $0;",
(true, false) => "return;",
(false, true) => "return $0",
(false, false) => "return",
};
Some(keyword(ctx, "return", snip))
2019-01-08 22:33:36 +03:00
}
#[cfg(test)]
mod tests {
2020-08-21 13:19:31 +02:00
use expect_test::{expect, Expect};
2020-07-03 12:51:18 +02:00
use crate::{
2020-07-03 13:21:14 +02:00
test_utils::{check_edit, completion_list},
CompletionKind,
};
2020-07-03 23:46:36 +02:00
use test_utils::mark;
2020-07-03 12:51:18 +02:00
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Keyword);
expect.assert_eq(&actual)
2020-06-12 08:49:12 +02:00
}
2019-01-08 22:33:36 +03:00
#[test]
fn test_keywords_in_use_stmt() {
2020-07-03 12:51:18 +02:00
check(
r"use <|>",
expect![[r#"
kw crate::
kw self
kw super::
"#]],
2019-01-08 22:33:36 +03:00
);
2020-07-03 12:51:18 +02:00
check(
r"use a::<|>",
expect![[r#"
2020-07-04 19:03:58 +02:00
kw self
kw super::
"#]],
2019-01-08 22:33:36 +03:00
);
2020-07-03 12:51:18 +02:00
check(
r"use a::{b, <|>}",
expect![[r#"
2020-07-04 19:03:58 +02:00
kw self
kw super::
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_at_source_file_level() {
2020-07-03 12:51:18 +02:00
check(
r"m<|>",
expect![[r#"
kw const
kw enum
kw extern
kw fn
kw impl
kw mod
kw pub
2020-08-25 17:20:29 +02:00
kw pub(crate)
2020-07-03 12:51:18 +02:00
kw static
kw struct
kw trait
kw type
kw union
kw unsafe
kw use
"#]],
);
}
2019-01-08 22:33:36 +03:00
#[test]
fn test_keywords_in_function() {
2020-07-03 12:51:18 +02:00
check(
r"fn quux() { <|> }",
expect![[r#"
kw const
kw extern
kw fn
kw if
kw if let
kw impl
kw let
kw loop
kw match
kw mod
kw return
kw static
kw trait
kw type
kw unsafe
kw use
kw while
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_inside_block() {
2020-07-03 12:51:18 +02:00
check(
r"fn quux() { if true { <|> } }",
expect![[r#"
kw const
kw extern
kw fn
kw if
kw if let
kw impl
kw let
kw loop
kw match
kw mod
kw return
kw static
kw trait
kw type
kw unsafe
kw use
kw while
"#]],
);
}
#[test]
fn test_keywords_after_if() {
2020-07-03 12:51:18 +02:00
check(
r#"fn quux() { if true { () } <|> }"#,
expect![[r#"
kw const
kw else
kw else if
kw extern
kw fn
kw if
kw if let
kw impl
kw let
kw loop
kw match
kw mod
kw return
kw static
kw trait
kw type
kw unsafe
kw use
kw while
"#]],
2019-01-08 22:33:36 +03:00
);
2020-07-03 13:21:14 +02:00
check_edit(
"else",
r#"fn quux() { if true { () } <|> }"#,
r#"fn quux() { if true { () } else {$0} }"#,
);
2019-01-08 22:33:36 +03:00
}
#[test]
fn test_keywords_in_match_arm() {
2020-07-03 12:51:18 +02:00
check(
r#"
fn quux() -> i32 {
2020-07-10 17:41:43 +02:00
match () { () => <|> }
2020-07-03 12:51:18 +02:00
}
"#,
expect![[r#"
kw if
kw if let
kw loop
kw match
kw return
kw unsafe
2020-07-10 17:41:43 +02:00
kw while
2020-07-03 12:51:18 +02:00
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_in_trait_def() {
2020-07-03 12:51:18 +02:00
check(
r"trait My { <|> }",
expect![[r#"
kw const
kw fn
kw type
kw unsafe
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_in_impl_def() {
2020-07-03 12:51:18 +02:00
check(
r"impl My { <|> }",
expect![[r#"
kw const
kw fn
kw pub
2020-08-25 17:20:29 +02:00
kw pub(crate)
2020-07-03 12:51:18 +02:00
kw type
kw unsafe
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_in_loop() {
2020-07-03 12:51:18 +02:00
check(
r"fn my() { loop { <|> } }",
expect![[r#"
kw break
kw const
kw continue
kw extern
kw fn
kw if
kw if let
kw impl
kw let
kw loop
kw match
kw mod
kw return
kw static
kw trait
kw type
kw unsafe
kw use
kw while
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_after_unsafe_in_item_list() {
2020-07-03 12:51:18 +02:00
check(
r"unsafe <|>",
expect![[r#"
kw fn
kw impl
kw trait
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_after_unsafe_in_block_expr() {
2020-07-03 12:51:18 +02:00
check(
r"fn my_fn() { unsafe <|> }",
expect![[r#"
kw fn
kw impl
kw trait
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_mut_in_ref_and_in_fn_parameters_list() {
2020-07-03 12:51:18 +02:00
check(
r"fn my_fn(&<|>) {}",
expect![[r#"
kw mut
"#]],
);
2020-07-03 12:51:18 +02:00
check(
r"fn my_fn(<|>) {}",
expect![[r#"
kw mut
"#]],
);
2020-07-03 12:51:18 +02:00
check(
r"fn my_fn() { let &<|> }",
expect![[r#"
kw mut
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_where_keyword() {
2020-07-03 12:51:18 +02:00
check(
r"trait A <|>",
expect![[r#"
kw where
"#]],
);
2020-07-03 12:51:18 +02:00
check(
r"impl A <|>",
expect![[r#"
kw where
"#]],
);
2019-01-08 22:33:36 +03:00
}
2020-07-03 23:46:36 +02:00
#[test]
fn no_keyword_completion_in_comments() {
mark::check!(no_keyword_completion_in_comments);
check(
r#"
fn test() {
let x = 2; // A comment<|>
}
"#,
expect![[""]],
);
check(
r#"
/*
Some multi-line comment<|>
*/
"#,
expect![[""]],
);
check(
r#"
/// Some doc comment
/// let test<|> = 1
"#,
expect![[""]],
);
}
2020-07-04 10:36:12 +02:00
#[test]
fn test_completion_await_impls_future() {
check(
r#"
2020-10-02 16:13:48 +02:00
//- /main.rs crate:main deps:std
2020-07-04 10:36:12 +02:00
use std::future::*;
struct A {}
impl Future for A {}
fn foo(a: A) { a.<|> }
2020-10-02 16:13:48 +02:00
//- /std/lib.rs crate:std
2020-07-04 10:36:12 +02:00
pub mod future {
#[lang = "future_trait"]
pub trait Future {}
}
2020-09-10 20:01:23 +08:00
"#,
expect![[r#"
kw await expr.await
"#]],
);
check(
r#"
2020-10-02 16:13:48 +02:00
//- /main.rs crate:main deps:std
2020-09-10 20:01:23 +08:00
use std::future::*;
fn foo() {
let a = async {};
a.<|>
}
2020-10-02 16:13:48 +02:00
//- /std/lib.rs crate:std
2020-09-10 20:01:23 +08:00
pub mod future {
#[lang = "future_trait"]
pub trait Future {
type Output;
}
}
2020-07-04 10:36:12 +02:00
"#,
expect![[r#"
kw await expr.await
"#]],
)
}
2020-07-10 17:41:43 +02:00
#[test]
fn after_let() {
check(
r#"fn main() { let _ = <|> }"#,
expect![[r#"
kw if
kw if let
kw loop
kw match
kw return
kw while
"#]],
)
}
2020-08-25 17:20:29 +02:00
#[test]
fn before_field() {
check(
r#"
struct Foo {
<|>
pub f: i32,
}
"#,
expect![[r#"
kw pub
kw pub(crate)
"#]],
)
}
#[test]
fn skip_struct_initializer() {
mark::check!(no_keyword_completion_in_record_lit);
check(
r#"
struct Foo {
pub f: i32,
}
fn foo() {
Foo {
<|>
}
}
"#,
expect![[r#""#]],
);
}
#[test]
fn struct_initializer_field_expr() {
check(
r#"
struct Foo {
pub f: i32,
}
fn foo() {
Foo {
f: <|>
}
}
"#,
expect![[r#"
kw if
kw if let
kw loop
kw match
kw return
kw while
"#]],
);
}
2019-01-08 22:33:36 +03:00
}