Unnamed repository; edit this file 'description' to name the repository.
More precise where keyword completions
Lukas Wirth 2022-06-03
parent c522669 · commit 6550a24
-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 b78ed26ec3..aa0d04cf6c 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 d55046e710..d6df5002f5 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 f8073f5431..ccc7c10746 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 27b271dde4..34bfa4517c 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 9e50e00ab7..81303eb38f 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 edc896636f..09ea78a3d5 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 9e442dbbc5..9369034cc6 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 }