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::{ use crate::{
completions::module_or_fn_macro, completions::module_or_fn_macro,
context::{PathCompletionCtx, PathKind, PathQualifierCtx}, context::{ItemListKind, PathCompletionCtx, PathKind, PathQualifierCtx},
CompletionContext, Completions, CompletionContext, CompletionItem, CompletionItemKind, Completions,
}; };
pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) { pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) {
let _p = profile::span("complete_item_list"); 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 { Some(PathCompletionCtx {
kind: PathKind::Item { kind }, kind: PathKind::Item { kind },
is_absolute_path, is_absolute_path,
qualifier, 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, _ => 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 { match path_qualifier {
Some(PathQualifierCtx { resolution, is_super_chain, .. }) => { 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::"); acc.add_keyword(ctx, "super::");
} }
} }
None if is_absolute_path => { None if is_absolute_path => acc.add_crate_roots(ctx),
acc.add_crate_roots(ctx);
}
None if ctx.qualifier_ctx.none() => { None if ctx.qualifier_ctx.none() => {
ctx.process_all_names(&mut |name, def| { ctx.process_all_names(&mut |name, def| {
if let Some(def) = module_or_fn_macro(ctx.db, 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 => {} 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. //! - `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. //! - `await`, as this is a postfix completion we handle this in the postfix completions.
use syntax::T;
use crate::{ use crate::{
context::{NameRefContext, PathKind}, context::{NameRefContext, PathKind},
CompletionContext, CompletionItem, CompletionItemKind, Completions, 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 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() { if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
return; return;
} }
@ -38,50 +32,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
} }
return; 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) { 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, SourceFile,
Module, Module,
Impl, Impl,
TraitImpl,
Trait, Trait,
ExternBlock, ExternBlock,
} }
@ -335,10 +336,6 @@ impl<'a> CompletionContext<'a> {
matches!(self.completion_location, Some(ImmediateLocation::Trait | ImmediateLocation::Impl)) 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 { pub(crate) fn expects_item(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::ItemList)) matches!(self.completion_location, Some(ImmediateLocation::ItemList))
} }
@ -348,19 +345,10 @@ impl<'a> CompletionContext<'a> {
matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_))) 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 { pub(crate) fn expects_ident_ref_expr(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::RefExpr)) 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. /// Whether the cursor is right after a trait or impl header.
/// trait Foo ident$0 /// trait Foo ident$0
// FIXME: This probably shouldn't exist // FIXME: This probably shouldn't exist
@ -1276,10 +1264,19 @@ impl<'a> CompletionContext<'a> {
Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat), Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type), Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type),
Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }), 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::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
Some(SyntaxKind::TRAIT) => ItemListKind::Trait, Some(it) => match_ast! {
Some(SyntaxKind::IMPL) => ItemListKind::Impl, match it {
_ => return Some(None), 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::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }), Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
@ -1313,10 +1310,16 @@ impl<'a> CompletionContext<'a> {
ast::UseTree(_) => Some(PathKind::Use), ast::UseTree(_) => Some(PathKind::Use),
ast::ItemList(_) => Some(PathKind::Item { kind: ItemListKind::Module }), ast::ItemList(_) => Some(PathKind::Item { kind: ItemListKind::Module }),
ast::AssocItemList(it) => Some(PathKind::Item { kind: { ast::AssocItemList(it) => Some(PathKind::Item { kind: {
match it.syntax().parent()?.kind() { match_ast! {
SyntaxKind::TRAIT => ItemListKind::Trait, match (it.syntax().parent()?) {
SyntaxKind::IMPL => ItemListKind::Impl, ast::Trait(_) => ItemListKind::Trait,
_ => return None, ast::Impl(it) => if it.trait_().is_some() {
ItemListKind::TraitImpl
} else {
ItemListKind::Impl
},
_ => return None
}
} }
}}), }}),
ast::ExternItemList(_) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }), ast::ExternItemList(_) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),

View file

@ -88,58 +88,19 @@ fn after_target_name_in_impl() {
#[test] #[test]
fn after_struct_name() { fn after_struct_name() {
// FIXME: This should emit `kw where` only // FIXME: This should emit `kw where`
check( check(r"struct Struct $0", expect![[r#""#]]);
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
"#]],
);
} }
#[test] #[test]
fn after_fn_name() { fn after_fn_name() {
// FIXME: This should emit `kw where` only // FIXME: This should emit `kw where`
check( check(r"fn func() $0", expect![[r#""#]]);
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
"#]],
);
} }
#[test] #[test]
fn before_record_field() { fn before_record_field() {
// FIXME: This should emit visibility qualifiers
check( check(
r#" r#"
struct Foo { struct Foo {
@ -147,10 +108,6 @@ struct Foo {
pub f: i32, pub f: i32,
} }
"#, "#,
expect![[r#" expect![[r#""#]],
kw pub
kw pub(crate)
kw pub(super)
"#]],
) )
} }

View file

@ -137,6 +137,7 @@ fn after_visibility() {
expect![[r#" expect![[r#"
kw const kw const
kw enum kw enum
kw extern
kw fn kw fn
kw mod kw mod
kw static kw static
@ -152,12 +153,10 @@ fn after_visibility() {
#[test] #[test]
fn after_visibility_unsafe() { fn after_visibility_unsafe() {
// FIXME this shouldn't show `impl`
check( check(
r#"pub unsafe $0"#, r#"pub unsafe $0"#,
expect![[r#" expect![[r#"
kw fn kw fn
kw impl
kw trait kw trait
"#]], "#]],
); );
@ -178,7 +177,6 @@ fn in_impl_assoc_item_list() {
kw pub(super) kw pub(super)
kw self:: kw self::
kw super:: kw super::
kw type
kw unsafe kw unsafe
"#]], "#]],
) )
@ -199,7 +197,6 @@ fn in_impl_assoc_item_list_after_attr() {
kw pub(super) kw pub(super)
kw self:: kw self::
kw super:: kw super::
kw type
kw unsafe kw unsafe
"#]], "#]],
) )
@ -249,16 +246,9 @@ impl Test for () {
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
ta type Type1 = ta type Type1 =
kw const
kw crate:: kw crate::
kw fn
kw pub
kw pub(crate)
kw pub(super)
kw self:: kw self::
kw super:: kw super::
kw type
kw unsafe
"#]], "#]],
); );
} }

View file

@ -38,6 +38,7 @@ struct Foo<'lt, T, const C: usize> {
#[test] #[test]
fn tuple_struct_field() { fn tuple_struct_field() {
// FIXME: This should emit visibility qualifiers
check( check(
r#" r#"
struct Foo<'lt, T, const C: usize>(f$0); struct Foo<'lt, T, const C: usize>(f$0);
@ -56,9 +57,6 @@ struct Foo<'lt, T, const C: usize>(f$0);
un Union un Union
bt u32 bt u32
kw crate:: kw crate::
kw pub
kw pub(crate)
kw pub(super)
kw self:: kw self::
kw super:: kw super::
"#]], "#]],