Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide-completion/src/completions.rs1
-rw-r--r--crates/ide-completion/src/completions/dot.rs14
-rw-r--r--crates/ide-completion/src/completions/expr.rs121
-rw-r--r--crates/ide-completion/src/completions/flyimport.rs44
-rw-r--r--crates/ide-completion/src/completions/item_list.rs166
-rw-r--r--crates/ide-completion/src/completions/item_list/trait_impl.rs (renamed from crates/ide-completion/src/completions/trait_impl.rs)113
-rw-r--r--crates/ide-completion/src/completions/keyword.rs6
-rw-r--r--crates/ide-completion/src/completions/record.rs37
-rw-r--r--crates/ide-completion/src/completions/type.rs3
-rw-r--r--crates/ide-completion/src/context.rs290
-rw-r--r--crates/ide-completion/src/lib.rs2
-rw-r--r--crates/ide-completion/src/patterns.rs29
-rw-r--r--crates/ide-completion/src/render.rs10
-rw-r--r--crates/ide-completion/src/render/macro_.rs4
14 files changed, 403 insertions, 437 deletions
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index b6358d4f40..b076358443 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -16,7 +16,6 @@ pub(crate) mod pattern;
pub(crate) mod postfix;
pub(crate) mod record;
pub(crate) mod snippet;
-pub(crate) mod trait_impl;
pub(crate) mod r#type;
pub(crate) mod use_;
pub(crate) mod vis;
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 1e0b771166..a11652ca30 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -46,12 +46,14 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
return;
}
match ctx.path_context() {
- Some(PathCompletionCtx {
- is_absolute_path: false,
- qualifier: None,
- kind: PathKind::Expr { .. },
- ..
- }) if !ctx.is_path_disallowed() => {}
+ Some(
+ path_ctx @ PathCompletionCtx {
+ is_absolute_path: false,
+ qualifier: None,
+ kind: PathKind::Expr { .. },
+ ..
+ },
+ ) if path_ctx.is_trivial_path() && ctx.qualifier_ctx.none() => {}
_ => return,
}
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 23f47523d6..7c3296a0b3 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -11,32 +11,38 @@ use crate::{
pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) {
let _p = profile::span("complete_expr_path");
- if ctx.is_path_disallowed() {
- return;
- }
- 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, after_if_expr },
- is_absolute_path,
- qualifier,
- ..
- }),
- record_expr,
- ..
- }) => (
- *is_absolute_path,
- qualifier,
- *in_block_expr,
- *in_loop_body,
- record_expr.as_ref().map_or(false, |&(_, it)| it),
- *after_if_expr,
- ),
- _ => return,
- };
+ let (
+ is_absolute_path,
+ qualifier,
+ in_block_expr,
+ in_loop_body,
+ is_func_update,
+ after_if_expr,
+ wants_mut_token,
+ ) = match ctx.nameref_ctx() {
+ Some(NameRefContext {
+ path_ctx:
+ Some(PathCompletionCtx {
+ kind:
+ PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent },
+ is_absolute_path,
+ qualifier,
+ ..
+ }),
+ record_expr,
+ ..
+ }) if ctx.qualifier_ctx.none() => (
+ *is_absolute_path,
+ qualifier,
+ *in_block_expr,
+ *in_loop_body,
+ record_expr.as_ref().map_or(false, |&(_, it)| it),
+ *after_if_expr,
+ ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false),
+ ),
+ _ => return,
+ };
let scope_def_applicable = |def| {
use hir::{GenericParam::*, ModuleDef::*};
@@ -164,12 +170,43 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
None if is_absolute_path => acc.add_crate_roots(ctx),
None => {
acc.add_nameref_keywords_with_colon(ctx);
- if let Some(hir::Adt::Enum(e)) =
+ if let Some(adt) =
ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
{
- super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
- acc.add_qualified_enum_variant(ctx, variant, path)
- });
+ let self_ty =
+ (|| ctx.sema.to_def(ctx.impl_def.as_ref()?)?.self_ty(ctx.db).as_adt())();
+ let complete_self = self_ty == Some(adt);
+
+ match adt {
+ hir::Adt::Struct(strukt) => {
+ let path = ctx
+ .module
+ .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
+ .filter(|it| it.len() > 1);
+
+ acc.add_struct_literal(ctx, strukt, path, None);
+
+ if complete_self {
+ acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE));
+ }
+ }
+ hir::Adt::Union(un) => {
+ let path = ctx
+ .module
+ .find_use_path(ctx.db, hir::ModuleDef::from(un))
+ .filter(|it| it.len() > 1);
+
+ acc.add_union_literal(ctx, un, path, None);
+ if complete_self {
+ acc.add_union_literal(ctx, un, None, Some(hir::known::SELF_TYPE));
+ }
+ }
+ hir::Adt::Enum(e) => {
+ super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
+ acc.add_qualified_enum_variant(ctx, variant, path)
+ });
+ }
+ }
}
ctx.process_all_names(&mut |name, def| {
if scope_def_applicable(def) {
@@ -180,20 +217,18 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
if !is_func_update {
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
- if ctx.expects_expression() {
- if !in_block_expr {
- add_keyword("unsafe", "unsafe {\n $0\n}");
- }
- add_keyword("match", "match $1 {\n $0\n}");
- add_keyword("while", "while $1 {\n $0\n}");
- add_keyword("while let", "while let $1 = $2 {\n $0\n}");
- add_keyword("loop", "loop {\n $0\n}");
- add_keyword("if", "if $1 {\n $0\n}");
- add_keyword("if let", "if let $1 = $2 {\n $0\n}");
- add_keyword("for", "for $1 in $2 {\n $0\n}");
- add_keyword("true", "true");
- add_keyword("false", "false");
+ if !in_block_expr {
+ add_keyword("unsafe", "unsafe {\n $0\n}");
}
+ add_keyword("match", "match $1 {\n $0\n}");
+ add_keyword("while", "while $1 {\n $0\n}");
+ add_keyword("while let", "while let $1 = $2 {\n $0\n}");
+ add_keyword("loop", "loop {\n $0\n}");
+ add_keyword("if", "if $1 {\n $0\n}");
+ add_keyword("if let", "if let $1 = $2 {\n $0\n}");
+ add_keyword("for", "for $1 in $2 {\n $0\n}");
+ add_keyword("true", "true");
+ add_keyword("false", "false");
if ctx.previous_token_is(T![if])
|| ctx.previous_token_is(T![while])
@@ -207,7 +242,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
add_keyword("else if", "else if $1 {\n $0\n}");
}
- if ctx.expects_ident_ref_expr() {
+ if wants_mut_token {
add_keyword("mut", "mut ");
}
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 22068096ba..901f7519d2 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -8,7 +8,7 @@ use itertools::Itertools;
use syntax::{AstNode, SyntaxNode, T};
use crate::{
- context::{CompletionContext, PathKind},
+ context::{CompletionContext, NameRefContext, PathCompletionCtx, PathKind, PatternContext},
patterns::ImmediateLocation,
render::{render_resolution_with_import, RenderContext},
};
@@ -110,16 +110,26 @@ 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 | PathKind::Item { .. }))
- || ctx.is_path_disallowed()
- {
- return None;
- }
- // FIXME: This should be encoded in a different way
- if ctx.pattern_ctx.is_none() && ctx.path_context().is_none() && !ctx.has_dot_receiver() {
- // completion inside `ast::Name` of a item declaration
- return None;
- }
+ let path_kind = match ctx.nameref_ctx() {
+ Some(NameRefContext { path_ctx: Some(PathCompletionCtx { kind, .. }), .. })
+ if matches!(
+ kind,
+ PathKind::Expr { .. }
+ | PathKind::Type { .. }
+ | PathKind::Attr { .. }
+ | PathKind::Derive
+ | PathKind::Pat
+ ) =>
+ {
+ Some(kind)
+ }
+ Some(NameRefContext { dot_access: Some(_), .. }) => None,
+ None if matches!(ctx.pattern_ctx, Some(PatternContext { record_pat: None, .. })) => {
+ Some(&PathKind::Pat)
+ }
+ _ => return None,
+ };
+
let potential_import_name = {
let token_kind = ctx.token.kind();
if matches!(token_kind, T![.] | T![::]) {
@@ -138,18 +148,10 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
return None;
}
- let path_kind = match ctx.path_kind() {
- Some(kind) => Some(kind),
- None if ctx.pattern_ctx.is_some() => Some(PathKind::Pat),
- None => None,
- };
let ns_filter = |import: &LocatedImport| {
let path_kind = match path_kind {
- Some(path_kind) => path_kind,
- None => match import.original_item {
- ItemInNs::Macros(mac) => return mac.is_fn_like(ctx.db),
- _ => return true,
- },
+ Some(it) => it,
+ None => return true,
};
match (path_kind, import.original_item) {
// Aren't handled in flyimport
diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs
index 287cf46f2e..d44bf0a6ab 100644
--- a/crates/ide-completion/src/completions/item_list.rs
+++ b/crates/ide-completion/src/completions/item_list.rs
@@ -6,88 +6,42 @@ use crate::{
CompletionContext, Completions,
};
+mod trait_impl;
+
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() {
- Some(PathCompletionCtx {
- kind: PathKind::Item { kind },
- is_absolute_path,
- qualifier,
- ..
- }) => (is_absolute_path, qualifier, Some(kind)),
- Some(PathCompletionCtx {
- kind: PathKind::Expr { in_block_expr: true, .. },
- is_absolute_path,
- qualifier,
- ..
- }) => (is_absolute_path, qualifier, None),
+ if let Some(_) = ctx.name_ctx() {
+ trait_impl::complete_trait_impl(acc, ctx);
+ return;
+ }
+
+ let (&is_absolute_path, path_qualifier, kind, is_trivial_path) = match ctx.path_context() {
+ Some(
+ ctx @ PathCompletionCtx {
+ kind: PathKind::Item { kind },
+ is_absolute_path,
+ qualifier,
+ ..
+ },
+ ) => (is_absolute_path, qualifier, Some(kind), ctx.is_trivial_path()),
+ Some(
+ ctx @ PathCompletionCtx {
+ kind: PathKind::Expr { in_block_expr: true, .. },
+ is_absolute_path,
+ qualifier,
+ ..
+ },
+ ) => (is_absolute_path, qualifier, None, ctx.is_trivial_path()),
_ => 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");
- }
+ if matches!(kind, Some(ItemListKind::TraitImpl)) {
+ trait_impl::complete_trait_impl(acc, ctx);
+ }
- add_keyword("fn", "fn $1($2) {\n $0\n}");
- add_keyword("unsafe", "unsafe");
- add_keyword("const", "const $0");
- }
- }
- break 'block;
+ if is_trivial_path {
+ add_keywords(acc, ctx, kind);
}
if kind.is_none() {
@@ -121,3 +75,65 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
None => {}
}
}
+
+fn add_keywords(acc: &mut Completions, ctx: &CompletionContext, kind: Option<&ItemListKind>) {
+ 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);
+
+ 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}");
+ }
+ }
+ return;
+ }
+
+ 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");
+ }
+ }
+}
diff --git a/crates/ide-completion/src/completions/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 54bd77526e..8a2bbae73f 100644
--- a/crates/ide-completion/src/completions/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -43,6 +43,10 @@ use syntax::{
use text_edit::TextEdit;
use crate::{
+ context::{
+ IdentContext, ItemListKind, NameContext, NameKind, NameRefContext, PathCompletionCtx,
+ PathKind,
+ },
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
};
@@ -54,7 +58,6 @@ enum ImplCompletionKind {
Const,
}
-// FIXME: Make this a submodule of [`item_list`]
pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
if let Some((kind, replacement_range, impl_def)) = completion_match(ctx) {
if let Some(hir_impl) = ctx.sema.to_def(&impl_def) {
@@ -77,73 +80,49 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
}
}
-// FIXME: This should be lifted out so that we can do proper smart item keyword completions
fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, TextRange, ast::Impl)> {
- let token = ctx.token.clone();
-
- // For keyword without name like `impl .. { fn $0 }`, the current position is inside
- // the whitespace token, which is outside `FN` syntax node.
- // We need to follow the previous token in this case.
- let mut token_before_ws = token.clone();
- if token.kind() == SyntaxKind::WHITESPACE {
- token_before_ws = token.prev_token()?;
- }
-
- let parent_kind = token_before_ws.parent().map_or(SyntaxKind::EOF, |it| it.kind());
- if token.parent().map(|n| n.kind()) == Some(SyntaxKind::ASSOC_ITEM_LIST)
- && matches!(
- token_before_ws.kind(),
- SyntaxKind::SEMICOLON | SyntaxKind::R_CURLY | SyntaxKind::L_CURLY
- )
- {
- let impl_def = ast::Impl::cast(token.parent()?.parent()?)?;
- let kind = ImplCompletionKind::All;
- let replacement_range = TextRange::empty(ctx.position.offset);
- Some((kind, replacement_range, impl_def))
- } else {
- let impl_item_offset = match token_before_ws.kind() {
- // `impl .. { const $0 }`
- // ERROR 0
- // CONST_KW <- *
- T![const] => 0,
- // `impl .. { fn/type $0 }`
- // FN/TYPE_ALIAS 0
- // FN_KW <- *
- T![fn] | T![type] => 0,
- // `impl .. { fn/type/const foo$0 }`
- // FN/TYPE_ALIAS/CONST 1
- // NAME 0
- // IDENT <- *
- SyntaxKind::IDENT if parent_kind == SyntaxKind::NAME => 1,
- // `impl .. { foo$0 }`
- // MACRO_CALL 3
- // PATH 2
- // PATH_SEGMENT 1
- // NAME_REF 0
- // IDENT <- *
- SyntaxKind::IDENT if parent_kind == SyntaxKind::NAME_REF => 3,
- _ => return None,
- };
-
- let impl_item = token_before_ws.ancestors().nth(impl_item_offset)?;
- // Must directly belong to an impl block.
- // IMPL
- // ASSOC_ITEM_LIST
- // <item>
- let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
- let kind = match impl_item.kind() {
- // `impl ... { const $0 fn/type/const }`
- _ if token_before_ws.kind() == T![const] => ImplCompletionKind::Const,
- SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
- SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
- SyntaxKind::FN => ImplCompletionKind::Fn,
- SyntaxKind::MACRO_CALL => ImplCompletionKind::All,
- _ => return None,
- };
-
- let replacement_range = replacement_range(ctx, &impl_item);
-
- Some((kind, replacement_range, impl_def))
+ match &ctx.ident_ctx {
+ IdentContext::Name(NameContext { name, kind, .. }) => {
+ let kind = match kind {
+ NameKind::Const => ImplCompletionKind::Const,
+ NameKind::Function => ImplCompletionKind::Fn,
+ NameKind::TypeAlias => ImplCompletionKind::TypeAlias,
+ _ => return None,
+ };
+ let token = ctx.token.clone();
+ let item = match name {
+ Some(name) => name.syntax().parent(),
+ None => {
+ if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
+ .parent()
+ }
+ }?;
+ Some((
+ kind,
+ replacement_range(ctx, &item),
+ // item -> ASSOC_ITEM_LIST -> IMPL
+ ast::Impl::cast(item.parent()?.parent()?)?,
+ ))
+ }
+ IdentContext::NameRef(NameRefContext {
+ nameref,
+ path_ctx:
+ Some(
+ path_ctx @ PathCompletionCtx {
+ kind: PathKind::Item { kind: ItemListKind::TraitImpl },
+ ..
+ },
+ ),
+ ..
+ }) if path_ctx.is_trivial_path() => Some((
+ ImplCompletionKind::All,
+ match nameref {
+ Some(name) => name.syntax().text_range(),
+ None => TextRange::empty(ctx.position.offset),
+ },
+ ctx.impl_def.clone()?,
+ )),
+ _ => None,
}
}
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index e870ecc229..65fa119178 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -8,11 +8,7 @@ use crate::{context::NameRefContext, CompletionContext, Completions};
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
let item = match ctx.nameref_ctx() {
- Some(NameRefContext { keyword: Some(item), record_expr: None, .. })
- if !ctx.is_non_trivial_path() =>
- {
- item
- }
+ Some(NameRefContext { keyword: Some(item), record_expr: None, .. }) => item,
_ => return,
};
diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs
index 6717ca0a0e..65805dba1c 100644
--- a/crates/ide-completion/src/completions/record.rs
+++ b/crates/ide-completion/src/completions/record.rs
@@ -71,43 +71,6 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
Some(())
}
-pub(crate) fn complete_record_literal(
- acc: &mut Completions,
- ctx: &CompletionContext,
-) -> Option<()> {
- if !ctx.expects_expression() {
- return None;
- }
-
- match ctx.expected_type.as_ref()?.as_adt()? {
- hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => {
- let path = ctx
- .module
- .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
- .filter(|it| it.len() > 1);
-
- acc.add_struct_literal(ctx, strukt, path, None);
-
- let impl_ = ctx.impl_def.as_ref()?;
- let impl_adt = ctx.sema.to_def(impl_)?.self_ty(ctx.db).as_adt()?;
- if hir::Adt::Struct(strukt) == impl_adt {
- acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE));
- }
- }
- hir::Adt::Union(un) if ctx.path_qual().is_none() => {
- let path = ctx
- .module
- .find_use_path(ctx.db, hir::ModuleDef::from(un))
- .filter(|it| it.len() > 1);
-
- acc.add_union_literal(ctx, un, path, None);
- }
- _ => {}
- };
-
- Some(())
-}
-
#[cfg(test)]
mod tests {
use crate::tests::check_edit;
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index bc8c070c14..9cf0b87ad6 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -13,9 +13,6 @@ use crate::{
pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) {
let _p = profile::span("complete_type_path");
- if ctx.is_path_disallowed() {
- return;
- }
let (&is_absolute_path, qualifier) = match ctx.path_context() {
Some(PathCompletionCtx {
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 6068a9eb32..02307def9e 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -43,12 +43,13 @@ pub(crate) enum Visible {
No,
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) enum PathKind {
Expr {
in_block_expr: bool,
in_loop_body: bool,
after_if_expr: bool,
+ ref_expr_parent: Option<ast::RefExpr>,
},
Type {
in_tuple_struct: bool,
@@ -101,8 +102,6 @@ pub(crate) struct PathCompletionCtx {
pub(super) is_absolute_path: bool,
/// The qualifier of the current path if it exists.
pub(super) qualifier: Option<PathQualifierCtx>,
- #[allow(dead_code)]
- // FIXME: use this
/// The parent of the path we are completing.
pub(super) parent: Option<ast::Path>,
pub(super) kind: PathKind,
@@ -110,6 +109,23 @@ pub(crate) struct PathCompletionCtx {
pub(super) has_type_args: bool,
}
+impl PathCompletionCtx {
+ pub(super) fn is_trivial_path(&self) -> bool {
+ matches!(
+ self,
+ PathCompletionCtx {
+ has_call_parens: false,
+ has_macro_bang: false,
+ is_absolute_path: false,
+ qualifier: None,
+ parent: None,
+ has_type_args: false,
+ ..
+ }
+ )
+ }
+}
+
#[derive(Debug)]
pub(crate) struct PathQualifierCtx {
pub(crate) path: ast::Path,
@@ -341,47 +357,14 @@ impl<'a> CompletionContext<'a> {
matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
}
- pub(crate) fn expects_ident_ref_expr(&self) -> bool {
- matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
- }
-
- // FIXME: This shouldn't exist
- pub(crate) fn is_path_disallowed(&self) -> bool {
- !self.qualifier_ctx.none()
- || (matches!(self.name_ctx(), Some(NameContext { .. })) && self.pattern_ctx.is_none())
- || matches!(self.pattern_ctx, Some(PatternContext { record_pat: Some(_), .. }))
- || matches!(
- self.nameref_ctx(),
- Some(NameRefContext { record_expr: Some((_, false)), .. })
- )
- }
-
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
self.nameref_ctx().and_then(|ctx| ctx.path_ctx.as_ref())
}
- pub(crate) fn expects_expression(&self) -> bool {
- matches!(self.path_context(), Some(PathCompletionCtx { kind: PathKind::Expr { .. }, .. }))
- }
-
- pub(crate) fn is_non_trivial_path(&self) -> bool {
- matches!(
- self.path_context(),
- Some(
- PathCompletionCtx { is_absolute_path: true, .. }
- | PathCompletionCtx { qualifier: Some(_), .. }
- )
- )
- }
-
pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
self.path_context().and_then(|it| it.qualifier.as_ref().map(|it| &it.path))
}
- pub(crate) fn path_kind(&self) -> Option<PathKind> {
- self.path_context().map(|it| it.kind)
- }
-
/// Checks if an item is visible and not `doc(hidden)` at the completion site.
pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
where
@@ -872,7 +855,7 @@ impl<'a> CompletionContext<'a> {
find_node_at_offset(&file_with_fake_ident, offset)
{
let parent = name_ref.syntax().parent()?;
- let (mut nameref_ctx, _) =
+ let (mut nameref_ctx, _, _) =
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
if let Some(path_ctx) = &mut nameref_ctx.path_ctx {
path_ctx.kind = PathKind::Derive;
@@ -920,13 +903,23 @@ impl<'a> CompletionContext<'a> {
self.impl_def = self
.sema
.token_ancestors_with_macros(self.token.clone())
- .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
- .find_map(ast::Impl::cast);
+ .take_while(|it| it.kind() != SOURCE_FILE)
+ .filter_map(ast::Item::cast)
+ .take(2)
+ .find_map(|it| match it {
+ ast::Item::Impl(impl_) => Some(impl_),
+ _ => None,
+ });
self.function_def = self
.sema
.token_ancestors_with_macros(self.token.clone())
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
- .find_map(ast::Fn::cast);
+ .filter_map(ast::Item::cast)
+ .take(2)
+ .find_map(|it| match it {
+ ast::Item::Fn(fn_) => Some(fn_),
+ _ => None,
+ });
match name_like {
ast::NameLike::Lifetime(lifetime) => {
@@ -938,50 +931,10 @@ impl<'a> CompletionContext<'a> {
}
ast::NameLike::NameRef(name_ref) => {
let parent = name_ref.syntax().parent()?;
- let (nameref_ctx, pat_ctx) =
+ let (nameref_ctx, pat_ctx, qualifier_ctx) =
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone());
- // Extract qualifiers
- if let Some(path_ctx) = &nameref_ctx.path_ctx {
- if path_ctx.qualifier.is_none() {
- let top = match path_ctx.kind {
- PathKind::Expr { in_block_expr: true, .. } => parent
- .ancestors()
- .find(|it| ast::PathExpr::can_cast(it.kind()))
- .and_then(|p| {
- let parent = p.parent()?;
- if ast::StmtList::can_cast(parent.kind()) {
- Some(p)
- } else if ast::ExprStmt::can_cast(parent.kind()) {
- Some(parent)
- } else {
- None
- }
- }),
- PathKind::Item { .. } => {
- parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
- }
- _ => None,
- };
- if let Some(top) = top {
- if let Some(NodeOrToken::Node(error_node)) =
- syntax::algo::non_trivia_sibling(
- top.into(),
- syntax::Direction::Prev,
- )
- {
- if error_node.kind() == SyntaxKind::ERROR {
- self.qualifier_ctx.unsafe_tok = error_node
- .children_with_tokens()
- .filter_map(NodeOrToken::into_token)
- .find(|it| it.kind() == T![unsafe]);
- self.qualifier_ctx.vis_node =
- error_node.children().find_map(ast::Visibility::cast);
- }
- }
- }
- }
- }
+ self.qualifier_ctx = qualifier_ctx;
self.ident_ctx = IdentContext::NameRef(nameref_ctx);
self.pattern_ctx = pat_ctx;
}
@@ -1070,40 +1023,44 @@ impl<'a> CompletionContext<'a> {
original_file: &SyntaxNode,
name_ref: ast::NameRef,
parent: SyntaxNode,
- ) -> (NameRefContext, Option<PatternContext>) {
+ ) -> (NameRefContext, Option<PatternContext>, QualifierCtx) {
let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
- let mut nameref_ctx = NameRefContext {
- dot_access: None,
- path_ctx: None,
- nameref,
- record_expr: None,
- keyword: None,
- };
+ let mut res = (
+ NameRefContext {
+ dot_access: None,
+ path_ctx: None,
+ nameref,
+ record_expr: None,
+ keyword: None,
+ },
+ None,
+ QualifierCtx::default(),
+ );
+ let (nameref_ctx, pattern_ctx, qualifier_ctx) = &mut res;
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
nameref_ctx.record_expr =
find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
.zip(Some(false));
- return (nameref_ctx, None);
+ return res;
}
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
- let pat_ctx =
- pattern_context_for(original_file, record_field.parent_record_pat().clone().into());
- return (
- nameref_ctx,
- Some(PatternContext {
- param_ctx: None,
- has_type_ascription: false,
- ref_token: None,
- mut_token: None,
- record_pat: find_node_in_file_compensated(
- original_file,
- &record_field.parent_record_pat(),
- ),
- ..pat_ctx
- }),
- );
+ *pattern_ctx = Some(PatternContext {
+ param_ctx: None,
+ has_type_ascription: false,
+ ref_token: None,
+ mut_token: None,
+ record_pat: find_node_in_file_compensated(
+ original_file,
+ &record_field.parent_record_pat(),
+ ),
+ ..pattern_context_for(
+ original_file,
+ record_field.parent_record_pat().clone().into(),
+ )
+ });
+ return res;
}
let segment = match_ast! {
@@ -1123,7 +1080,7 @@ impl<'a> CompletionContext<'a> {
kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
receiver
});
- return (nameref_ctx, None);
+ return res;
},
ast::MethodCallExpr(method) => {
let receiver = find_in_original_file(method.receiver(), original_file);
@@ -1132,9 +1089,9 @@ impl<'a> CompletionContext<'a> {
kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
receiver
});
- return (nameref_ctx, None);
+ return res;
},
- _ => return (nameref_ctx, None),
+ _ => return res,
}
};
@@ -1148,7 +1105,6 @@ impl<'a> CompletionContext<'a> {
kind: PathKind::Item { kind: ItemListKind::SourceFile },
has_type_args: false,
};
- let mut pat_ctx = None;
let is_in_block = |it: &SyntaxNode| {
it.parent()
@@ -1205,9 +1161,9 @@ impl<'a> CompletionContext<'a> {
None
};
- let kind = path.syntax().ancestors().find_map(|it| {
- // using Option<Option<PathKind>> as extra controlflow
- let kind = match_ast! {
+ // Infer the path kind
+ let kind = path.syntax().parent().and_then(|it| {
+ match_ast! {
match it {
ast::PathType(it) => Some(PathKind::Type {
in_tuple_struct: it.syntax().parent().map_or(false, |it| ast::TupleField::can_cast(it.kind()))
@@ -1217,7 +1173,7 @@ impl<'a> CompletionContext<'a> {
if ast::ExprStmt::can_cast(p.kind()) {
if let Some(kind) = inbetween_body_and_decl_check(p) {
nameref_ctx.keyword = Some(kind);
- return Some(None);
+ return None;
}
}
}
@@ -1228,27 +1184,29 @@ impl<'a> CompletionContext<'a> {
let in_block_expr = is_in_block(it.syntax());
let in_loop_body = is_in_loop_body(it.syntax());
let after_if_expr = after_if_expr(it.syntax().clone());
+ let ref_expr_parent = path.as_single_name_ref()
+ .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
- Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr })
+ Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent })
},
ast::TupleStructPat(it) => {
path_ctx.has_call_parens = true;
- pat_ctx = Some(pattern_context_for(original_file, it.into()));
+ *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
Some(PathKind::Pat)
},
ast::RecordPat(it) => {
path_ctx.has_call_parens = true;
- pat_ctx = Some(pattern_context_for(original_file, it.into()));
+ *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
Some(PathKind::Pat)
},
ast::PathPat(it) => {
- pat_ctx = Some(pattern_context_for(original_file, it.into()));
+ *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
Some(PathKind::Pat)
},
ast::MacroCall(it) => {
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
nameref_ctx.keyword = Some(kind);
- return Some(None);
+ return None;
}
path_ctx.has_macro_bang = it.excl_token().is_some();
@@ -1266,21 +1224,23 @@ impl<'a> CompletionContext<'a> {
} else {
ItemListKind::Impl
},
- _ => return Some(None)
+ _ => return None
}
},
- None => return Some(None),
+ None => return None,
} }),
Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
_ => {
- return Some(parent.and_then(ast::MacroExpr::cast).map(|it| {
+ return parent.and_then(ast::MacroExpr::cast).map(|it| {
let in_loop_body = is_in_loop_body(it.syntax());
let in_block_expr = is_in_block(it.syntax());
let after_if_expr = after_if_expr(it.syntax().clone());
fill_record_expr(it.syntax());
- PathKind::Expr { in_block_expr, in_loop_body, after_if_expr }
- }));
+ let ref_expr_parent = path.as_single_name_ref()
+ .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
+ PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent }
+ });
},
}
},
@@ -1302,30 +1262,14 @@ impl<'a> CompletionContext<'a> {
})(),
ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
ast::UseTree(_) => Some(PathKind::Use),
- ast::ItemList(_) => Some(PathKind::Item { kind: ItemListKind::Module }),
- ast::AssocItemList(it) => Some(PathKind::Item { kind: {
- 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,
}
- };
- Some(kind)
- }).flatten();
+ }
+ });
+
match kind {
Some(kind) => path_ctx.kind = kind,
- None => return (nameref_ctx, pat_ctx),
+ None => return res,
}
path_ctx.has_type_args = segment.generic_arg_list().is_some();
@@ -1367,8 +1311,62 @@ impl<'a> CompletionContext<'a> {
path_ctx.is_absolute_path = true;
}
}
+
+ if path_ctx.is_trivial_path() {
+ // fetch the full expression that may have qualifiers attached to it
+ let top_node = match path_ctx.kind {
+ PathKind::Expr { in_block_expr: true, .. } => {
+ parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| {
+ let parent = p.parent()?;
+ if ast::StmtList::can_cast(parent.kind()) {
+ Some(p)
+ } else if ast::ExprStmt::can_cast(parent.kind()) {
+ Some(parent)
+ } else {
+ None
+ }
+ })
+ }
+ PathKind::Item { .. } => {
+ parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
+ }
+ _ => None,
+ };
+ if let Some(top) = top_node {
+ if let Some(NodeOrToken::Node(error_node)) =
+ syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev)
+ {
+ if error_node.kind() == SyntaxKind::ERROR {
+ qualifier_ctx.unsafe_tok = error_node
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|it| it.kind() == T![unsafe]);
+ qualifier_ctx.vis_node =
+ error_node.children().find_map(ast::Visibility::cast);
+ }
+ }
+
+ if let PathKind::Item { .. } = path_ctx.kind {
+ if qualifier_ctx.none() {
+ if let Some(t) = top.first_token() {
+ if let Some(prev) = t
+ .prev_token()
+ .and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev))
+ {
+ if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) {
+ // This was inferred to be an item position path, but it seems
+ // to be part of some other broken node which leaked into an item
+ // list, so return without setting the path context
+ return res;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
nameref_ctx.path_ctx = Some(path_ctx);
- (nameref_ctx, pat_ctx)
+ res
}
}
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index c100dd63ea..9dc367b0bc 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -169,11 +169,9 @@ pub fn completions(
completions::mod_::complete_mod(acc, ctx);
completions::pattern::complete_pattern(acc, ctx);
completions::postfix::complete_postfix(acc, ctx);
- completions::record::complete_record_literal(acc, ctx);
completions::record::complete_record(acc, ctx);
completions::snippet::complete_expr_snippet(acc, ctx);
completions::snippet::complete_item_snippet(acc, ctx);
- completions::trait_impl::complete_trait_impl(acc, ctx);
completions::r#type::complete_type_path(acc, ctx);
completions::r#type::complete_inferred_type(acc, ctx);
completions::use_::complete_use_tree(acc, ctx);
diff --git a/crates/ide-completion/src/patterns.rs b/crates/ide-completion/src/patterns.rs
index 9abbfaa407..761c97b9a9 100644
--- a/crates/ide-completion/src/patterns.rs
+++ b/crates/ide-completion/src/patterns.rs
@@ -30,7 +30,6 @@ pub(crate) enum TypeAnnotation {
/// from which file the nodes are.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum ImmediateLocation {
- RefExpr,
TypeBound,
/// Original file ast node
TypeAnnotation(TypeAnnotation),
@@ -80,7 +79,6 @@ pub(crate) fn determine_location(
let res = match_ast! {
match parent {
- ast::RefExpr(_) => ImmediateLocation::RefExpr,
ast::TypeBound(_) => ImmediateLocation::TypeBound,
ast::TypeBoundList(_) => ImmediateLocation::TypeBound,
ast::GenericArgList(_) => sema
@@ -248,30 +246,3 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> {
}
None
}
-
-#[cfg(test)]
-mod tests {
- use syntax::algo::find_node_at_offset;
-
- use crate::tests::position;
-
- use super::*;
-
- fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) {
- let (db, pos) = position(code);
-
- let sema = Semantics::new(&db);
- let original_file = sema.parse(pos.file_id);
-
- let name_like = find_node_at_offset(original_file.syntax(), pos.offset).unwrap();
- assert_eq!(
- determine_location(&sema, original_file.syntax(), pos.offset, &name_like),
- loc.into()
- );
- }
-
- #[test]
- fn test_ref_expr_loc() {
- check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
- }
-}
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index ca2b3ad343..942dc03368 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -1099,6 +1099,8 @@ fn go(world: &WorldSnapshot) { go(w$0) }
"#,
expect![[r#"
lc world [type+name+local]
+ st WorldSnapshot {…} []
+ st &WorldSnapshot {…} [type]
st WorldSnapshot []
fn go(…) []
"#]],
@@ -1197,6 +1199,8 @@ fn main() {
lc s [name+local]
lc &mut s [type+name+local]
st S []
+ st &mut S [type]
+ st S []
fn main() []
fn foo(…) []
"#]],
@@ -1266,6 +1270,8 @@ fn main() {
lc m [local]
lc t [local]
lc &t [type+local]
+ st S []
+ st &S [type]
st T []
st S []
fn main() []
@@ -1311,6 +1317,8 @@ fn main() {
lc m [local]
lc t [local]
lc &mut t [type+local]
+ st S []
+ st &mut S [type]
st T []
st S []
fn main() []
@@ -1405,6 +1413,8 @@ fn main() {
}
"#,
expect![[r#"
+ st S []
+ st &S [type]
st T []
st S []
fn main() []
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index 5c862f013a..de527860d8 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -34,8 +34,8 @@ fn render(
let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
let needs_bang = match completion.path_context() {
- Some(&PathCompletionCtx { kind, has_macro_bang, .. }) => {
- is_fn_like && kind != PathKind::Use && !has_macro_bang
+ Some(PathCompletionCtx { kind, has_macro_bang, .. }) => {
+ is_fn_like && *kind != PathKind::Use && !has_macro_bang
}
_ => is_fn_like,
};