mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +00:00
Auto merge of #12459 - Veykril:completions, r=Veykril
internal: Clean up keyword completion handling https://github.com/rust-lang/rust-analyzer/issues/12144
This commit is contained in:
commit
d06d0f8774
15 changed files with 295 additions and 400 deletions
|
@ -4,6 +4,7 @@ pub(crate) mod attribute;
|
|||
pub(crate) mod dot;
|
||||
pub(crate) mod expr;
|
||||
pub(crate) mod extern_abi;
|
||||
pub(crate) mod field;
|
||||
pub(crate) mod flyimport;
|
||||
pub(crate) mod fn_param;
|
||||
pub(crate) mod format_string;
|
||||
|
@ -110,6 +111,26 @@ impl Completions {
|
|||
["self", "super", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
|
||||
}
|
||||
|
||||
pub(crate) fn add_keyword_snippet(&mut self, ctx: &CompletionContext, kw: &str, snippet: &str) {
|
||||
let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
|
||||
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
if snippet.ends_with('}') && ctx.incomplete_let {
|
||||
// complete block expression snippets with a trailing semicolon, if inside an incomplete let
|
||||
cov_mark::hit!(let_semi);
|
||||
item.insert_snippet(cap, format!("{};", snippet));
|
||||
} else {
|
||||
item.insert_snippet(cap, snippet);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
item.insert_text(if snippet.contains('$') { kw } else { snippet });
|
||||
}
|
||||
};
|
||||
item.add_to(self);
|
||||
}
|
||||
|
||||
pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
|
||||
ctx.process_all_names(&mut |name, res| match res {
|
||||
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
|
||||
|
|
|
@ -15,12 +15,12 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||
return;
|
||||
}
|
||||
|
||||
let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update) =
|
||||
let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update, after_if_expr) =
|
||||
match ctx.nameref_ctx() {
|
||||
Some(NameRefContext {
|
||||
path_ctx:
|
||||
Some(PathCompletionCtx {
|
||||
kind: PathKind::Expr { in_block_expr, in_loop_body },
|
||||
kind: PathKind::Expr { in_block_expr, in_loop_body, after_if_expr },
|
||||
is_absolute_path,
|
||||
qualifier,
|
||||
..
|
||||
|
@ -33,6 +33,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||
*in_block_expr,
|
||||
*in_loop_body,
|
||||
record_expr.as_ref().map_or(false, |&(_, it)| it),
|
||||
*after_if_expr,
|
||||
),
|
||||
_ => return,
|
||||
};
|
||||
|
@ -177,8 +178,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||
});
|
||||
|
||||
if !is_func_update {
|
||||
let mut add_keyword =
|
||||
|kw, snippet| super::keyword::add_keyword(acc, ctx, kw, snippet);
|
||||
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
||||
|
||||
if ctx.expects_expression() {
|
||||
if !in_block_expr {
|
||||
|
@ -202,7 +202,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||
add_keyword("let", "let");
|
||||
}
|
||||
|
||||
if ctx.after_if() {
|
||||
if after_if_expr {
|
||||
add_keyword("else", "else {\n $0\n}");
|
||||
add_keyword("else if", "else if $1 {\n $0\n}");
|
||||
}
|
||||
|
|
33
crates/ide-completion/src/completions/field.rs
Normal file
33
crates/ide-completion/src/completions/field.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
//! Completion of field list position.
|
||||
|
||||
use crate::{
|
||||
context::{IdentContext, NameContext, NameKind, NameRefContext, PathCompletionCtx, PathKind},
|
||||
CompletionContext, Completions,
|
||||
};
|
||||
|
||||
pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
match &ctx.ident_ctx {
|
||||
IdentContext::Name(NameContext { kind: NameKind::RecordField, .. })
|
||||
| IdentContext::NameRef(NameRefContext {
|
||||
path_ctx:
|
||||
Some(PathCompletionCtx {
|
||||
has_macro_bang: false,
|
||||
is_absolute_path: false,
|
||||
qualifier: None,
|
||||
parent: None,
|
||||
kind: PathKind::Type { in_tuple_struct: true },
|
||||
has_type_args: false,
|
||||
..
|
||||
}),
|
||||
..
|
||||
}) => {
|
||||
if ctx.qualifier_ctx.vis_node.is_none() {
|
||||
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
||||
add_keyword("pub(crate)", "pub(crate)");
|
||||
add_keyword("pub(super)", "pub(super)");
|
||||
add_keyword("pub", "pub");
|
||||
}
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
}
|
|
@ -110,10 +110,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
|||
if !ctx.config.enable_imports_on_the_fly {
|
||||
return None;
|
||||
}
|
||||
if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use))
|
||||
if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use | PathKind::Item { .. }))
|
||||
|| ctx.is_path_disallowed()
|
||||
|| ctx.expects_item()
|
||||
|| ctx.expects_assoc_item()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
@ -160,7 +158,10 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
|||
(_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
|
||||
// and so are macros(except for attributes)
|
||||
(
|
||||
PathKind::Expr { .. } | PathKind::Type | PathKind::Item { .. } | PathKind::Pat,
|
||||
PathKind::Expr { .. }
|
||||
| PathKind::Type { .. }
|
||||
| PathKind::Item { .. }
|
||||
| PathKind::Pat,
|
||||
ItemInNs::Macros(mac),
|
||||
) => mac.is_fn_like(ctx.db),
|
||||
(PathKind::Item { .. }, _) => true,
|
||||
|
@ -170,14 +171,14 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
|||
(PathKind::Pat, ItemInNs::Types(_)) => true,
|
||||
(PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
|
||||
|
||||
(PathKind::Type, ItemInNs::Types(ty)) => {
|
||||
(PathKind::Type { .. }, ItemInNs::Types(ty)) => {
|
||||
if matches!(ctx.completion_location, Some(ImmediateLocation::TypeBound)) {
|
||||
matches!(ty, ModuleDef::Trait(_))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
(PathKind::Type, ItemInNs::Values(_)) => false,
|
||||
(PathKind::Type { .. }, ItemInNs::Values(_)) => false,
|
||||
|
||||
(PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(ctx.db),
|
||||
(PathKind::Attr { .. }, _) => false,
|
||||
|
|
|
@ -2,22 +2,98 @@
|
|||
|
||||
use crate::{
|
||||
completions::module_or_fn_macro,
|
||||
context::{PathCompletionCtx, PathKind, PathQualifierCtx},
|
||||
context::{ItemListKind, PathCompletionCtx, PathKind, PathQualifierCtx},
|
||||
CompletionContext, Completions,
|
||||
};
|
||||
|
||||
pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
let _p = profile::span("complete_item_list");
|
||||
|
||||
let (&is_absolute_path, path_qualifier, _kind) = match ctx.path_context() {
|
||||
let (&is_absolute_path, path_qualifier, kind) = match ctx.path_context() {
|
||||
Some(PathCompletionCtx {
|
||||
kind: PathKind::Item { kind },
|
||||
is_absolute_path,
|
||||
qualifier,
|
||||
..
|
||||
}) => (is_absolute_path, qualifier, kind),
|
||||
}) => (is_absolute_path, qualifier, Some(kind)),
|
||||
Some(PathCompletionCtx {
|
||||
kind: PathKind::Expr { in_block_expr: true, .. },
|
||||
is_absolute_path,
|
||||
qualifier,
|
||||
..
|
||||
}) => (is_absolute_path, qualifier, None),
|
||||
_ => return,
|
||||
};
|
||||
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
||||
|
||||
let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
|
||||
let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait));
|
||||
let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock));
|
||||
let in_trait = matches!(kind, Some(ItemListKind::Trait));
|
||||
let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl));
|
||||
let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl));
|
||||
let no_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
|
||||
let in_block = matches!(kind, None);
|
||||
|
||||
'block: loop {
|
||||
if ctx.is_non_trivial_path() {
|
||||
break 'block;
|
||||
}
|
||||
if !in_trait_impl {
|
||||
if ctx.qualifier_ctx.unsafe_tok.is_some() {
|
||||
if in_item_list || in_assoc_non_trait_impl {
|
||||
add_keyword("fn", "fn $1($2) {\n $0\n}");
|
||||
}
|
||||
if in_item_list {
|
||||
add_keyword("trait", "trait $1 {\n $0\n}");
|
||||
if no_qualifiers {
|
||||
add_keyword("impl", "impl $1 {\n $0\n}");
|
||||
}
|
||||
}
|
||||
break 'block;
|
||||
}
|
||||
|
||||
if in_item_list {
|
||||
add_keyword("enum", "enum $1 {\n $0\n}");
|
||||
add_keyword("mod", "mod $0");
|
||||
add_keyword("static", "static $0");
|
||||
add_keyword("struct", "struct $0");
|
||||
add_keyword("trait", "trait $1 {\n $0\n}");
|
||||
add_keyword("union", "union $1 {\n $0\n}");
|
||||
add_keyword("use", "use $0");
|
||||
if no_qualifiers {
|
||||
add_keyword("impl", "impl $1 {\n $0\n}");
|
||||
}
|
||||
}
|
||||
|
||||
if !in_trait && !in_block && no_qualifiers {
|
||||
add_keyword("pub(crate)", "pub(crate)");
|
||||
add_keyword("pub(super)", "pub(super)");
|
||||
add_keyword("pub", "pub");
|
||||
}
|
||||
|
||||
if in_extern_block {
|
||||
add_keyword("fn", "fn $1($2);");
|
||||
} else {
|
||||
if !in_inherent_impl {
|
||||
if !in_trait {
|
||||
add_keyword("extern", "extern $0");
|
||||
}
|
||||
add_keyword("type", "type $0");
|
||||
}
|
||||
|
||||
add_keyword("fn", "fn $1($2) {\n $0\n}");
|
||||
add_keyword("unsafe", "unsafe");
|
||||
add_keyword("const", "const $0");
|
||||
}
|
||||
}
|
||||
break 'block;
|
||||
}
|
||||
|
||||
if kind.is_none() {
|
||||
// this is already handled by expression
|
||||
return;
|
||||
}
|
||||
|
||||
match path_qualifier {
|
||||
Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
|
||||
|
@ -33,9 +109,7 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
|
|||
acc.add_keyword(ctx, "super::");
|
||||
}
|
||||
}
|
||||
None if is_absolute_path => {
|
||||
acc.add_crate_roots(ctx);
|
||||
}
|
||||
None if is_absolute_path => acc.add_crate_roots(ctx),
|
||||
None if ctx.qualifier_ctx.none() => {
|
||||
ctx.process_all_names(&mut |name, def| {
|
||||
if let Some(def) = module_or_fn_macro(ctx.db, def) {
|
||||
|
|
|
@ -2,106 +2,39 @@
|
|||
//! - `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::T;
|
||||
use syntax::ast::Item;
|
||||
|
||||
use crate::{
|
||||
context::{NameRefContext, PathKind},
|
||||
CompletionContext, CompletionItem, CompletionItemKind, Completions,
|
||||
};
|
||||
use crate::{context::NameRefContext, CompletionContext, 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 mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
|
||||
|
||||
let expects_assoc_item = ctx.expects_assoc_item();
|
||||
let has_block_expr_parent = ctx.has_block_expr_parent();
|
||||
let expects_item = ctx.expects_item();
|
||||
|
||||
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");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ctx.previous_token_is(T![unsafe]) {
|
||||
if expects_item || expects_assoc_item || has_block_expr_parent {
|
||||
add_keyword("fn", "fn $1($2) {\n $0\n}")
|
||||
}
|
||||
|
||||
if expects_item || has_block_expr_parent {
|
||||
add_keyword("trait", "trait $1 {\n $0\n}");
|
||||
add_keyword("impl", "impl $1 {\n $0\n}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ctx.qualifier_ctx.vis_node.is_none()
|
||||
&& (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_field())
|
||||
{
|
||||
add_keyword("pub(crate)", "pub(crate)");
|
||||
add_keyword("pub(super)", "pub(super)");
|
||||
add_keyword("pub", "pub");
|
||||
}
|
||||
|
||||
if expects_item || expects_assoc_item || has_block_expr_parent {
|
||||
add_keyword("unsafe", "unsafe");
|
||||
add_keyword("fn", "fn $1($2) {\n $0\n}");
|
||||
add_keyword("const", "const $0");
|
||||
add_keyword("type", "type $0");
|
||||
}
|
||||
|
||||
if expects_item || has_block_expr_parent {
|
||||
if ctx.qualifier_ctx.vis_node.is_none() {
|
||||
add_keyword("impl", "impl $1 {\n $0\n}");
|
||||
add_keyword("extern", "extern $0");
|
||||
}
|
||||
add_keyword("use", "use $0");
|
||||
add_keyword("trait", "trait $1 {\n $0\n}");
|
||||
add_keyword("static", "static $0");
|
||||
add_keyword("mod", "mod $0");
|
||||
}
|
||||
|
||||
if expects_item || has_block_expr_parent {
|
||||
add_keyword("enum", "enum $1 {\n $0\n}");
|
||||
add_keyword("struct", "struct $0");
|
||||
add_keyword("union", "union $1 {\n $0\n}");
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) {
|
||||
let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
|
||||
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
if snippet.ends_with('}') && ctx.incomplete_let {
|
||||
// complete block expression snippets with a trailing semicolon, if inside an incomplete let
|
||||
cov_mark::hit!(let_semi);
|
||||
item.insert_snippet(cap, format!("{};", snippet));
|
||||
} else {
|
||||
item.insert_snippet(cap, snippet);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
item.insert_text(if snippet.contains('$') { kw } else { snippet });
|
||||
let item = match ctx.nameref_ctx() {
|
||||
Some(NameRefContext { keyword: Some(item), record_expr: None, .. })
|
||||
if !ctx.is_non_trivial_path() =>
|
||||
{
|
||||
item
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
item.add_to(acc);
|
||||
|
||||
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
||||
|
||||
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");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -18,9 +18,12 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||
}
|
||||
|
||||
let (&is_absolute_path, qualifier) = match ctx.path_context() {
|
||||
Some(PathCompletionCtx { kind: PathKind::Type, is_absolute_path, qualifier, .. }) => {
|
||||
(is_absolute_path, qualifier)
|
||||
}
|
||||
Some(PathCompletionCtx {
|
||||
kind: PathKind::Type { .. },
|
||||
is_absolute_path,
|
||||
qualifier,
|
||||
..
|
||||
}) => (is_absolute_path, qualifier),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use ide_db::{
|
|||
use syntax::{
|
||||
algo::{find_node_at_offset, non_trivia_sibling},
|
||||
ast::{self, AttrKind, HasArgList, HasName, NameOrNameRef},
|
||||
match_ast, AstNode, AstToken, NodeOrToken,
|
||||
match_ast, AstNode, AstToken, Direction, NodeOrToken,
|
||||
SyntaxKind::{self, *},
|
||||
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
|
||||
};
|
||||
|
@ -23,8 +23,8 @@ use text_edit::Indel;
|
|||
|
||||
use crate::{
|
||||
patterns::{
|
||||
determine_location, determine_prev_sibling, is_in_loop_body, is_in_token_of_for_loop,
|
||||
previous_token, ImmediateLocation, ImmediatePrevSibling,
|
||||
determine_location, is_in_loop_body, is_in_token_of_for_loop, previous_token,
|
||||
ImmediateLocation,
|
||||
},
|
||||
CompletionConfig,
|
||||
};
|
||||
|
@ -48,8 +48,11 @@ pub(super) enum PathKind {
|
|||
Expr {
|
||||
in_block_expr: bool,
|
||||
in_loop_body: bool,
|
||||
after_if_expr: bool,
|
||||
},
|
||||
Type {
|
||||
in_tuple_struct: bool,
|
||||
},
|
||||
Type,
|
||||
Attr {
|
||||
kind: AttrKind,
|
||||
annotated_item_kind: Option<SyntaxKind>,
|
||||
|
@ -71,6 +74,7 @@ pub(super) enum ItemListKind {
|
|||
SourceFile,
|
||||
Module,
|
||||
Impl,
|
||||
TraitImpl,
|
||||
Trait,
|
||||
ExternBlock,
|
||||
}
|
||||
|
@ -182,6 +186,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)>,
|
||||
}
|
||||
|
@ -259,7 +265,6 @@ pub(crate) struct CompletionContext<'a> {
|
|||
pub(super) incomplete_let: bool,
|
||||
|
||||
pub(super) completion_location: Option<ImmediateLocation>,
|
||||
pub(super) prev_sibling: Option<ImmediatePrevSibling>,
|
||||
pub(super) previous_token: Option<SyntaxToken>,
|
||||
|
||||
pub(super) ident_ctx: IdentContext,
|
||||
|
@ -331,55 +336,15 @@ impl<'a> CompletionContext<'a> {
|
|||
self.dot_receiver().is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn expects_assoc_item(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::Trait | ImmediateLocation::Impl))
|
||||
}
|
||||
|
||||
pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::Impl))
|
||||
}
|
||||
|
||||
pub(crate) fn expects_item(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::ItemList))
|
||||
}
|
||||
|
||||
// FIXME: This shouldn't exist
|
||||
pub(crate) fn expects_generic_arg(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
|
||||
}
|
||||
|
||||
pub(crate) fn has_block_expr_parent(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::StmtList))
|
||||
}
|
||||
|
||||
pub(crate) fn expects_ident_ref_expr(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
|
||||
}
|
||||
|
||||
pub(crate) fn expect_field(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::TupleField))
|
||||
|| matches!(self.name_ctx(), Some(NameContext { kind: NameKind::RecordField, .. }))
|
||||
}
|
||||
|
||||
/// 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))
|
||||
}
|
||||
|
||||
// FIXME: This shouldn't exist
|
||||
pub(crate) fn is_path_disallowed(&self) -> bool {
|
||||
!self.qualifier_ctx.none()
|
||||
|
@ -558,7 +523,6 @@ impl<'a> CompletionContext<'a> {
|
|||
impl_def: None,
|
||||
incomplete_let: false,
|
||||
completion_location: None,
|
||||
prev_sibling: None,
|
||||
previous_token: None,
|
||||
// dummy value, will be overwritten
|
||||
ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
|
||||
|
@ -953,7 +917,6 @@ impl<'a> CompletionContext<'a> {
|
|||
};
|
||||
self.completion_location =
|
||||
determine_location(&self.sema, original_file, offset, &name_like);
|
||||
self.prev_sibling = determine_prev_sibling(&name_like);
|
||||
self.impl_def = self
|
||||
.sema
|
||||
.token_ancestors_with_macros(self.token.clone())
|
||||
|
@ -1110,8 +1073,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 =
|
||||
|
@ -1195,6 +1163,13 @@ impl<'a> CompletionContext<'a> {
|
|||
find_node_in_file_compensated(original_file, &record_expr).zip(Some(true));
|
||||
}
|
||||
};
|
||||
let after_if_expr = |node: SyntaxNode| {
|
||||
let prev_expr = (|| {
|
||||
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
|
||||
ast::ExprStmt::cast(prev_sibling)?.expr()
|
||||
})();
|
||||
matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
|
||||
};
|
||||
|
||||
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
|
||||
// ex. trait Foo $0 {}
|
||||
|
@ -1208,7 +1183,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(),
|
||||
|
@ -1221,24 +1196,27 @@ 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| {
|
||||
// using Option<Option<PathKind>> as extra controlflow
|
||||
let kind = match_ast! {
|
||||
match it {
|
||||
ast::PathType(_) => Some(PathKind::Type),
|
||||
ast::PathType(it) => Some(PathKind::Type {
|
||||
in_tuple_struct: it.syntax().parent().map_or(false, |it| ast::TupleField::can_cast(it.kind()))
|
||||
}),
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1249,7 +1227,9 @@ 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());
|
||||
Some(PathKind::Expr { in_block_expr, in_loop_body })
|
||||
let after_if_expr = after_if_expr(it.syntax().clone());
|
||||
|
||||
Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr })
|
||||
},
|
||||
ast::TupleStructPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
|
@ -1266,7 +1246,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);
|
||||
}
|
||||
|
||||
|
@ -1274,12 +1255,21 @@ impl<'a> CompletionContext<'a> {
|
|||
let parent = it.syntax().parent();
|
||||
match parent.as_ref().map(|it| it.kind()) {
|
||||
Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
|
||||
Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type),
|
||||
Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type { in_tuple_struct: false }),
|
||||
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()).map(|it| it.kind()) {
|
||||
Some(SyntaxKind::TRAIT) => ItemListKind::Trait,
|
||||
Some(SyntaxKind::IMPL) => ItemListKind::Impl,
|
||||
_ => return Some(None),
|
||||
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 Some(None)
|
||||
}
|
||||
},
|
||||
None => return Some(None),
|
||||
} }),
|
||||
Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
|
||||
Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
|
||||
|
@ -1287,8 +1277,9 @@ impl<'a> CompletionContext<'a> {
|
|||
return Some(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());
|
||||
fill_record_expr(it.syntax());
|
||||
PathKind::Expr { in_block_expr, in_loop_body }
|
||||
PathKind::Expr { in_block_expr, in_loop_body, after_if_expr }
|
||||
}));
|
||||
},
|
||||
}
|
||||
|
@ -1313,12 +1304,18 @@ impl<'a> CompletionContext<'a> {
|
|||
ast::UseTree(_) => Some(PathKind::Use),
|
||||
ast::ItemList(_) => Some(PathKind::Item { kind: ItemListKind::Module }),
|
||||
ast::AssocItemList(it) => Some(PathKind::Item { kind: {
|
||||
match it.syntax().parent()?.kind() {
|
||||
SyntaxKind::TRAIT => ItemListKind::Trait,
|
||||
SyntaxKind::IMPL => ItemListKind::Impl,
|
||||
_ => return None,
|
||||
match_ast! {
|
||||
match (it.syntax().parent()?) {
|
||||
ast::Trait(_) => ItemListKind::Trait,
|
||||
ast::Impl(it) => if it.trait_().is_some() {
|
||||
ItemListKind::TraitImpl
|
||||
} else {
|
||||
ItemListKind::Impl
|
||||
},
|
||||
_ => return None
|
||||
}
|
||||
}}),
|
||||
}
|
||||
}}),
|
||||
ast::ExternItemList(_) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
|
||||
ast::SourceFile(_) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
|
||||
_ => return None,
|
||||
|
|
|
@ -158,6 +158,7 @@ pub fn completions(
|
|||
completions::dot::complete_dot(acc, ctx);
|
||||
completions::expr::complete_expr_path(acc, ctx);
|
||||
completions::extern_abi::complete_extern_abi(acc, ctx);
|
||||
completions::field::complete_field_list(acc, ctx);
|
||||
completions::flyimport::import_on_the_fly(acc, ctx);
|
||||
completions::fn_param::complete_fn_param(acc, ctx);
|
||||
completions::format_string::format_string(acc, ctx);
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
use hir::Semantics;
|
||||
use ide_db::RootDatabase;
|
||||
use syntax::{
|
||||
algo::non_trivia_sibling,
|
||||
ast::{self, HasLoopBody, HasName},
|
||||
match_ast, AstNode, Direction, SyntaxElement,
|
||||
match_ast, AstNode, SyntaxElement,
|
||||
SyntaxKind::*,
|
||||
SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||
};
|
||||
|
@ -17,14 +16,6 @@ use syntax::{
|
|||
#[cfg(test)]
|
||||
use crate::tests::check_pattern_is_applicable;
|
||||
|
||||
/// Immediate previous node to what we are completing.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ImmediatePrevSibling {
|
||||
IfExpr,
|
||||
TraitDefName,
|
||||
ImplDefType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum TypeAnnotation {
|
||||
Let(Option<ast::Pat>),
|
||||
|
@ -39,13 +30,7 @@ pub(crate) enum TypeAnnotation {
|
|||
/// from which file the nodes are.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ImmediateLocation {
|
||||
Impl,
|
||||
Trait,
|
||||
TupleField,
|
||||
RefExpr,
|
||||
IdentPat,
|
||||
StmtList,
|
||||
ItemList,
|
||||
TypeBound,
|
||||
/// Original file ast node
|
||||
TypeAnnotation(TypeAnnotation),
|
||||
|
@ -54,56 +39,6 @@ pub(crate) enum ImmediateLocation {
|
|||
GenericArgList(ast::GenericArgList),
|
||||
}
|
||||
|
||||
pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<ImmediatePrevSibling> {
|
||||
let node = match name_like {
|
||||
ast::NameLike::NameRef(name_ref) => maximize_name_ref(name_ref),
|
||||
ast::NameLike::Name(n) => n.syntax().clone(),
|
||||
ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
|
||||
};
|
||||
let node = match node.parent().and_then(ast::MacroCall::cast) {
|
||||
// When a path is being typed after the name of a trait/type of an impl it is being
|
||||
// parsed as a macro, so when the trait/impl has a block following it an we are between the
|
||||
// name and block the macro will attach the block to itself so maximizing fails to take
|
||||
// that into account
|
||||
// FIXME path expr and statement have a similar problem with attrs
|
||||
Some(call)
|
||||
if call.excl_token().is_none()
|
||||
&& call.token_tree().map_or(false, |t| t.l_curly_token().is_some())
|
||||
&& call.semicolon_token().is_none() =>
|
||||
{
|
||||
call.syntax().clone()
|
||||
}
|
||||
_ => node,
|
||||
};
|
||||
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
|
||||
let res = match_ast! {
|
||||
match prev_sibling {
|
||||
ast::ExprStmt(it) => {
|
||||
let node = it.expr().filter(|_| it.semicolon_token().is_none())?.syntax().clone();
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::IfExpr(_) => ImmediatePrevSibling::IfExpr,
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
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,
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub(crate) fn determine_location(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
original_file: &SyntaxNode,
|
||||
|
@ -140,30 +75,14 @@ pub(crate) fn determine_location(
|
|||
_ => parent,
|
||||
},
|
||||
// SourceFile
|
||||
None => {
|
||||
return match node.kind() {
|
||||
MACRO_ITEMS | SOURCE_FILE => Some(ImmediateLocation::ItemList),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let res = match_ast! {
|
||||
match parent {
|
||||
ast::IdentPat(_) => ImmediateLocation::IdentPat,
|
||||
ast::StmtList(_) => ImmediateLocation::StmtList,
|
||||
ast::SourceFile(_) => ImmediateLocation::ItemList,
|
||||
ast::ItemList(_) => ImmediateLocation::ItemList,
|
||||
ast::RefExpr(_) => ImmediateLocation::RefExpr,
|
||||
ast::TupleField(_) => ImmediateLocation::TupleField,
|
||||
ast::TupleFieldList(_) => ImmediateLocation::TupleField,
|
||||
ast::TypeBound(_) => ImmediateLocation::TypeBound,
|
||||
ast::TypeBoundList(_) => ImmediateLocation::TypeBound,
|
||||
ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) {
|
||||
Some(IMPL) => ImmediateLocation::Impl,
|
||||
Some(TRAIT) => ImmediateLocation::Trait,
|
||||
_ => return None,
|
||||
},
|
||||
ast::GenericArgList(_) => sema
|
||||
.find_node_at_offset_with_macros(original_file, offset)
|
||||
.map(ImmediateLocation::GenericArgList)?,
|
||||
|
@ -351,83 +270,8 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
fn check_prev_sibling(code: &str, sibling: impl Into<Option<ImmediatePrevSibling>>) {
|
||||
check_pattern_is_applicable(code, |e| {
|
||||
let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
|
||||
assert_eq!(determine_prev_sibling(name), sibling.into());
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trait_loc() {
|
||||
check_location(r"trait A { f$0 }", ImmediateLocation::Trait);
|
||||
check_location(r"trait A { #[attr] f$0 }", ImmediateLocation::Trait);
|
||||
check_location(r"trait A { f$0 fn f() {} }", ImmediateLocation::Trait);
|
||||
check_location(r"trait A { fn f() {} f$0 }", ImmediateLocation::Trait);
|
||||
check_location(r"trait A$0 {}", None);
|
||||
check_location(r"trait A { fn f$0 }", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_impl_loc() {
|
||||
check_location(r"impl A { f$0 }", ImmediateLocation::Impl);
|
||||
check_location(r"impl A { #[attr] f$0 }", ImmediateLocation::Impl);
|
||||
check_location(r"impl A { f$0 fn f() {} }", ImmediateLocation::Impl);
|
||||
check_location(r"impl A { fn f() {} f$0 }", ImmediateLocation::Impl);
|
||||
check_location(r"impl A$0 {}", None);
|
||||
check_location(r"impl A { fn f$0 }", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_expr_loc() {
|
||||
check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::StmtList);
|
||||
check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::StmtList);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ident_pat_loc() {
|
||||
check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat);
|
||||
check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat);
|
||||
check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat);
|
||||
check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ref_expr_loc() {
|
||||
check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_list_loc() {
|
||||
check_location(r"i$0", ImmediateLocation::ItemList);
|
||||
check_location(r"#[attr] i$0", ImmediateLocation::ItemList);
|
||||
check_location(r"fn f() {} i$0", ImmediateLocation::ItemList);
|
||||
check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList);
|
||||
check_location(r"mod foo { #[attr] f$0 }", ImmediateLocation::ItemList);
|
||||
check_location(r"mod foo { fn f() {} f$0 }", ImmediateLocation::ItemList);
|
||||
check_location(r"mod foo$0 {}", None);
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ fn render_resolution_simple_(
|
|||
// Add `<>` for generic types
|
||||
let type_path_no_ty_args = matches!(
|
||||
ctx.completion.path_context(),
|
||||
Some(PathCompletionCtx { kind: PathKind::Type, has_type_args: false, .. })
|
||||
Some(PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. })
|
||||
) && ctx.completion.config.callable.is_some();
|
||||
if type_path_no_ty_args {
|
||||
if let Some(cap) = ctx.snippet_cap() {
|
||||
|
|
|
@ -202,7 +202,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
|
|||
Some(PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }) => {
|
||||
return false
|
||||
}
|
||||
Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type, .. }) => {
|
||||
Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }) => {
|
||||
cov_mark::hit!(no_parens_in_use_item);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -76,65 +76,65 @@ 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` only
|
||||
fn completes_where() {
|
||||
check(
|
||||
r"struct Struct $0",
|
||||
expect![[r#"
|
||||
kw const
|
||||
kw enum
|
||||
kw extern
|
||||
kw fn
|
||||
kw impl
|
||||
kw mod
|
||||
kw pub
|
||||
kw pub(crate)
|
||||
kw pub(super)
|
||||
kw static
|
||||
kw struct
|
||||
kw trait
|
||||
kw type
|
||||
kw union
|
||||
kw unsafe
|
||||
kw use
|
||||
"#]],
|
||||
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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after_fn_name() {
|
||||
// FIXME: This should emit `kw where` only
|
||||
check(
|
||||
r"fn func() $0",
|
||||
expect![[r#"
|
||||
kw const
|
||||
kw enum
|
||||
kw extern
|
||||
kw fn
|
||||
kw impl
|
||||
kw mod
|
||||
kw pub
|
||||
kw pub(crate)
|
||||
kw pub(super)
|
||||
kw static
|
||||
kw struct
|
||||
kw trait
|
||||
kw type
|
||||
kw union
|
||||
kw unsafe
|
||||
kw use
|
||||
"#]],
|
||||
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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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#"
|
||||
|
@ -137,6 +136,7 @@ fn after_visibility() {
|
|||
expect![[r#"
|
||||
kw const
|
||||
kw enum
|
||||
kw extern
|
||||
kw fn
|
||||
kw mod
|
||||
kw static
|
||||
|
@ -152,12 +152,10 @@ fn after_visibility() {
|
|||
|
||||
#[test]
|
||||
fn after_visibility_unsafe() {
|
||||
// FIXME this shouldn't show `impl`
|
||||
check(
|
||||
r#"pub unsafe $0"#,
|
||||
expect![[r#"
|
||||
kw fn
|
||||
kw impl
|
||||
kw trait
|
||||
"#]],
|
||||
);
|
||||
|
@ -178,7 +176,6 @@ fn in_impl_assoc_item_list() {
|
|||
kw pub(super)
|
||||
kw self::
|
||||
kw super::
|
||||
kw type
|
||||
kw unsafe
|
||||
"#]],
|
||||
)
|
||||
|
@ -199,7 +196,6 @@ fn in_impl_assoc_item_list_after_attr() {
|
|||
kw pub(super)
|
||||
kw self::
|
||||
kw super::
|
||||
kw type
|
||||
kw unsafe
|
||||
"#]],
|
||||
)
|
||||
|
@ -249,16 +245,9 @@ impl Test for () {
|
|||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
ta type Type1 =
|
||||
kw const
|
||||
kw crate::
|
||||
kw fn
|
||||
kw pub
|
||||
kw pub(crate)
|
||||
kw pub(super)
|
||||
kw self::
|
||||
kw super::
|
||||
kw type
|
||||
kw unsafe
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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…
Reference in a new issue