Skip to content

Commit

Permalink
Auto merge of #12459 - Veykril:completions, r=Veykril
Browse files Browse the repository at this point in the history
internal: Clean up keyword completion handling

#12144
  • Loading branch information
bors committed Jun 3, 2022
2 parents d0a7ad4 + 2a60b84 commit d06d0f8
Show file tree
Hide file tree
Showing 15 changed files with 291 additions and 396 deletions.
21 changes: 21 additions & 0 deletions crates/ide-completion/src/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub(crate) mod attribute;
pub(crate) mod dot;
pub(crate) mod expr;
pub(crate) mod extern_abi;
pub(crate) mod field;
pub(crate) mod flyimport;
pub(crate) mod fn_param;
pub(crate) mod format_string;
Expand Down Expand Up @@ -110,6 +111,26 @@ impl Completions {
["self", "super", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
}

pub(crate) fn add_keyword_snippet(&mut self, 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(self);
}

pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
ctx.process_all_names(&mut |name, res| match res {
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
Expand Down
10 changes: 5 additions & 5 deletions crates/ide-completion/src/completions/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
return;
}

let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update) =
let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update, after_if_expr) =
match ctx.nameref_ctx() {
Some(NameRefContext {
path_ctx:
Some(PathCompletionCtx {
kind: PathKind::Expr { in_block_expr, in_loop_body },
kind: PathKind::Expr { in_block_expr, in_loop_body, after_if_expr },
is_absolute_path,
qualifier,
..
Expand All @@ -33,6 +33,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
*in_block_expr,
*in_loop_body,
record_expr.as_ref().map_or(false, |&(_, it)| it),
*after_if_expr,
),
_ => return,
};
Expand Down Expand Up @@ -177,8 +178,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
});

if !is_func_update {
let mut add_keyword =
|kw, snippet| super::keyword::add_keyword(acc, ctx, kw, snippet);
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);

if ctx.expects_expression() {
if !in_block_expr {
Expand All @@ -202,7 +202,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
add_keyword("let", "let");
}

if ctx.after_if() {
if after_if_expr {
add_keyword("else", "else {\n $0\n}");
add_keyword("else if", "else if $1 {\n $0\n}");
}
Expand Down
33 changes: 33 additions & 0 deletions crates/ide-completion/src/completions/field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Completion of field list position.
use crate::{
context::{IdentContext, NameContext, NameKind, NameRefContext, PathCompletionCtx, PathKind},
CompletionContext, Completions,
};

pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext) {
match &ctx.ident_ctx {
IdentContext::Name(NameContext { kind: NameKind::RecordField, .. })
| IdentContext::NameRef(NameRefContext {
path_ctx:
Some(PathCompletionCtx {
has_macro_bang: false,
is_absolute_path: false,
qualifier: None,
parent: None,
kind: PathKind::Type { in_tuple_struct: true },
has_type_args: false,
..
}),
..
}) => {
if ctx.qualifier_ctx.vis_node.is_none() {
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
add_keyword("pub(crate)", "pub(crate)");
add_keyword("pub(super)", "pub(super)");
add_keyword("pub", "pub");
}
}
_ => return,
}
}
13 changes: 7 additions & 6 deletions crates/ide-completion/src/completions/flyimport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
if !ctx.config.enable_imports_on_the_fly {
return None;
}
if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use))
if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use | PathKind::Item { .. }))
|| ctx.is_path_disallowed()
|| ctx.expects_item()
|| ctx.expects_assoc_item()
{
return None;
}
Expand Down Expand Up @@ -160,7 +158,10 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
(_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
// and so are macros(except for attributes)
(
PathKind::Expr { .. } | PathKind::Type | PathKind::Item { .. } | PathKind::Pat,
PathKind::Expr { .. }
| PathKind::Type { .. }
| PathKind::Item { .. }
| PathKind::Pat,
ItemInNs::Macros(mac),
) => mac.is_fn_like(ctx.db),
(PathKind::Item { .. }, _) => true,
Expand All @@ -170,14 +171,14 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
(PathKind::Pat, ItemInNs::Types(_)) => true,
(PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),

(PathKind::Type, ItemInNs::Types(ty)) => {
(PathKind::Type { .. }, ItemInNs::Types(ty)) => {
if matches!(ctx.completion_location, Some(ImmediateLocation::TypeBound)) {
matches!(ty, ModuleDef::Trait(_))
} else {
true
}
}
(PathKind::Type, ItemInNs::Values(_)) => false,
(PathKind::Type { .. }, ItemInNs::Values(_)) => false,

(PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(ctx.db),
(PathKind::Attr { .. }, _) => false,
Expand Down
86 changes: 80 additions & 6 deletions crates/ide-completion/src/completions/item_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,98 @@
use crate::{
completions::module_or_fn_macro,
context::{PathCompletionCtx, PathKind, PathQualifierCtx},
context::{ItemListKind, PathCompletionCtx, PathKind, PathQualifierCtx},
CompletionContext, 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| acc.add_keyword_snippet(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 ctx.is_non_trivial_path() {
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, .. }) => {
Expand All @@ -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) {
Expand Down
115 changes: 24 additions & 91 deletions crates/ide-completion/src/completions/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,106 +2,39 @@
//! - `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 syntax::ast::Item;

use crate::{
context::{NameRefContext, PathKind},
CompletionContext, CompletionItem, CompletionItemKind, Completions,
};
use crate::{context::NameRefContext, CompletionContext, Completions};

pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
if matches!(ctx.nameref_ctx(), Some(NameRefContext { record_expr: Some(_), .. })) {
cov_mark::hit!(no_keyword_completion_in_record_lit);
return;
}
if ctx.is_non_trivial_path() {
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
return;
}
if ctx.pattern_ctx.is_some() {
return;
}

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;
}
if ctx.has_unfinished_impl_or_trait_prev_sibling() {
add_keyword("where", "where");
if ctx.has_impl_prev_sibling() {
add_keyword("for", "for");
}
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}")
let item = match ctx.nameref_ctx() {
Some(NameRefContext { keyword: Some(item), record_expr: None, .. })
if !ctx.is_non_trivial_path() =>
{
item
}
_ => return,
};

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) {
let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);

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);
match item {
Item::Impl(it) => {
if it.for_token().is_none() && it.trait_().is_none() && it.self_ty().is_some() {
add_keyword("for", "for");
}
add_keyword("where", "where");
}
None => {
item.insert_text(if snippet.contains('$') { kw } else { snippet });
Item::Enum(_)
| Item::Fn(_)
| Item::Struct(_)
| Item::Trait(_)
| Item::TypeAlias(_)
| Item::Union(_) => {
add_keyword("where", "where");
}
};
item.add_to(acc);
_ => (),
}
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit d06d0f8

Please sign in to comment.