about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/ide-completion/src/completions/item_list.rs2
-rw-r--r--crates/ide-completion/src/completions/keyword.rs47
-rw-r--r--crates/ide-completion/src/context.rs42
-rw-r--r--crates/ide-completion/src/patterns.rs29
-rw-r--r--crates/ide-completion/src/tests/item.rs62
-rw-r--r--crates/ide-completion/src/tests/item_list.rs1
-rw-r--r--crates/ide-completion/src/tests/record.rs1
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");
+        }
+        Item::Enum(_)
+        | Item::Fn(_)
+        | Item::Struct(_)
+        | Item::Trait(_)
+        | Item::TypeAlias(_)
+        | Item::Union(_) => {
+            add_keyword("where", "where");
         }
-        return;
+        _ => (),
     }
 }
 
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<DotAccess>,
     pub(super) path_ctx: Option<PathCompletionCtx>,
+    /// Position where we are only interested in keyword completions
+    pub(super) keyword: Option<ast::Item>,
     /// 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<PatternContext>) {
         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<Immedi
                     }
                 }
             },
-            ast::Trait(it) => 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,
         }
     };
@@ -343,22 +330,6 @@ mod tests {
     }
 
     #[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);
         check_prev_sibling(r"fn foo() { if true {}; w$0", None);
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 for Type $0",
+        r"impl Trait f$0",
         expect![[r#"
             kw for
             kw where
         "#]],
     );
+    check(
+        r"impl Trait for Type $0",
+        expect![[r#"
+            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 }