From 6550a241fb370d21c8a3fc31053ceb823628d42e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 3 Jun 2022 16:11:26 +0200 Subject: [PATCH] More precise where keyword completions --- .../src/completions/item_list.rs | 2 +- .../ide-completion/src/completions/keyword.rs | 47 +++++++------- crates/ide-completion/src/context.rs | 42 ++++++------- crates/ide-completion/src/patterns.rs | 29 --------- crates/ide-completion/src/tests/item.rs | 62 +++++++++++++++---- crates/ide-completion/src/tests/item_list.rs | 1 - crates/ide-completion/src/tests/record.rs | 1 - 7 files changed, 96 insertions(+), 88 deletions(-) diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index b78ed26ec3f..aa0d04cf6cd 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -36,7 +36,7 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) let in_block = matches!(kind, None); 'block: loop { - if path_qualifier.is_some() { + if ctx.is_non_trivial_path() { break 'block; } if !in_trait_impl { diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index d55046e7107..d6df5002f5d 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -2,35 +2,40 @@ //! - `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::ast::Item; + use crate::{ - context::{NameRefContext, PathKind}, - CompletionContext, CompletionItem, CompletionItemKind, Completions, + context::NameRefContext, CompletionContext, CompletionItem, CompletionItemKind, 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 item = match ctx.nameref_ctx() { + Some(NameRefContext { keyword: Some(item), record_expr: None, .. }) + if !ctx.is_non_trivial_path() => + { + item + } + _ => return, + }; let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet); - 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"); + 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"); } - return; + Item::Enum(_) + | Item::Fn(_) + | Item::Struct(_) + | Item::Trait(_) + | Item::TypeAlias(_) + | Item::Union(_) => { + add_keyword("where", "where"); + } + _ => (), } } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index f8073f54310..ccc7c107468 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -185,6 +185,8 @@ pub(super) struct NameRefContext { // FIXME: these fields are actually disjoint -> enum pub(super) dot_access: Option, pub(super) path_ctx: Option, + /// Position where we are only interested in keyword completions + pub(super) keyword: Option, /// The record expression this nameref is a field of pub(super) record_expr: Option<(ast::RecordExpr, bool)>, } @@ -343,21 +345,6 @@ impl<'a> CompletionContext<'a> { matches!(self.completion_location, Some(ImmediateLocation::RefExpr)) } - /// Whether the cursor is right after a trait or impl header. - /// trait Foo ident$0 - // FIXME: This probably shouldn't exist - pub(crate) fn has_unfinished_impl_or_trait_prev_sibling(&self) -> bool { - matches!( - self.prev_sibling, - Some(ImmediatePrevSibling::ImplDefType | ImmediatePrevSibling::TraitDefName) - ) - } - - // FIXME: This probably shouldn't exist - pub(crate) fn has_impl_prev_sibling(&self) -> bool { - matches!(self.prev_sibling, Some(ImmediatePrevSibling::ImplDefType)) - } - pub(crate) fn after_if(&self) -> bool { matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr)) } @@ -1092,8 +1079,13 @@ impl<'a> CompletionContext<'a> { ) -> (NameRefContext, Option) { 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 }; + let mut nameref_ctx = NameRefContext { + dot_access: None, + path_ctx: None, + nameref, + record_expr: None, + keyword: None, + }; if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) { nameref_ctx.record_expr = @@ -1190,7 +1182,7 @@ impl<'a> CompletionContext<'a> { syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev) { if let Some(item) = ast::Item::cast(n) { - match item { + let is_inbetween = match &item { ast::Item::Const(it) => it.body().is_none(), ast::Item::Enum(it) => it.variant_list().is_none(), ast::Item::ExternBlock(it) => it.extern_item_list().is_none(), @@ -1203,13 +1195,13 @@ impl<'a> CompletionContext<'a> { ast::Item::TypeAlias(it) => it.ty().is_none(), ast::Item::Union(it) => it.record_field_list().is_none(), _ => false, + }; + if is_inbetween { + return Some(item); } - } else { - false } - } else { - false } + None }; let kind = path.syntax().ancestors().find_map(|it| { @@ -1222,7 +1214,8 @@ impl<'a> CompletionContext<'a> { ast::PathExpr(it) => { if let Some(p) = it.syntax().parent() { if ast::ExprStmt::can_cast(p.kind()) { - if inbetween_body_and_decl_check(p) { + if let Some(kind) = inbetween_body_and_decl_check(p) { + nameref_ctx.keyword = Some(kind); return Some(None); } } @@ -1250,7 +1243,8 @@ impl<'a> CompletionContext<'a> { Some(PathKind::Pat) }, ast::MacroCall(it) => { - if inbetween_body_and_decl_check(it.syntax().clone()) { + if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { + nameref_ctx.keyword = Some(kind); return Some(None); } diff --git a/crates/ide-completion/src/patterns.rs b/crates/ide-completion/src/patterns.rs index 27b271dde46..34bfa4517cf 100644 --- a/crates/ide-completion/src/patterns.rs +++ b/crates/ide-completion/src/patterns.rs @@ -21,8 +21,6 @@ use crate::tests::check_pattern_is_applicable; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum ImmediatePrevSibling { IfExpr, - TraitDefName, - ImplDefType, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -81,17 +79,6 @@ pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option if it.assoc_item_list().is_none() { - ImmediatePrevSibling::TraitDefName - } else { - return None - }, - ast::Impl(it) => if it.assoc_item_list().is_none() - && (it.for_token().is_none() || it.self_ty().is_some()) { - ImmediatePrevSibling::ImplDefType - } else { - return None - }, _ => return None, } }; @@ -342,22 +329,6 @@ mod tests { check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr); } - #[test] - fn test_impl_prev_sibling() { - check_prev_sibling(r"impl A w$0 ", ImmediatePrevSibling::ImplDefType); - check_prev_sibling(r"impl A w$0 {}", ImmediatePrevSibling::ImplDefType); - check_prev_sibling(r"impl A for A w$0 ", ImmediatePrevSibling::ImplDefType); - check_prev_sibling(r"impl A for A w$0 {}", ImmediatePrevSibling::ImplDefType); - check_prev_sibling(r"impl A for w$0 {}", None); - check_prev_sibling(r"impl A for w$0", None); - } - - #[test] - fn test_trait_prev_sibling() { - check_prev_sibling(r"trait A w$0 ", ImmediatePrevSibling::TraitDefName); - check_prev_sibling(r"trait A w$0 {}", ImmediatePrevSibling::TraitDefName); - } - #[test] fn test_if_expr_prev_sibling() { check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr); diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index 9e50e00ab73..81303eb38f4 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs @@ -76,26 +76,66 @@ fn after_target_name_in_impl() { kw where "#]], ); - // FIXME: This should not emit `kw for` + check( + r"impl Trait f$0", + expect![[r#" + kw for + kw where + "#]], + ); check( r"impl Trait for Type $0", expect![[r#" - kw for kw where "#]], ); } #[test] -fn after_struct_name() { - // FIXME: This should emit `kw where` - check(r"struct Struct $0", expect![[r#""#]]); -} - -#[test] -fn after_fn_name() { - // FIXME: This should emit `kw where` - check(r"fn func() $0", expect![[r#""#]]); +fn completes_where() { + check( + r"struct Struct $0", + expect![[r#" + kw where + "#]], + ); + check( + r"struct Struct $0 {}", + expect![[r#" + kw where + "#]], + ); + // FIXME: This shouldn't be completed here + check( + r"struct Struct $0 ()", + expect![[r#" + kw where + "#]], + ); + check( + r"fn func() $0", + expect![[r#" + kw where + "#]], + ); + check( + r"enum Enum $0", + expect![[r#" + kw where + "#]], + ); + check( + r"enum Enum $0 {}", + expect![[r#" + kw where + "#]], + ); + check( + r"trait Trait $0 {}", + expect![[r#" + kw where + "#]], + ); } #[test] diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs index edc896636f4..09ea78a3d50 100644 --- a/crates/ide-completion/src/tests/item_list.rs +++ b/crates/ide-completion/src/tests/item_list.rs @@ -108,7 +108,6 @@ fn in_item_list_after_attr() { #[test] fn in_qualified_path() { - cov_mark::check!(no_keyword_completion_in_non_trivial_path); check( r#"crate::$0"#, expect![[r#" diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index 9e442dbbc56..9369034cc62 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -9,7 +9,6 @@ fn check(ra_fixture: &str, expect: Expect) { #[test] fn without_default_impl() { - cov_mark::check!(no_keyword_completion_in_record_lit); check( r#" struct Struct { foo: u32, bar: usize }