This commit is contained in:
Lukas Wirth 2022-06-17 16:22:51 +02:00
parent f35a9a1dcc
commit 85363d18e8
4 changed files with 94 additions and 64 deletions

View file

@ -2,7 +2,6 @@
use hir::ScopeDef;
use ide_db::FxHashSet;
use syntax::T;
use crate::{
context::{NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PathQualifierCtx},
@ -20,6 +19,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
is_func_update,
after_if_expr,
wants_mut_token,
in_condition,
) = match ctx.nameref_ctx() {
Some(&NameRefContext {
kind:
@ -29,6 +29,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
in_block_expr,
in_loop_body,
after_if_expr,
in_condition,
ref ref_expr_parent,
ref is_func_update,
},
@ -45,6 +46,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
is_func_update.is_some(),
after_if_expr,
ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false),
in_condition,
),
_ => return,
};
@ -235,10 +237,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
add_keyword("true", "true");
add_keyword("false", "false");
if ctx.previous_token_is(T![if])
|| ctx.previous_token_is(T![while])
|| in_block_expr
{
if (in_condition && !is_absolute_path) || in_block_expr {
add_keyword("let", "let");
}

View file

@ -34,7 +34,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
matches!(location, TypeLocation::GenericArgList(_))
}
ScopeDef::ImplSelfType(_) => {
!ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
!matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
}
// Don't suggest attribute macros and derives.
ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),

View file

@ -92,6 +92,8 @@ pub(super) enum PathKind {
in_block_expr: bool,
in_loop_body: bool,
after_if_expr: bool,
/// Whether this expression is the direct condition of an if or while expression
in_condition: bool,
ref_expr_parent: Option<ast::RefExpr>,
is_func_update: Option<ast::RecordExpr>,
},
@ -121,6 +123,8 @@ pub(crate) enum TypeLocation {
TypeAscription(TypeAscriptionTarget),
GenericArgList(Option<ast::GenericArgList>),
TypeBound,
ImplTarget,
ImplTrait,
Other,
}

View file

