Unnamed repository; edit this file 'description' to name the repository.
internal: Move most remaining keyword completions to item list completions
Lukas Wirth 2022-06-03
parent 4f5c7aa · commit 519ac81
-rw-r--r--crates/ide-completion/src/completions/item_list.rs108
-rw-r--r--crates/ide-completion/src/completions/keyword.rs50
-rw-r--r--crates/ide-completion/src/context.rs47
-rw-r--r--crates/ide-completion/src/tests/item.rs55
-rw-r--r--crates/ide-completion/src/tests/item_list.rs12
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs6
6 files changed, 135 insertions, 143 deletions
diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs
index edff146d8d..b78ed26ec3 100644
--- a/crates/ide-completion/src/completions/item_list.rs
+++ b/crates/ide-completion/src/completions/item_list.rs
@@ -2,22 +2,98 @@
use crate::{
completions::module_or_fn_macro,
- context::{PathCompletionCtx, PathKind, PathQualifierCtx},
- CompletionContext, Completions,
+ context::{ItemListKind, PathCompletionCtx, PathKind, PathQualifierCtx},
+ CompletionContext, CompletionItem, CompletionItemKind, Completions,
};
pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) {
let _p = profile::span("complete_item_list");
- let (&is_absolute_path, path_qualifier, _kind) = match ctx.path_context() {
+ let (&is_absolute_path, path_qualifier, kind) = match ctx.path_context() {
Some(PathCompletionCtx {
kind: PathKind::Item { kind },
is_absolute_path,
qualifier,
..
- }) => (is_absolute_path, qualifier, kind),
+ }) => (is_absolute_path, qualifier, Some(kind)),
+ Some(PathCompletionCtx {
+ kind: PathKind::Expr { in_block_expr: true, .. },
+ is_absolute_path,
+ qualifier,
+ ..
+ }) => (is_absolute_path, qualifier, None),
_ => return,
};
+ let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
+
+ let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
+ let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait));
+ let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock));
+ let in_trait = matches!(kind, Some(ItemListKind::Trait));
+ let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl));
+ let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl));
+ let no_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
+ let in_block = matches!(kind, None);
+
+ 'block: loop {
+ if path_qualifier.is_some() {
+ break 'block;
+ }
+ if !in_trait_impl {
+ if ctx.qualifier_ctx.unsafe_tok.is_some() {
+ if in_item_list || in_assoc_non_trait_impl {
+ add_keyword("fn", "fn $1($2) {\n $0\n}");
+ }
+ if in_item_list {
+ add_keyword("trait", "trait $1 {\n $0\n}");
+ if no_qualifiers {
+ add_keyword("impl", "impl $1 {\n $0\n}");
+ }
+ }
+ break 'block;
+ }
+
+ if in_item_list {
+ add_keyword("enum", "enum $1 {\n $0\n}");
+ add_keyword("mod", "mod $0");
+ add_keyword("static", "static $0");
+ add_keyword("struct", "struct $0");
+ add_keyword("trait", "trait $1 {\n $0\n}");
+ add_keyword("union", "union $1 {\n $0\n}");
+ add_keyword("use", "use $0");
+ if no_qualifiers {
+ add_keyword("impl", "impl $1 {\n $0\n}");
+ }
+ }
+
+ if !in_trait && !in_block && no_qualifiers {
+ add_keyword("pub(crate)", "pub(crate)");
+ add_keyword("pub(super)", "pub(super)");
+ add_keyword("pub", "pub");
+ }
+
+ if in_extern_block {
+ add_keyword("fn", "fn $1($2);");
+ } else {
+ if !in_inherent_impl {
+ if !in_trait {
+ add_keyword("extern", "extern $0");
+ }
+ add_keyword("type", "type $0");
+ }
+
+ add_keyword("fn", "fn $1($2) {\n $0\n}");
+ add_keyword("unsafe", "unsafe");
+ add_keyword("const", "const $0");
+ }
+ }
+ break 'block;
+ }
+
+ if kind.is_none() {
+ // this is already handled by expression
+ return;
+ }
match path_qualifier {
Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
@@ -33,9 +109,7 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
acc.add_keyword(ctx, "super::");
}
}
- None if is_absolute_path => {
- acc.add_crate_roots(ctx);
- }
+ None if is_absolute_path => acc.add_crate_roots(ctx),
None if ctx.qualifier_ctx.none() => {
ctx.process_all_names(&mut |name, def| {
if let Some(def) = module_or_fn_macro(ctx.db, def) {
@@ -47,3 +121,23 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
None => {}
}
}
+
+pub(super) fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) {
+ let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
+
+ match ctx.config.snippet_cap {
+ Some(cap) => {
+ if snippet.ends_with('}') && ctx.incomplete_let {
+ // complete block expression snippets with a trailing semicolon, if inside an incomplete let
+ cov_mark::hit!(let_semi);
+ item.insert_snippet(cap, format!("{};", snippet));
+ } else {
+ item.insert_snippet(cap, snippet);
+ }
+ }
+ None => {
+ item.insert_text(if snippet.contains('$') { kw } else { snippet });
+ }
+ };
+ item.add_to(acc);
+}
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index 281e6e9783..d55046e710 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -2,8 +2,6 @@
//! - `self`, `super` and `crate`, as these are considered part of path completions.
//! - `await`, as this is a postfix completion we handle this in the postfix completions.
-use syntax::T;
-
use crate::{
context::{NameRefContext, PathKind},
CompletionContext, CompletionItem, CompletionItemKind, Completions,
@@ -24,10 +22,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
- let expects_assoc_item = ctx.expects_assoc_item();
- let has_block_expr_parent = ctx.has_block_expr_parent();
- let expects_item = ctx.expects_item();
-
if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
return;
}
@@ -38,50 +32,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
}
return;
}
- if ctx.previous_token_is(T![unsafe]) {
- if expects_item || expects_assoc_item || has_block_expr_parent {
- add_keyword("fn", "fn $1($2) {\n $0\n}")
- }
-
- if expects_item || has_block_expr_parent {
- add_keyword("trait", "trait $1 {\n $0\n}");
- add_keyword("impl", "impl $1 {\n $0\n}");
- }
-
- return;
- }
-
- if ctx.qualifier_ctx.vis_node.is_none()
- && (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_field())
- {
- add_keyword("pub(crate)", "pub(crate)");
- add_keyword("pub(super)", "pub(super)");
- add_keyword("pub", "pub");
- }
-
- if expects_item || expects_assoc_item || has_block_expr_parent {
- add_keyword("unsafe", "unsafe");
- add_keyword("fn", "fn $1($2) {\n $0\n}");
- add_keyword("const", "const $0");
- add_keyword("type", "type $0");
- }
-
- if expects_item || has_block_expr_parent {
- if ctx.qualifier_ctx.vis_node.is_none() {
- add_keyword("impl", "impl $1 {\n $0\n}");
- add_keyword("extern", "extern $0");
- }
- add_keyword("use", "use $0");
- add_keyword("trait", "trait $1 {\n $0\n}");
- add_keyword("static", "static $0");
- add_keyword("mod", "mod $0");
- }
-
- if expects_item || has_block_expr_parent {
- add_keyword("enum", "enum $1 {\n $0\n}");
- add_keyword("struct", "struct $0");
- add_keyword("union", "union $1 {\n $0\n}");
- }
}
pub(super) fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) {
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index f3e316ff3c..4eac86162a 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -71,6 +71,7 @@ pub(super) enum ItemListKind {
SourceFile,
Module,
Impl,
+ TraitImpl,
Trait,
ExternBlock,
}
@@ -335,10 +336,6 @@ impl<'a> CompletionContext<'a> {
matches!(self.completion_location, Some(ImmediateLocation::Trait | ImmediateLocation::Impl))
}
- pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
- matches!(self.completion_location, Some(ImmediateLocation::Impl))
- }
-
pub(crate) fn expects_item(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::ItemList))
}
@@ -348,19 +345,10 @@ impl<'a> CompletionContext<'a> {
matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
}
- pub(crate) fn has_block_expr_parent(&self) -> bool {
- matches!(self.completion_location, Some(ImmediateLocation::StmtList))
- }
-
pub(crate) fn expects_ident_ref_expr(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
}
- pub(crate) fn expect_field(&self) -> bool {
- matches!(self.completion_location, Some(ImmediateLocation::TupleField))
- || matches!(self.name_ctx(), Some(NameContext { kind: NameKind::RecordField, .. }))
- }
-
/// Whether the cursor is right after a trait or impl header.
/// trait Foo ident$0
// FIXME: This probably shouldn't exist
@@ -1276,10 +1264,19 @@ impl<'a> CompletionContext<'a> {
Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type),
Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
- Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()).map(|it| it.kind()) {
- Some(SyntaxKind::TRAIT) => ItemListKind::Trait,
- Some(SyntaxKind::IMPL) => ItemListKind::Impl,
- _ => return Some(None),
+ Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
+ Some(it) => match_ast! {
+ match it {
+ ast::Trait(_) => ItemListKind::Trait,
+ ast::Impl(it) => if it.trait_().is_some() {
+ ItemListKind::TraitImpl
+ } else {
+ ItemListKind::Impl
+ },
+ _ => return Some(None)
+ }
+ },
+ None => return Some(None),
} }),
Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
@@ -1313,12 +1310,18 @@ impl<'a> CompletionContext<'a> {
ast::UseTree(_) => Some(PathKind::Use),
ast::ItemList(_) => Some(PathKind::Item { kind: ItemListKind::Module }),
ast::AssocItemList(it) => Some(PathKind::Item { kind: {
- match it.syntax().parent()?.kind() {
- SyntaxKind::TRAIT => ItemListKind::Trait,
- SyntaxKind::IMPL => ItemListKind::Impl,
- _ => return None,
+ match_ast! {
+ match (it.syntax().parent()?) {
+ ast::Trait(_) => ItemListKind::Trait,
+ ast::Impl(it) => if it.trait_().is_some() {
+ ItemListKind::TraitImpl
+ } else {
+ ItemListKind::Impl
+ },
+ _ => return None
}
- }}),
+ }
+ }}),
ast::ExternItemList(_) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
ast::SourceFile(_) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
_ => return None,
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs
index 537c9a7fa2..d1f5d2a33c 100644
--- a/crates/ide-completion/src/tests/item.rs
+++ b/crates/ide-completion/src/tests/item.rs
@@ -88,58 +88,19 @@ fn after_target_name_in_impl() {
#[test]
fn after_struct_name() {
- // FIXME: This should emit `kw where` only
- check(
- r"struct Struct $0",
- expect![[r#"
- kw const
- kw enum
- kw extern
- kw fn
- kw impl
- kw mod
- kw pub
- kw pub(crate)
- kw pub(super)
- kw static
- kw struct
- kw trait
- kw type
- kw union
- kw unsafe
- kw use
- "#]],
- );
+ // FIXME: This should emit `kw where`
+ check(r"struct Struct $0", expect![[r#""#]]);
}
#[test]
fn after_fn_name() {
- // FIXME: This should emit `kw where` only
- check(
- r"fn func() $0",
- expect![[r#"
- kw const
- kw enum
- kw extern
- kw fn
- kw impl
- kw mod
- kw pub
- kw pub(crate)
- kw pub(super)
- kw static
- kw struct
- kw trait
- kw type
- kw union
- kw unsafe
- kw use
- "#]],
- );
+ // FIXME: This should emit `kw where`
+ check(r"fn func() $0", expect![[r#""#]]);
}
#[test]
fn before_record_field() {
+ // FIXME: This should emit visibility qualifiers
check(
r#"
struct Foo {
@@ -147,10 +108,6 @@ struct Foo {
pub f: i32,
}
"#,
- expect![[r#"
- kw pub
- kw pub(crate)
- kw pub(super)
- "#]],
+ expect![[r#""#]],
)
}
diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs
index d03a4fd5cd..edc896636f 100644
--- a/crates/ide-completion/src/tests/item_list.rs
+++ b/crates/ide-completion/src/tests/item_list.rs
@@ -137,6 +137,7 @@ fn after_visibility() {
expect![[r#"
kw const
kw enum
+ kw extern
kw fn
kw mod
kw static
@@ -152,12 +153,10 @@ fn after_visibility() {
#[test]
fn after_visibility_unsafe() {
- // FIXME this shouldn't show `impl`
check(
r#"pub unsafe $0"#,
expect![[r#"
kw fn
- kw impl
kw trait
"#]],
);
@@ -178,7 +177,6 @@ fn in_impl_assoc_item_list() {
kw pub(super)
kw self::
kw super::
- kw type
kw unsafe
"#]],
)
@@ -199,7 +197,6 @@ fn in_impl_assoc_item_list_after_attr() {
kw pub(super)
kw self::
kw super::
- kw type
kw unsafe
"#]],
)
@@ -249,16 +246,9 @@ impl Test for () {
ma makro!(…) macro_rules! makro
md module
ta type Type1 =
- kw const
kw crate::
- kw fn
- kw pub
- kw pub(crate)
- kw pub(super)
kw self::
kw super::
- kw type
- kw unsafe
"#]],
);
}
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 1e5e86eef5..76942110f8 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -38,13 +38,14 @@ struct Foo<'lt, T, const C: usize> {
#[test]
fn tuple_struct_field() {
+ // FIXME: This should emit visibility qualifiers
check(
r#"
struct Foo<'lt, T, const C: usize>(f$0);
"#,
expect![[r#"
en Enum
- ma makro!(…) macro_rules! makro
+ ma makro!(…) macro_rules! makro
md module
sp Self
st Foo<…>
@@ -56,9 +57,6 @@ struct Foo<'lt, T, const C: usize>(f$0);
un Union
bt u32
kw crate::
- kw pub
- kw pub(crate)
- kw pub(super)
kw self::
kw super::
"#]],