More precise where keyword completions
This commit is contained in:
parent
c522669f00
commit
6550a241fb
@ -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 {
|
||||
|
@ -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");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
};
|
||||
@ -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);
|
||||
|
@ -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]
|
||||
|
@ -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#"
|
||||
|
@ -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 }
|
||||
|
Loading…
x
Reference in New Issue
Block a user