@ -327,7 +327,8 @@ impl<'a> CompletionContext<'a> {
return None;
}
self.previous_token = previous_token(syntax_element.clone());
self.previous_token =
syntax_element.clone().into_token().and_then(previous_non_trivia_token);
self.incomplete_let =
syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
@ -647,8 +648,8 @@ impl<'a> CompletionContext<'a> {
None
};
let type_location = |it: Option<SyntaxNode>| {
let parent = it?;
let type_location = |node: &SyntaxNode| {
let parent = node.parent()?;
let res = match_ast! {
match parent {
ast::Const(it) => {
@ -690,6 +691,15 @@ impl<'a> CompletionContext<'a> {
}
TypeLocation::TypeAscription(TypeAscriptionTarget::Let(find_opt_node_in_file(original_file, it.pat())))
},
ast::Impl(it) => {
match it.trait_() {
Some(t) if t.syntax() == node => TypeLocation::ImplTrait,
_ => match it.self_ty() {
Some(t) if t.syntax() == node => TypeLocation::ImplTarget,
_ => return None,
},
}
},
ast::TypeBound(_) => TypeLocation::TypeBound,
// is this case needed?
ast::TypeBoundList(_) => TypeLocation::TypeBound,
@ -703,16 +713,49 @@ impl<'a> CompletionContext<'a> {
Some(res)
};
let is_in_condition = |it: &ast::Expr| {
(|| {
let parent = it.syntax().parent()?;
if let Some(expr) = ast::WhileExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
} else if let Some(expr) = ast::IfExpr::cast(parent) {
Some(expr.condition()? == *it)
} else {
None
}
})()
.unwrap_or(false)
};
let make_path_kind_expr = |expr: ast::Expr| {
let it = expr.syntax();
let in_block_expr = is_in_block(it);
let in_loop_body = is_in_loop_body(it);
let after_if_expr = after_if_expr(it.clone());
let ref_expr_parent =
path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
let is_func_update = func_update_record(it);
let in_condition = is_in_condition(&expr);
PathKind::Expr {
in_block_expr,
in_loop_body,
after_if_expr,
in_condition,
ref_expr_parent,
is_func_update,
}
};
let make_path_kind_type = |ty: ast::Type| {
let location = type_location(ty.syntax());
PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
};
// Infer the path kind
let kind = path.syntax().parent().and_then(|it| {
match_ast! {
match it {
ast::PathType(it) => {
let location = type_location(it.syntax().parent());
Some(PathKind::Type {
location: location.unwrap_or(TypeLocation::Other),
})
},
ast::PathType(it) => Some(make_path_kind_type(it.into())),
ast::PathExpr(it) => {
if let Some(p) = it.syntax().parent() {
if ast::ExprStmt::can_cast(p.kind()) {
@ -724,14 +767,8 @@ impl<'a> CompletionContext<'a> {
}
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
let in_block_expr = is_in_block(it.syntax());
let in_loop_body = is_in_loop_body(it.syntax());
let after_if_expr = after_if_expr(it.syntax().clone());
let ref_expr_parent = path.as_single_name_ref()
.and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
let is_func_update = func_update_record(it.syntax());
Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update })
Some(make_path_kind_expr(it.into()))
},
ast::TupleStructPat(it) => {
path_ctx.has_call_parens = true;
@ -748,50 +785,41 @@ impl<'a> CompletionContext<'a> {
Some(PathKind::Pat)
},
ast::MacroCall(it) => {
// A macro call in this position is usually a result of parsing recovery, so check that
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
nameref_ctx.kind = Some(NameRefKind::Keyword(kind));
return None;
}
path_ctx.has_macro_bang = it.excl_token().is_some();
let parent = it.syntax().parent();
match parent.as_ref().map(|it| it.kind()) {
Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
Some(SyntaxKind::MACRO_TYPE) => {
let location = type_location(parent.unwrap().parent());
Some(PathKind::Type {
location: location.unwrap_or(TypeLocation::Other),
})
},
Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
Some(it) => match_ast! {
match it {
ast::Trait(_) => ItemListKind::Trait,
ast::Impl(it) => if it.trait_().is_some() {
ItemListKind::TraitImpl
} else {
ItemListKind::Impl
},
_ => return None
}
},
None => return None,
} }),
Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
_ => {
return parent.and_then(ast::MacroExpr::cast).map(|it| {
let in_loop_body = is_in_loop_body(it.syntax());
let in_block_expr = is_in_block(it.syntax());
let after_if_expr = after_if_expr(it.syntax().clone());
let ref_expr_parent = path.as_single_name_ref()
.and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
let is_func_update = func_update_record(it.syntax());
PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update }
});
},
}
let parent = it.syntax().parent()?;
// Any path in an item list will be treated as a macro call by the parser
let res = match_ast! {
match parent {
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
ast::MacroPat(_) => PathKind::Pat,
ast::MacroType(ty) => make_path_kind_type(ty.into()),
ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
Some(it) => match_ast! {
match it {
ast::Trait(_) => ItemListKind::Trait,
ast::Impl(it) => if it.trait_().is_some() {
ItemListKind::TraitImpl
} else {
ItemListKind::Impl
},
_ => return None
}
},
None => return None,
} },
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
_ => return None,
}
};
Some(res)
},
ast::Meta(meta) => (|| {
let attr = meta.parent_attr()?;
@ -818,10 +846,13 @@ impl<'a> CompletionContext<'a> {
match kind {
Some(kind) => path_ctx.kind = kind,
// unresolved path kind, so this isn't really a path we should be completing,
// just some random identifier which might be in keyword position
None => return res,
}
path_ctx.has_type_args = segment.generic_arg_list().is_some();
// calculate the qualifier context
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
if !use_tree_parent {
path_ctx.is_absolute_path =
@ -1034,10 +1065,6 @@ fn has_ref(token: &SyntaxToken) -> bool {
token.kind() == T![&]
}
pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
element.into_token().and_then(previous_non_trivia_token)
}
pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool {
// oh my ...
(|| {