internal: Move most remaining keyword completions to item list completions

This commit is contained in:
Lukas Wirth 2022-06-03 15:15:21 +02:00
parent 4f5c7aafff
commit 519ac81b57
6 changed files with 135 additions and 143 deletions

View File

@ -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);
}

View File

@ -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) {

View File

@ -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,

View File

@ -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#""#]],
)
}

View File

@ -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
"#]],
);
}

View File

@ -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::
"#]],