694 lines
16 KiB
Rust
Raw Normal View History

//! Completes keywords.
use std::iter;
2021-05-26 21:09:27 +02:00
use syntax::{SyntaxKind, T};
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();
2021-05-15 01:09:53 +02:00
let kw_completion = move |text: &str| {
let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, text);
item.kind(CompletionItemKind::Keyword).insert_text(text);
item
};
2020-08-13 22:41:55 +02:00
if ctx.use_item_syntax.is_some() {
if ctx.path_qual.is_none() {
2021-05-15 01:09:53 +02:00
kw_completion("crate::").add_to(acc);
2019-01-08 22:33:36 +03:00
}
2021-05-15 01:09:53 +02:00
kw_completion("self").add_to(acc);
if iter::successors(ctx.path_qual.clone(), |p| p.qualifier())
.all(|p| p.segment().and_then(|s| s.super_token()).is_some())
{
2021-05-15 01:09:53 +02:00
kw_completion("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) {
2021-05-15 01:09:53 +02:00
let mut item = kw_completion("await");
item.detail("expr.await");
2021-03-12 12:12:32 +03:00
item.add_to(acc);
2020-07-04 10:36:12 +02:00
}
};
}
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 {
2021-03-08 22:19:44 +02:00
cov_mark::hit!(no_keyword_completion_in_comments);
return;
}
if ctx.record_lit_syntax.is_some() {
2021-03-08 22:19:44 +02:00
cov_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;
}
2021-05-26 21:09:27 +02:00
if ctx.previous_token_is(T![unsafe]) {
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 $1($2) {\n $0\n}")
2020-06-13 13:47:30 +02:00
}
2020-07-30 11:42:51 +02:00
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}");
add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}");
2020-06-13 13:47:30 +02:00
}
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
{
add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}");
2020-06-13 13:47:30 +02:00
}
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 $1 {\n $0\n}");
add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}");
2020-06-13 13:47:30 +02:00
}
2020-07-30 11:42:51 +02:00
if ctx.has_item_list_or_source_file_parent {
add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}");
add_keyword(ctx, acc, "struct", "struct $0");
add_keyword(ctx, acc, "union", "union $1 {\n $0\n}");
2020-06-13 13:47:30 +02:00
}
2020-07-10 17:56:55 +02:00
if ctx.is_expr {
add_keyword(ctx, acc, "match", "match $1 {\n $0\n}");
add_keyword(ctx, acc, "while", "while $1 {\n $0\n}");
add_keyword(ctx, acc, "while let", "while let $1 = $2 {\n $0\n}");
add_keyword(ctx, acc, "loop", "loop {\n $0\n}");
add_keyword(ctx, acc, "if", "if $1 {\n $0\n}");
add_keyword(ctx, acc, "if let", "if let $1 = $2 {\n $0\n}");
add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}");
2020-06-13 13:47:30 +02:00
}
2020-07-10 17:41:43 +02:00
2021-05-26 21:09:27 +02:00
if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || ctx.block_expr_parent {
2020-06-13 13:47:30 +02:00
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 {\n $0\n}");
add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}");
2020-06-13 13:47:30 +02:00
}
2020-07-30 11:42:51 +02:00
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
add_keyword(ctx, acc, "mod", "mod $0");
2020-06-13 13:47:30 +02:00
}
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,
};
2020-06-13 13:47:30 +02:00
2021-01-15 15:13:30 +03:00
add_keyword(
ctx,
acc,
"return",
match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
(true, true) => "return $0;",
(true, false) => "return;",
(false, true) => "return $0",
(false, false) => "return",
},
)
2020-06-13 13:47:30 +02:00
}
fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
2021-03-12 12:12:32 +03:00
let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw);
item.kind(CompletionItemKind::Keyword);
match ctx.config.snippet_cap {
Some(cap) => {
let tmp;
let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
2021-03-08 22:19:44 +02:00
cov_mark::hit!(let_semi);
tmp = format!("{};", snippet);
&tmp
} else {
snippet
};
2021-03-12 12:12:32 +03:00
item.insert_snippet(cap, snippet);
}
None => {
2021-03-12 12:12:32 +03:00
item.insert_text(if snippet.contains('$') { kw } else { snippet });
}
2019-01-08 22:33:36 +03:00
};
2021-03-12 12:12:32 +03:00
item.add_to(acc);
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 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(
2021-01-06 20:15:48 +00:00
r"use $0",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw crate::
kw self
kw super::
"#]],
2019-01-08 22:33:36 +03:00
);
// FIXME: `self` shouldn't be shown here and the check below
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r"use a::$0",
expect![[r#"
kw self
"#]],
);
check(
r"use super::$0",
2020-07-03 12:51:18 +02:00
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(
2021-01-06 20:15:48 +00:00
r"use a::{b, $0}",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw self
"#]],
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(
2021-01-06 20:15:48 +00:00
r"m$0",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw use
2020-07-03 12:51:18 +02:00
kw impl
kw trait
kw enum
kw struct
2020-07-03 12:51:18 +02:00
kw union
kw mod
kw const
kw type
kw static
kw extern
2020-07-03 12:51:18 +02:00
kw unsafe
kw pub(crate)
kw pub
2020-07-03 12:51:18 +02:00
"#]],
);
}
2019-01-08 22:33:36 +03:00
#[test]
fn test_keywords_in_function() {
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r"fn quux() { $0 }",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw use
kw impl
kw trait
kw match
kw while
2021-03-03 21:59:41 +01:00
kw while let
kw loop
2020-07-03 12:51:18 +02:00
kw if
kw if let
kw for
2020-07-03 12:51:18 +02:00
kw let
kw mod
kw const
2020-07-03 12:51:18 +02:00
kw type
kw static
kw extern
2020-07-03 12:51:18 +02:00
kw unsafe
kw return
2020-07-03 12:51:18 +02:00
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_inside_block() {
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r"fn quux() { if true { $0 } }",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw use
kw impl
kw trait
kw match
kw while
2021-03-03 21:59:41 +01:00
kw while let
kw loop
2020-07-03 12:51:18 +02:00
kw if
kw if let
kw for
2020-07-03 12:51:18 +02:00
kw let
kw mod
kw const
2020-07-03 12:51:18 +02:00
kw type
kw static
kw extern
2020-07-03 12:51:18 +02:00
kw unsafe
kw return
2020-07-03 12:51:18 +02:00
"#]],
);
}
#[test]
fn test_keywords_after_if() {
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r#"fn quux() { if true { () } $0 }"#,
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw use
kw impl
kw trait
kw match
kw while
2021-03-03 21:59:41 +01:00
kw while let
kw loop
2020-07-03 12:51:18 +02:00
kw if
kw if let
kw for
2020-07-03 12:51:18 +02:00
kw let
kw else
kw else if
2020-07-03 12:51:18 +02:00
kw mod
kw const
2020-07-03 12:51:18 +02:00
kw type
kw static
kw extern
2020-07-03 12:51:18 +02:00
kw unsafe
kw return
2020-07-03 12:51:18 +02:00
"#]],
2019-01-08 22:33:36 +03:00
);
2020-07-03 13:21:14 +02:00
check_edit(
"else",
2021-01-06 20:15:48 +00:00
r#"fn quux() { if true { () } $0 }"#,
r#"fn quux() { if true { () } else {
$0
} }"#,
2020-07-03 13:21:14 +02:00
);
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 {
2021-01-06 20:15:48 +00:00
match () { () => $0 }
2020-07-03 12:51:18 +02:00
}
"#,
expect![[r#"
kw match
kw while
2021-03-03 21:59:41 +01:00
kw while let
kw loop
2020-07-03 12:51:18 +02:00
kw if
kw if let
kw for
2020-07-03 12:51:18 +02:00
kw unsafe
kw return
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(
2021-01-06 20:15:48 +00:00
r"trait My { $0 }",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw const
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_impl_def() {
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r"impl My { $0 }",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw const
2020-07-03 12:51:18 +02:00
kw type
kw unsafe
kw pub(crate)
kw pub
2020-07-03 12:51:18 +02:00
"#]],
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn test_keywords_in_loop() {
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r"fn my() { loop { $0 } }",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw use
kw impl
kw trait
kw match
kw while
2021-03-03 21:59:41 +01:00
kw while let
kw loop
2020-07-03 12:51:18 +02:00
kw if
kw if let
kw for
2020-07-03 12:51:18 +02:00
kw let
kw mod
kw const
2020-07-03 12:51:18 +02:00
kw type
kw static
kw extern
2020-07-03 12:51:18 +02:00
kw unsafe
kw continue
kw break
kw return
2020-07-03 12:51:18 +02:00
"#]],
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(
2021-01-06 20:15:48 +00:00
r"unsafe $0",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw trait
kw impl
2020-07-03 12:51:18 +02:00
"#]],
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(
2021-01-06 20:15:48 +00:00
r"fn my_fn() { unsafe $0 }",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw fn
kw trait
kw impl
2020-07-03 12:51:18 +02:00
"#]],
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(
2021-01-06 20:15:48 +00:00
r"fn my_fn(&$0) {}",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw mut
"#]],
);
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r"fn my_fn($0) {}",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw mut
"#]],
);
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r"fn my_fn() { let &$0 }",
2020-07-03 12:51:18 +02:00
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(
2021-01-06 20:15:48 +00:00
r"trait A $0",
2020-07-03 12:51:18 +02:00
expect![[r#"
kw where
"#]],
);
2020-07-03 12:51:18 +02:00
check(
2021-01-06 20:15:48 +00:00
r"impl A $0",
2020-07-03 12:51:18 +02:00
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() {
2021-03-08 22:19:44 +02:00
cov_mark::check!(no_keyword_completion_in_comments);
2020-07-03 23:46:36 +02:00
check(
r#"
fn test() {
2021-01-06 20:15:48 +00:00
let x = 2; // A comment$0
2020-07-03 23:46:36 +02:00
}
"#,
expect![[""]],
);
check(
r#"
/*
2021-01-06 20:15:48 +00:00
Some multi-line comment$0
2020-07-03 23:46:36 +02:00
*/
"#,
expect![[""]],
);
check(
r#"
/// Some doc comment
2021-01-06 20:15:48 +00:00
/// let test$0 = 1
2020-07-03 23:46:36 +02:00
"#,
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 {}
2021-01-06 20:15:48 +00:00
fn foo(a: A) { a.$0 }
2020-07-04 10:36:12 +02:00
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 {};
2021-01-06 20:15:48 +00:00
a.$0
2020-09-10 20:01:23 +08:00
}
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(
2021-01-06 20:15:48 +00:00
r#"fn main() { let _ = $0 }"#,
2020-07-10 17:41:43 +02:00
expect![[r#"
kw match
kw while
2021-03-03 21:59:41 +01:00
kw while let
kw loop
2020-07-10 17:41:43 +02:00
kw if
kw if let
kw for
2020-07-10 17:41:43 +02:00
kw return
"#]],
)
}
2020-08-25 17:20:29 +02:00
#[test]
fn before_field() {
check(
r#"
struct Foo {
2021-01-06 20:15:48 +00:00
$0
2020-08-25 17:20:29 +02:00
pub f: i32,
}
"#,
expect![[r#"
kw pub(crate)
kw pub
2020-08-25 17:20:29 +02:00
"#]],
)
}
#[test]
fn skip_struct_initializer() {
2021-03-08 22:19:44 +02:00
cov_mark::check!(no_keyword_completion_in_record_lit);
check(
r#"
struct Foo {
pub f: i32,
}
fn foo() {
Foo {
2021-01-06 20:15:48 +00:00
$0
}
}
"#,
expect![[r#""#]],
);
}
#[test]
fn struct_initializer_field_expr() {
check(
r#"
struct Foo {
pub f: i32,
}
fn foo() {
Foo {
2021-01-06 20:15:48 +00:00
f: $0
}
}
"#,
expect![[r#"
kw match
kw while
2021-03-03 21:59:41 +01:00
kw while let
kw loop
kw if
kw if let
kw for
kw return
"#]],
);
}
#[test]
fn let_semi() {
2021-03-08 22:19:44 +02:00
cov_mark::check!(let_semi);
check_edit(
"match",
r#"
fn main() { let x = $0 }
"#,
r#"
fn main() { let x = match $1 {
$0
}; }
"#,
);
check_edit(
"if",
r#"
fn main() {
let x = $0
let y = 92;
}
"#,
r#"
fn main() {
let x = if $1 {
$0
};
let y = 92;
}
"#,
);
check_edit(
"loop",
r#"
fn main() {
let x = $0
bar();
}
"#,
r#"
fn main() {
let x = loop {
$0
};
bar();
}
"#,
);
}
2019-01-08 22:33:36 +03:00
}