diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 72a8a5ebe1..9a4cd8628a 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs @@ -1,4 +1,6 @@ -//! Completes keywords. +//! Completes keywords, except: +//! - `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::{SyntaxKind, T}; @@ -25,18 +27,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte return; } - // Suggest .await syntax for types that implement Future trait - if let Some(receiver) = ctx.dot_receiver() { - if let Some(ty) = ctx.sema.type_of_expr(receiver) { - if ty.impls_future(ctx.db) { - let mut item = - CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await"); - item.kind(CompletionItemKind::Keyword).detail("expr.await"); - item.add_to(acc); - } - }; - } - let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet); let expects_assoc_item = ctx.expects_assoc_item(); diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index 4871698b24..8aaaa10477 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs @@ -42,6 +42,13 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { None => return, }; + // Suggest .await syntax for types that implement Future trait + if receiver_ty.impls_future(ctx.db) { + let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await"); + item.kind(CompletionItemKind::Keyword).detail("expr.await"); + item.add_to(acc); + } + let cap = match ctx.config.snippet_cap { Some(it) => it, None => return, diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index ed0f902017..1d3a0ec868 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -6,10 +6,12 @@ use hir::HasVisibility; use rustc_hash::FxHashSet; use syntax::{ast, AstNode}; -use crate::{context::PathCompletionContext, CompletionContext, Completions}; +use crate::{ + context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, Completions, +}; pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { - if ctx.is_path_disallowed() { + if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() { return; } let (path, use_tree_parent) = match &ctx.path_context { @@ -26,10 +28,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon let context_module = ctx.scope.module(); - if ctx.expects_item() || ctx.expects_assoc_item() { + if let Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) = + ctx.completion_location + { if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { - let module_scope = module.scope(ctx.db, context_module); - for (name, def) in module_scope { + for (name, def) in module.scope(ctx.db, context_module) { if let hir::ScopeDef::MacroDef(macro_def) = def { if macro_def.is_fn_like() { acc.add_macro(ctx, Some(name.clone()), macro_def); diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 6942a305b4..eaf117d678 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -12,7 +12,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC if ctx.in_use_tree() { // only show modules in a fresh UseTree - cov_mark::hit!(only_completes_modules_in_import); + cov_mark::hit!(unqualified_path_only_modules_in_import); ctx.scope.process_all_names(&mut |name, res| { if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { acc.add_resolution(ctx, name, &res); @@ -24,37 +24,39 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC return; } std::array::IntoIter::new(["self", "super", "crate"]).for_each(|kw| acc.add_keyword(ctx, kw)); - if let Some(ImmediateLocation::Visibility(_)) = ctx.completion_location { - return; - } - if ctx.expects_item() || ctx.expects_assoc_item() { - // only show macros in {Assoc}ItemList - ctx.scope.process_all_names(&mut |name, res| { - if let hir::ScopeDef::MacroDef(mac) = res { - if mac.is_fn_like() { - acc.add_macro(ctx, Some(name.clone()), mac); + match &ctx.completion_location { + Some(ImmediateLocation::Visibility(_)) => return, + Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => { + // only show macros in {Assoc}ItemList + ctx.scope.process_all_names(&mut |name, res| { + if let hir::ScopeDef::MacroDef(mac) = res { + if mac.is_fn_like() { + acc.add_macro(ctx, Some(name.clone()), mac); + } } - } - if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { - acc.add_resolution(ctx, name, &res); - } - }); - return; - } - - if matches!(&ctx.completion_location, Some(ImmediateLocation::TypeBound)) { - ctx.scope.process_all_names(&mut |name, res| { - let add_resolution = match res { - ScopeDef::MacroDef(mac) => mac.is_fn_like(), - ScopeDef::ModuleDef(hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_)) => true, - _ => false, - }; - if add_resolution { - acc.add_resolution(ctx, name, &res); - } - }); - return; + if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { + acc.add_resolution(ctx, name, &res); + } + }); + return; + } + Some(ImmediateLocation::TypeBound) => { + ctx.scope.process_all_names(&mut |name, res| { + let add_resolution = match res { + ScopeDef::MacroDef(mac) => mac.is_fn_like(), + ScopeDef::ModuleDef(hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_)) => { + true + } + _ => false, + }; + if add_resolution { + acc.add_resolution(ctx, name, &res); + } + }); + return; + } + _ => (), } if !ctx.expects_type() { diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index bd24bbc88b..cbe21b35f2 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs @@ -1,4 +1,8 @@ //! Patterns telling us certain facts about current syntax element, they are used in completion context +//! +//! Most logic in this module first expands the token below the cursor to a maximum node that acts similar to the token itself. +//! This means we for example expand a NameRef token to its outermost Path node, as semantically these act in the same location +//! and the completions usually query for path specific things on the Path context instead. This simplifies some location handling. use hir::Semantics; use ide_db::RootDatabase; diff --git a/crates/ide_completion/src/tests.rs b/crates/ide_completion/src/tests.rs index 9018e913c3..3e65c65963 100644 --- a/crates/ide_completion/src/tests.rs +++ b/crates/ide_completion/src/tests.rs @@ -201,7 +201,7 @@ pub(crate) fn check_pattern_is_not_applicable(code: &str, check: fn(SyntaxElemen pub(crate) fn get_all_items(config: CompletionConfig, code: &str) -> Vec { let (db, position) = position(code); - crate::completions(&db, &config, position).unwrap().into() + crate::completions(&db, &config, position).map_or_else(Vec::default, Into::into) } fn check_no_completion(ra_fixture: &str) { diff --git a/crates/ide_completion/src/tests/item.rs b/crates/ide_completion/src/tests/item.rs index c9629c9d13..f4e1301832 100644 --- a/crates/ide_completion/src/tests/item.rs +++ b/crates/ide_completion/src/tests/item.rs @@ -68,7 +68,7 @@ fn after_trait_name_in_trait_def() { } #[test] -fn after_trait_or_target_name_in_impl() { +fn after_target_name_in_impl() { check( r"impl Trait $0", expect![[r#" @@ -76,6 +76,8 @@ fn after_trait_or_target_name_in_impl() { kw for "#]], ); + // FIXME: This should emit `kw where` + check(r"impl Trait for Type $0", expect![[r#""#]]); } #[test] diff --git a/crates/ide_completion/src/tests/use_tree.rs b/crates/ide_completion/src/tests/use_tree.rs index a4017e937f..853081c9be 100644 --- a/crates/ide_completion/src/tests/use_tree.rs +++ b/crates/ide_completion/src/tests/use_tree.rs @@ -10,7 +10,7 @@ fn check(ra_fixture: &str, expect: Expect) { #[test] fn use_tree_start() { - cov_mark::check!(only_completes_modules_in_import); + cov_mark::check!(unqualified_path_only_modules_in_import); check( r#" //- /lib.rs crate:main deps:other_crate diff --git a/crates/ide_completion/src/tests/visibility.rs b/crates/ide_completion/src/tests/visibility.rs index 41820ac180..984155cfd2 100644 --- a/crates/ide_completion/src/tests/visibility.rs +++ b/crates/ide_completion/src/tests/visibility.rs @@ -23,3 +23,35 @@ pub($0) "#]], ); } + +#[test] +fn after_in_kw() { + check( + r#" +pub(in $0) +"#, + expect![[r#" + kw self + kw super + kw crate + "#]], + ); +} + +#[test] +fn qualified() { + // FIXME: only show parent modules + check( + r#" +mod foo { + pub(in crate::$0) +} + +mod bar {} +"#, + expect![[r#" + md bar + md foo + "#]], + ); +}