mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
Auto merge of #12187 - Veykril:completion-rev, r=Veykril
internal: More completion context refactoring
This commit is contained in:
commit
5d5bbec9b6
26 changed files with 350 additions and 247 deletions
|
@ -18,7 +18,7 @@ use syntax::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::module_or_attr,
|
completions::module_or_attr,
|
||||||
context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
|
context::{CompletionContext, IdentContext, PathCompletionCtx, PathKind, PathQualifierCtx},
|
||||||
item::CompletionItem,
|
item::CompletionItem,
|
||||||
Completions,
|
Completions,
|
||||||
};
|
};
|
||||||
|
@ -35,7 +35,10 @@ pub(crate) fn complete_known_attribute_input(
|
||||||
acc: &mut Completions,
|
acc: &mut Completions,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let attribute = ctx.fake_attribute_under_caret.as_ref()?;
|
let attribute = match &ctx.ident_ctx {
|
||||||
|
IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: Some(it) } => it,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
let name_ref = match attribute.path() {
|
let name_ref = match attribute.path() {
|
||||||
Some(p) => Some(p.as_single_name_ref()?),
|
Some(p) => Some(p.as_single_name_ref()?),
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -69,8 +72,8 @@ pub(crate) fn complete_known_attribute_input(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let (is_absolute_path, qualifier, is_inner, annotated_item_kind) = match ctx.path_context {
|
let (is_absolute_path, qualifier, is_inner, annotated_item_kind) = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(&PathCompletionCtx {
|
||||||
kind: PathKind::Attr { kind, annotated_item_kind },
|
kind: PathKind::Attr { kind, annotated_item_kind },
|
||||||
is_absolute_path,
|
is_absolute_path,
|
||||||
ref qualifier,
|
ref qualifier,
|
||||||
|
|
|
@ -11,9 +11,12 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let (qualifier, is_absolute_path) = match ctx.path_context {
|
let (qualifier, is_absolute_path) = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(&PathCompletionCtx {
|
||||||
kind: PathKind::Derive, ref qualifier, is_absolute_path, ..
|
kind: PathKind::Derive,
|
||||||
|
ref qualifier,
|
||||||
|
is_absolute_path,
|
||||||
|
..
|
||||||
}) => (qualifier, is_absolute_path),
|
}) => (qualifier, is_absolute_path),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,15 +3,21 @@
|
||||||
use ide_db::FxHashSet;
|
use ide_db::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{CompletionContext, PathCompletionCtx, PathKind},
|
context::{CompletionContext, DotAccess, NameRefContext, PathCompletionCtx, PathKind},
|
||||||
patterns::ImmediateLocation,
|
|
||||||
Completions,
|
Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Complete dot accesses, i.e. fields or methods.
|
/// Complete dot accesses, i.e. fields or methods.
|
||||||
pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let dot_receiver = match ctx.dot_receiver() {
|
let (dot_access, dot_receiver) = match ctx.nameref_ctx() {
|
||||||
Some(expr) => expr,
|
Some(NameRefContext {
|
||||||
|
dot_access:
|
||||||
|
Some(
|
||||||
|
access @ (DotAccess::Method { receiver: Some(receiver), .. }
|
||||||
|
| DotAccess::Field { receiver: Some(receiver), .. }),
|
||||||
|
),
|
||||||
|
..
|
||||||
|
}) => (access, receiver),
|
||||||
_ => return complete_undotted_self(acc, ctx),
|
_ => return complete_undotted_self(acc, ctx),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,7 +26,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(ctx.completion_location, Some(ImmediateLocation::MethodCall { .. })) {
|
if let DotAccess::Method { .. } = dot_access {
|
||||||
cov_mark::hit!(test_no_struct_field_completion_for_method_call);
|
cov_mark::hit!(test_no_struct_field_completion_for_method_call);
|
||||||
} else {
|
} else {
|
||||||
complete_fields(
|
complete_fields(
|
||||||
|
@ -38,7 +44,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if !ctx.config.enable_self_on_the_fly {
|
if !ctx.config.enable_self_on_the_fly {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match ctx.path_context {
|
match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(PathCompletionCtx {
|
||||||
is_absolute_path: false,
|
is_absolute_path: false,
|
||||||
qualifier: None,
|
qualifier: None,
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (&is_absolute_path, qualifier) = match &ctx.path_context {
|
let (&is_absolute_path, qualifier) = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(PathCompletionCtx {
|
||||||
kind: PathKind::Expr { .. },
|
kind: PathKind::Expr { .. },
|
||||||
is_absolute_path,
|
is_absolute_path,
|
||||||
|
|
|
@ -5,7 +5,9 @@ use syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind,
|
completions::Completions,
|
||||||
|
context::{CompletionContext, IdentContext},
|
||||||
|
CompletionItem, CompletionItemKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Most of these are feature gated, we should filter/add feature gate completions once we have them.
|
// Most of these are feature gated, we should filter/add feature gate completions once we have them.
|
||||||
|
@ -41,10 +43,14 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(crate) fn complete_extern_abi(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_extern_abi(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
||||||
if ctx.token.parent().and_then(ast::Abi::cast).is_none() {
|
let abi_str = match &ctx.ident_ctx {
|
||||||
return None;
|
IdentContext::String { expanded: Some(expanded), .. }
|
||||||
}
|
if expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) =>
|
||||||
let abi_str = ast::String::cast(ctx.token.clone())?;
|
{
|
||||||
|
expanded
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
let source_range = abi_str.text_range_between_quotes()?;
|
let source_range = abi_str.text_range_between_quotes()?;
|
||||||
for &abi in SUPPORTED_CALLING_CONVENTIONS {
|
for &abi in SUPPORTED_CALLING_CONVENTIONS {
|
||||||
CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc);
|
CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc);
|
||||||
|
|
|
@ -119,7 +119,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// FIXME: This should be encoded in a different way
|
// FIXME: This should be encoded in a different way
|
||||||
if ctx.pattern_ctx.is_none() && ctx.path_context.is_none() && !ctx.has_dot_receiver() {
|
if ctx.pattern_ctx.is_none() && ctx.path_context().is_none() && !ctx.has_dot_receiver() {
|
||||||
// completion inside `ast::Name` of a item declaration
|
// completion inside `ast::Name` of a item declaration
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -217,10 +217,9 @@ pub(crate) fn position_for_import(
|
||||||
) -> Option<SyntaxNode> {
|
) -> Option<SyntaxNode> {
|
||||||
Some(
|
Some(
|
||||||
match import_candidate {
|
match import_candidate {
|
||||||
Some(ImportCandidate::Path(_)) => ctx.name_syntax.as_ref()?.syntax(),
|
|
||||||
Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
|
Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
|
||||||
Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
|
Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
|
||||||
None => return ctx.original_token.parent(),
|
Some(ImportCandidate::Path(_)) | None => return ctx.original_token.parent(),
|
||||||
}
|
}
|
||||||
.clone(),
|
.clone(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,16 +2,21 @@
|
||||||
|
|
||||||
use ide_db::syntax_helpers::format_string::is_format_string;
|
use ide_db::syntax_helpers::format_string::is_format_string;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{ast, AstToken, TextRange, TextSize};
|
use syntax::{AstToken, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Completions};
|
use crate::{
|
||||||
|
context::{CompletionContext, IdentContext},
|
||||||
|
CompletionItem, CompletionItemKind, Completions,
|
||||||
|
};
|
||||||
|
|
||||||
/// Complete identifiers in format strings.
|
/// Complete identifiers in format strings.
|
||||||
pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let string = match ast::String::cast(ctx.token.clone())
|
let string = match &ctx.ident_ctx {
|
||||||
.zip(ast::String::cast(ctx.original_token.clone()))
|
IdentContext::String { expanded: Some(expanded), original }
|
||||||
{
|
if is_format_string(&expanded) =>
|
||||||
Some((expanded, original)) if is_format_string(&expanded) => original,
|
{
|
||||||
|
original
|
||||||
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let cursor = ctx.position.offset;
|
let cursor = ctx.position.offset;
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (&is_absolute_path, qualifier) = match &ctx.path_context {
|
let (&is_absolute_path, qualifier) = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(PathCompletionCtx {
|
||||||
kind: PathKind::Item { .. },
|
kind: PathKind::Item { .. },
|
||||||
is_absolute_path,
|
is_absolute_path,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//! - `self`, `super` and `crate`, as these are considered part of path completions.
|
//! - `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.
|
//! - `await`, as this is a postfix completion we handle this in the postfix completions.
|
||||||
|
|
||||||
use syntax::{SyntaxKind, T};
|
use syntax::T;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{PathCompletionCtx, PathKind},
|
context::{PathCompletionCtx, PathKind},
|
||||||
|
@ -11,18 +11,10 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if ctx.token.kind() == SyntaxKind::COMMENT {
|
|
||||||
cov_mark::hit!(no_keyword_completion_in_comments);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
|
if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
|
||||||
cov_mark::hit!(no_keyword_completion_in_record_lit);
|
cov_mark::hit!(no_keyword_completion_in_record_lit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ctx.fake_attribute_under_caret.is_some() {
|
|
||||||
cov_mark::hit!(no_keyword_completion_in_attr_of_expr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ctx.is_non_trivial_path() {
|
if ctx.is_non_trivial_path() {
|
||||||
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
|
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
|
||||||
return;
|
return;
|
||||||
|
@ -124,8 +116,8 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
||||||
add_keyword("mut", "mut ");
|
add_keyword("mut", "mut ");
|
||||||
}
|
}
|
||||||
|
|
||||||
let (can_be_stmt, in_loop_body) = match ctx.path_context {
|
let (can_be_stmt, in_loop_body) = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(&PathCompletionCtx {
|
||||||
is_absolute_path: false,
|
is_absolute_path: false,
|
||||||
kind: PathKind::Expr { in_block_expr, in_loop_body, .. },
|
kind: PathKind::Expr { in_block_expr, in_loop_body, .. },
|
||||||
..
|
..
|
||||||
|
|
|
@ -12,17 +12,20 @@ use syntax::{ast, TokenText};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::Completions,
|
completions::Completions,
|
||||||
context::{CompletionContext, LifetimeContext},
|
context::{CompletionContext, LifetimeContext, LifetimeKind},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Completes lifetimes.
|
/// Completes lifetimes.
|
||||||
pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let lp = match &ctx.lifetime_ctx {
|
let (lp, lifetime) = match ctx.lifetime_ctx() {
|
||||||
Some(LifetimeContext::Lifetime) => None,
|
Some(LifetimeContext { kind: LifetimeKind::Lifetime, lifetime }) => (None, lifetime),
|
||||||
Some(LifetimeContext::LifetimeParam { is_decl: false, param }) => Some(param),
|
Some(LifetimeContext {
|
||||||
|
kind: LifetimeKind::LifetimeParam { is_decl: false, param },
|
||||||
|
lifetime,
|
||||||
|
}) => (Some(param), lifetime),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let param_lifetime = match (ctx.lifetime(), lp.and_then(|lp| lp.lifetime())) {
|
let param_lifetime = match (lifetime, lp.and_then(|lp| lp.lifetime())) {
|
||||||
(Some(lt), Some(lp)) if lp == lt.clone() => return,
|
(Some(lt), Some(lp)) if lp == lt.clone() => return,
|
||||||
(Some(_), Some(lp)) => Some(lp),
|
(Some(_), Some(lp)) => Some(lp),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -46,7 +49,7 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
|
|
||||||
/// Completes labels.
|
/// Completes labels.
|
||||||
pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if !matches!(ctx.lifetime_ctx, Some(LifetimeContext::LabelRef)) {
|
if !matches!(ctx.lifetime_ctx(), Some(LifetimeContext { kind: LifetimeKind::LabelRef, .. })) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.process_all_names_raw(&mut |name, res| {
|
ctx.process_all_names_raw(&mut |name, res| {
|
||||||
|
|
|
@ -3,21 +3,23 @@
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use hir::{Module, ModuleSource};
|
use hir::{Module, ModuleSource};
|
||||||
use ide_db::FxHashSet;
|
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::{SourceDatabaseExt, VfsPath},
|
base_db::{SourceDatabaseExt, VfsPath},
|
||||||
RootDatabase, SymbolKind,
|
FxHashSet, RootDatabase, SymbolKind,
|
||||||
};
|
};
|
||||||
use syntax::{ast, AstNode, SyntaxKind};
|
use syntax::{ast, AstNode, SyntaxKind};
|
||||||
|
|
||||||
use crate::{context::NameContext, CompletionItem};
|
use crate::{
|
||||||
|
context::{CompletionContext, NameContext, NameKind},
|
||||||
use crate::{context::CompletionContext, Completions};
|
CompletionItem, Completions,
|
||||||
|
};
|
||||||
|
|
||||||
/// Complete mod declaration, i.e. `mod $0;`
|
/// Complete mod declaration, i.e. `mod $0;`
|
||||||
pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
||||||
let mod_under_caret = match &ctx.name_ctx {
|
let mod_under_caret = match ctx.name_ctx() {
|
||||||
Some(NameContext::Module(mod_under_caret)) if mod_under_caret.item_list().is_none() => {
|
Some(NameContext { kind: NameKind::Module(mod_under_caret), .. })
|
||||||
|
if mod_under_caret.item_list().is_none() =>
|
||||||
|
{
|
||||||
mod_under_caret
|
mod_under_caret
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
};
|
};
|
||||||
let refutable = patctx.refutability == PatternRefutability::Refutable;
|
let refutable = patctx.refutability == PatternRefutability::Refutable;
|
||||||
|
|
||||||
if let Some(path_ctx) = &ctx.path_context {
|
if let Some(path_ctx) = ctx.path_context() {
|
||||||
pattern_path_completion(acc, ctx, path_ctx);
|
pattern_path_completion(acc, ctx, path_ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,8 @@ use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::postfix::format_like::add_format_like_completions,
|
completions::postfix::format_like::add_format_like_completions,
|
||||||
context::CompletionContext,
|
context::{CompletionContext, DotAccess, NameRefContext},
|
||||||
item::{Builder, CompletionRelevancePostfixMatch},
|
item::{Builder, CompletionRelevancePostfixMatch},
|
||||||
patterns::ImmediateLocation,
|
|
||||||
CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,
|
CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,11 +23,15 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
|
let (dot_receiver, receiver_is_ambiguous_float_literal) = match ctx.nameref_ctx() {
|
||||||
Some(ImmediateLocation::MethodCall { receiver: Some(it), .. }) => (it, false),
|
Some(NameRefContext {
|
||||||
Some(ImmediateLocation::FieldAccess {
|
dot_access: Some(DotAccess::Method { receiver: Some(it), .. }),
|
||||||
receiver: Some(it),
|
..
|
||||||
receiver_is_ambiguous_float_literal,
|
}) => (it, false),
|
||||||
|
Some(NameRefContext {
|
||||||
|
dot_access:
|
||||||
|
Some(DotAccess::Field { receiver: Some(it), receiver_is_ambiguous_float_literal }),
|
||||||
|
..
|
||||||
}) => (it, *receiver_is_ambiguous_float_literal),
|
}) => (it, *receiver_is_ambiguous_float_literal),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let can_be_stmt = match ctx.path_context {
|
let &can_be_stmt = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(PathCompletionCtx {
|
||||||
is_absolute_path: false,
|
is_absolute_path: false,
|
||||||
qualifier: None,
|
qualifier: None,
|
||||||
|
@ -43,7 +43,7 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let path_kind = match ctx.path_context {
|
let path_kind = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(PathCompletionCtx {
|
||||||
is_absolute_path: false,
|
is_absolute_path: false,
|
||||||
qualifier: None,
|
qualifier: None,
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (&is_absolute_path, qualifier) = match &ctx.path_context {
|
let (&is_absolute_path, qualifier) = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx { kind: PathKind::Type, is_absolute_path, qualifier, .. }) => {
|
Some(PathCompletionCtx { kind: PathKind::Type, is_absolute_path, qualifier, .. }) => {
|
||||||
(is_absolute_path, qualifier)
|
(is_absolute_path, qualifier)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,19 @@ use ide_db::FxHashSet;
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
|
context::{CompletionContext, NameRefContext, PathCompletionCtx, PathKind, PathQualifierCtx},
|
||||||
item::Builder,
|
item::Builder,
|
||||||
CompletionRelevance, Completions,
|
CompletionRelevance, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let (&is_absolute_path, qualifier) = match &ctx.path_context {
|
let (&is_absolute_path, qualifier, name_ref) = match ctx.nameref_ctx() {
|
||||||
Some(PathCompletionCtx { kind: PathKind::Use, is_absolute_path, qualifier, .. }) => {
|
Some(NameRefContext {
|
||||||
(is_absolute_path, qualifier)
|
path_ctx:
|
||||||
}
|
Some(PathCompletionCtx { kind: PathKind::Use, is_absolute_path, qualifier, .. }),
|
||||||
|
nameref,
|
||||||
|
..
|
||||||
|
}) => (is_absolute_path, qualifier, nameref),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,7 +58,7 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
let module_scope = module.scope(ctx.db, Some(ctx.module));
|
let module_scope = module.scope(ctx.db, Some(ctx.module));
|
||||||
let unknown_is_current = |name: &hir::Name| {
|
let unknown_is_current = |name: &hir::Name| {
|
||||||
matches!(
|
matches!(
|
||||||
ctx.name_ref(),
|
name_ref,
|
||||||
Some(name_ref) if name_ref.syntax().text() == name.to_smol_str().as_str()
|
Some(name_ref) if name_ref.syntax().text() == name.to_smol_str().as_str()
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_vis(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_vis(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let (&is_absolute_path, qualifier, &has_in_token) = match &ctx.path_context {
|
let (&is_absolute_path, qualifier, &has_in_token) = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(PathCompletionCtx {
|
||||||
kind: PathKind::Vis { has_in_token },
|
kind: PathKind::Vis { has_in_token },
|
||||||
is_absolute_path,
|
is_absolute_path,
|
||||||
|
|
|
@ -14,8 +14,8 @@ use ide_db::{
|
||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::{find_node_at_offset, non_trivia_sibling},
|
algo::{find_node_at_offset, non_trivia_sibling},
|
||||||
ast::{self, AttrKind, HasName, NameOrNameRef},
|
ast::{self, AttrKind, HasArgList, HasName, NameOrNameRef},
|
||||||
match_ast, AstNode, NodeOrToken,
|
match_ast, AstNode, AstToken, NodeOrToken,
|
||||||
SyntaxKind::{self, *},
|
SyntaxKind::{self, *},
|
||||||
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
|
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
|
||||||
};
|
};
|
||||||
|
@ -117,16 +117,29 @@ pub(super) struct PatternContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum LifetimeContext {
|
pub(super) struct LifetimeContext {
|
||||||
|
pub(super) lifetime: Option<ast::Lifetime>,
|
||||||
|
pub(super) kind: LifetimeKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) enum LifetimeKind {
|
||||||
LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
|
LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
|
||||||
Lifetime,
|
Lifetime,
|
||||||
LabelRef,
|
LabelRef,
|
||||||
LabelDef,
|
LabelDef,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct NameContext {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) name: Option<ast::Name>,
|
||||||
|
pub(super) kind: NameKind,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(super) enum NameContext {
|
pub(super) enum NameKind {
|
||||||
Const,
|
Const,
|
||||||
ConstParam,
|
ConstParam,
|
||||||
Enum,
|
Enum,
|
||||||
|
@ -148,6 +161,43 @@ pub(super) enum NameContext {
|
||||||
Variant,
|
Variant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct NameRefContext {
|
||||||
|
/// NameRef syntax in the original file
|
||||||
|
pub(super) nameref: Option<ast::NameRef>,
|
||||||
|
pub(super) dot_access: Option<DotAccess>,
|
||||||
|
pub(super) path_ctx: Option<PathCompletionCtx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) enum IdentContext {
|
||||||
|
Name(NameContext),
|
||||||
|
NameRef(NameRefContext),
|
||||||
|
Lifetime(LifetimeContext),
|
||||||
|
/// Original token, fake token
|
||||||
|
String {
|
||||||
|
original: ast::String,
|
||||||
|
expanded: Option<ast::String>,
|
||||||
|
},
|
||||||
|
UnexpandedAttrTT {
|
||||||
|
fake_attribute_under_caret: Option<ast::Attr>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) enum DotAccess {
|
||||||
|
Field {
|
||||||
|
receiver: Option<ast::Expr>,
|
||||||
|
/// True if the receiver is an integer and there is no ident in the original file after it yet
|
||||||
|
/// like `0.$0`
|
||||||
|
receiver_is_ambiguous_float_literal: bool,
|
||||||
|
},
|
||||||
|
Method {
|
||||||
|
receiver: Option<ast::Expr>,
|
||||||
|
has_parens: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum ParamKind {
|
pub(crate) enum ParamKind {
|
||||||
Function(ast::Fn),
|
Function(ast::Fn),
|
||||||
|
@ -183,20 +233,16 @@ pub(crate) struct CompletionContext<'a> {
|
||||||
pub(super) function_def: Option<ast::Fn>,
|
pub(super) function_def: Option<ast::Fn>,
|
||||||
/// The parent impl of the cursor position if it exists.
|
/// The parent impl of the cursor position if it exists.
|
||||||
pub(super) impl_def: Option<ast::Impl>,
|
pub(super) impl_def: Option<ast::Impl>,
|
||||||
/// The NameLike under the cursor in the original file if it exists.
|
|
||||||
pub(super) name_syntax: Option<ast::NameLike>,
|
|
||||||
/// Are we completing inside a let statement with a missing semicolon?
|
/// Are we completing inside a let statement with a missing semicolon?
|
||||||
pub(super) incomplete_let: bool,
|
pub(super) incomplete_let: bool,
|
||||||
|
|
||||||
pub(super) completion_location: Option<ImmediateLocation>,
|
pub(super) completion_location: Option<ImmediateLocation>,
|
||||||
pub(super) prev_sibling: Option<ImmediatePrevSibling>,
|
pub(super) prev_sibling: Option<ImmediatePrevSibling>,
|
||||||
pub(super) fake_attribute_under_caret: Option<ast::Attr>,
|
|
||||||
pub(super) previous_token: Option<SyntaxToken>,
|
pub(super) previous_token: Option<SyntaxToken>,
|
||||||
|
|
||||||
pub(super) name_ctx: Option<NameContext>,
|
pub(super) ident_ctx: IdentContext,
|
||||||
pub(super) lifetime_ctx: Option<LifetimeContext>,
|
|
||||||
pub(super) pattern_ctx: Option<PatternContext>,
|
pub(super) pattern_ctx: Option<PatternContext>,
|
||||||
pub(super) path_context: Option<PathCompletionCtx>,
|
|
||||||
|
|
||||||
pub(super) existing_derives: FxHashSet<hir::Macro>,
|
pub(super) existing_derives: FxHashSet<hir::Macro>,
|
||||||
|
|
||||||
|
@ -220,14 +266,6 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn name_ref(&self) -> Option<&ast::NameRef> {
|
|
||||||
self.name_syntax.as_ref().and_then(ast::NameLike::as_name_ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn lifetime(&self) -> Option<&ast::Lifetime> {
|
|
||||||
self.name_syntax.as_ref().and_then(ast::NameLike::as_lifetime)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
|
pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
|
||||||
self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
|
self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
|
||||||
}
|
}
|
||||||
|
@ -236,22 +274,40 @@ impl<'a> CompletionContext<'a> {
|
||||||
FamousDefs(&self.sema, self.krate)
|
FamousDefs(&self.sema, self.krate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn nameref_ctx(&self) -> Option<&NameRefContext> {
|
||||||
|
match &self.ident_ctx {
|
||||||
|
IdentContext::NameRef(it) => Some(it),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn name_ctx(&self) -> Option<&NameContext> {
|
||||||
|
match &self.ident_ctx {
|
||||||
|
IdentContext::Name(it) => Some(it),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lifetime_ctx(&self) -> Option<&LifetimeContext> {
|
||||||
|
match &self.ident_ctx {
|
||||||
|
IdentContext::Lifetime(it) => Some(it),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
|
pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
|
||||||
match &self.completion_location {
|
match self.nameref_ctx() {
|
||||||
Some(
|
Some(NameRefContext {
|
||||||
ImmediateLocation::MethodCall { receiver, .. }
|
dot_access:
|
||||||
| ImmediateLocation::FieldAccess { receiver, .. },
|
Some(DotAccess::Method { receiver, .. } | DotAccess::Field { receiver, .. }),
|
||||||
) => receiver.as_ref(),
|
..
|
||||||
|
}) => receiver.as_ref(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn has_dot_receiver(&self) -> bool {
|
pub(crate) fn has_dot_receiver(&self) -> bool {
|
||||||
matches!(
|
self.dot_receiver().is_some()
|
||||||
&self.completion_location,
|
|
||||||
Some(ImmediateLocation::FieldAccess { receiver, .. } | ImmediateLocation::MethodCall { receiver,.. })
|
|
||||||
if receiver.is_some()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expects_assoc_item(&self) -> bool {
|
pub(crate) fn expects_assoc_item(&self) -> bool {
|
||||||
|
@ -259,7 +315,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expects_variant(&self) -> bool {
|
pub(crate) fn expects_variant(&self) -> bool {
|
||||||
matches!(self.name_ctx, Some(NameContext::Variant))
|
matches!(self.name_ctx(), Some(NameContext { kind: NameKind::Variant, .. }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
|
pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
|
||||||
|
@ -284,7 +340,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
|
|
||||||
pub(crate) fn expect_field(&self) -> bool {
|
pub(crate) fn expect_field(&self) -> bool {
|
||||||
matches!(self.completion_location, Some(ImmediateLocation::TupleField))
|
matches!(self.completion_location, Some(ImmediateLocation::TupleField))
|
||||||
|| matches!(self.name_ctx, Some(NameContext::RecordField))
|
|| matches!(self.name_ctx(), Some(NameContext { kind: NameKind::RecordField, .. }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the cursor is right after a trait or impl header.
|
/// Whether the cursor is right after a trait or impl header.
|
||||||
|
@ -321,24 +377,31 @@ impl<'a> CompletionContext<'a> {
|
||||||
self.completion_location,
|
self.completion_location,
|
||||||
Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_))
|
Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_))
|
||||||
)
|
)
|
||||||
|| matches!(self.name_ctx, Some(NameContext::Module(_) | NameContext::Rename))
|
|| matches!(
|
||||||
|
self.name_ctx(),
|
||||||
|
Some(NameContext { kind: NameKind::Module(_) | NameKind::Rename, .. })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
|
||||||
|
self.nameref_ctx().and_then(|ctx| ctx.path_ctx.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expects_expression(&self) -> bool {
|
pub(crate) fn expects_expression(&self) -> bool {
|
||||||
matches!(self.path_context, Some(PathCompletionCtx { kind: PathKind::Expr { .. }, .. }))
|
matches!(self.path_context(), Some(PathCompletionCtx { kind: PathKind::Expr { .. }, .. }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expects_type(&self) -> bool {
|
pub(crate) fn expects_type(&self) -> bool {
|
||||||
matches!(self.path_context, Some(PathCompletionCtx { kind: PathKind::Type, .. }))
|
matches!(self.path_context(), Some(PathCompletionCtx { kind: PathKind::Type, .. }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn path_is_call(&self) -> bool {
|
pub(crate) fn path_is_call(&self) -> bool {
|
||||||
self.path_context.as_ref().map_or(false, |it| it.has_call_parens)
|
self.path_context().map_or(false, |it| it.has_call_parens)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_non_trivial_path(&self) -> bool {
|
pub(crate) fn is_non_trivial_path(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.path_context,
|
self.path_context(),
|
||||||
Some(
|
Some(
|
||||||
PathCompletionCtx { is_absolute_path: true, .. }
|
PathCompletionCtx { is_absolute_path: true, .. }
|
||||||
| PathCompletionCtx { qualifier: Some(_), .. }
|
| PathCompletionCtx { qualifier: Some(_), .. }
|
||||||
|
@ -347,11 +410,11 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
|
pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
|
||||||
self.path_context.as_ref().and_then(|it| it.qualifier.as_ref().map(|it| &it.path))
|
self.path_context().and_then(|it| it.qualifier.as_ref().map(|it| &it.path))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn path_kind(&self) -> Option<PathKind> {
|
pub(crate) fn path_kind(&self) -> Option<PathKind> {
|
||||||
self.path_context.as_ref().map(|it| it.kind)
|
self.path_context().map(|it| it.kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_immediately_after_macro_bang(&self) -> bool {
|
pub(crate) fn is_immediately_after_macro_bang(&self) -> bool {
|
||||||
|
@ -497,25 +560,22 @@ impl<'a> CompletionContext<'a> {
|
||||||
expected_type: None,
|
expected_type: None,
|
||||||
function_def: None,
|
function_def: None,
|
||||||
impl_def: None,
|
impl_def: None,
|
||||||
name_syntax: None,
|
incomplete_let: false,
|
||||||
lifetime_ctx: None,
|
|
||||||
pattern_ctx: None,
|
|
||||||
name_ctx: None,
|
|
||||||
completion_location: None,
|
completion_location: None,
|
||||||
prev_sibling: None,
|
prev_sibling: None,
|
||||||
fake_attribute_under_caret: None,
|
|
||||||
previous_token: None,
|
previous_token: None,
|
||||||
path_context: None,
|
// dummy value, will be overwritten
|
||||||
locals,
|
ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
|
||||||
incomplete_let: false,
|
pattern_ctx: None,
|
||||||
existing_derives: Default::default(),
|
existing_derives: Default::default(),
|
||||||
|
locals,
|
||||||
};
|
};
|
||||||
ctx.expand_and_fill(
|
ctx.expand_and_fill(
|
||||||
original_file.syntax().clone(),
|
original_file.syntax().clone(),
|
||||||
file_with_fake_ident.syntax().clone(),
|
file_with_fake_ident.syntax().clone(),
|
||||||
offset,
|
offset,
|
||||||
fake_ident_token,
|
fake_ident_token,
|
||||||
);
|
)?;
|
||||||
Some(ctx)
|
Some(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,7 +588,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
mut speculative_file: SyntaxNode,
|
mut speculative_file: SyntaxNode,
|
||||||
mut offset: TextSize,
|
mut offset: TextSize,
|
||||||
mut fake_ident_token: SyntaxToken,
|
mut fake_ident_token: SyntaxToken,
|
||||||
) {
|
) -> Option<()> {
|
||||||
let _p = profile::span("CompletionContext::expand_and_fill");
|
let _p = profile::span("CompletionContext::expand_and_fill");
|
||||||
let mut derive_ctx = None;
|
let mut derive_ctx = None;
|
||||||
|
|
||||||
|
@ -658,7 +718,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
break 'expansion;
|
break 'expansion;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fill(&original_file, speculative_file, offset, derive_ctx);
|
self.fill(&original_file, speculative_file, offset, derive_ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the expected type and name of the cursor position.
|
/// Calculate the expected type and name of the cursor position.
|
||||||
|
@ -806,7 +866,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
file_with_fake_ident: SyntaxNode,
|
file_with_fake_ident: SyntaxNode,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
|
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
|
||||||
) {
|
) -> Option<()> {
|
||||||
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
|
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
|
||||||
let syntax_element = NodeOrToken::Token(fake_ident_token);
|
let syntax_element = NodeOrToken::Token(fake_ident_token);
|
||||||
if is_in_token_of_for_loop(syntax_element.clone()) {
|
if is_in_token_of_for_loop(syntax_element.clone()) {
|
||||||
|
@ -815,11 +875,10 @@ impl<'a> CompletionContext<'a> {
|
||||||
// don't bother populating the context
|
// don't bother populating the context
|
||||||
// FIXME: the completion calculations should end up good enough
|
// FIXME: the completion calculations should end up good enough
|
||||||
// such that this special case becomes unnecessary
|
// such that this special case becomes unnecessary
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.previous_token = previous_token(syntax_element.clone());
|
self.previous_token = previous_token(syntax_element.clone());
|
||||||
self.fake_attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
|
|
||||||
|
|
||||||
self.incomplete_let =
|
self.incomplete_let =
|
||||||
syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
|
syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
|
||||||
|
@ -841,27 +900,53 @@ impl<'a> CompletionContext<'a> {
|
||||||
if let Some(ast::NameLike::NameRef(name_ref)) =
|
if let Some(ast::NameLike::NameRef(name_ref)) =
|
||||||
find_node_at_offset(&file_with_fake_ident, offset)
|
find_node_at_offset(&file_with_fake_ident, offset)
|
||||||
{
|
{
|
||||||
self.name_syntax =
|
let parent = name_ref.syntax().parent()?;
|
||||||
find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
|
let (mut nameref_ctx, _) =
|
||||||
if let Some((path_ctx, _)) =
|
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
|
||||||
Self::classify_name_ref(&self.sema, &original_file, name_ref)
|
if let Some(path_ctx) = &mut nameref_ctx.path_ctx {
|
||||||
{
|
path_ctx.kind = PathKind::Derive;
|
||||||
self.path_context =
|
|
||||||
Some(PathCompletionCtx { kind: PathKind::Derive, ..path_ctx });
|
|
||||||
}
|
}
|
||||||
|
self.ident_ctx = IdentContext::NameRef(nameref_ctx);
|
||||||
|
return Some(());
|
||||||
}
|
}
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
|
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return,
|
None => {
|
||||||
|
if let Some(original) = ast::String::cast(self.original_token.clone()) {
|
||||||
|
self.ident_ctx = IdentContext::String {
|
||||||
|
original,
|
||||||
|
expanded: ast::String::cast(self.token.clone()),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Fix up trailing whitespace problem
|
||||||
|
// #[attr(foo = $0
|
||||||
|
let token = if self.token.kind() == SyntaxKind::WHITESPACE {
|
||||||
|
self.previous_token.as_ref()?
|
||||||
|
} else {
|
||||||
|
&self.token
|
||||||
|
};
|
||||||
|
let p = token.parent()?;
|
||||||
|
if p.kind() == SyntaxKind::TOKEN_TREE
|
||||||
|
&& p.ancestors().any(|it| it.kind() == SyntaxKind::META)
|
||||||
|
{
|
||||||
|
self.ident_ctx = IdentContext::UnexpandedAttrTT {
|
||||||
|
fake_attribute_under_caret: syntax_element
|
||||||
|
.ancestors()
|
||||||
|
.find_map(ast::Attr::cast),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
self.completion_location =
|
self.completion_location =
|
||||||
determine_location(&self.sema, original_file, offset, &name_like);
|
determine_location(&self.sema, original_file, offset, &name_like);
|
||||||
self.prev_sibling = determine_prev_sibling(&name_like);
|
self.prev_sibling = determine_prev_sibling(&name_like);
|
||||||
self.name_syntax =
|
|
||||||
find_node_at_offset(original_file, name_like.syntax().text_range().start());
|
|
||||||
self.impl_def = self
|
self.impl_def = self
|
||||||
.sema
|
.sema
|
||||||
.token_ancestors_with_macros(self.token.clone())
|
.token_ancestors_with_macros(self.token.clone())
|
||||||
|
@ -875,30 +960,31 @@ impl<'a> CompletionContext<'a> {
|
||||||
|
|
||||||
match name_like {
|
match name_like {
|
||||||
ast::NameLike::Lifetime(lifetime) => {
|
ast::NameLike::Lifetime(lifetime) => {
|
||||||
self.lifetime_ctx = Self::classify_lifetime(&self.sema, original_file, lifetime);
|
self.ident_ctx = IdentContext::Lifetime(Self::classify_lifetime(
|
||||||
|
&self.sema,
|
||||||
|
original_file,
|
||||||
|
lifetime,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
ast::NameLike::NameRef(name_ref) => {
|
ast::NameLike::NameRef(name_ref) => {
|
||||||
if let Some((path_ctx, pat_ctx)) =
|
let parent = name_ref.syntax().parent()?;
|
||||||
Self::classify_name_ref(&self.sema, original_file, name_ref)
|
let (nameref_ctx, pat_ctx) =
|
||||||
{
|
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
|
||||||
self.path_context = Some(path_ctx);
|
self.ident_ctx = IdentContext::NameRef(nameref_ctx);
|
||||||
self.pattern_ctx = pat_ctx;
|
self.pattern_ctx = pat_ctx;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast::NameLike::Name(name) => {
|
ast::NameLike::Name(name) => {
|
||||||
if let Some((name_ctx, pat_ctx)) =
|
let (name_ctx, pat_ctx) = Self::classify_name(&self.sema, original_file, name)?;
|
||||||
Self::classify_name(&self.sema, original_file, name)
|
self.pattern_ctx = pat_ctx;
|
||||||
{
|
self.ident_ctx = IdentContext::Name(name_ctx);
|
||||||
self.pattern_ctx = pat_ctx;
|
|
||||||
self.name_ctx = Some(name_ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_lifetime(
|
fn classify_lifetime(
|
||||||
_sema: &Semantics<RootDatabase>,
|
_sema: &Semantics<RootDatabase>,
|
||||||
_original_file: &SyntaxNode,
|
original_file: &SyntaxNode,
|
||||||
lifetime: ast::Lifetime,
|
lifetime: ast::Lifetime,
|
||||||
) -> Option<LifetimeContext> {
|
) -> Option<LifetimeContext> {
|
||||||
let parent = lifetime.syntax().parent()?;
|
let parent = lifetime.syntax().parent()?;
|
||||||
|
@ -906,18 +992,21 @@ impl<'a> CompletionContext<'a> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(match_ast! {
|
let kind = match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::LifetimeParam(param) => LifetimeContext::LifetimeParam {
|
ast::LifetimeParam(param) => LifetimeKind::LifetimeParam {
|
||||||
is_decl: param.lifetime().as_ref() == Some(&lifetime),
|
is_decl: param.lifetime().as_ref() == Some(&lifetime),
|
||||||
param
|
param
|
||||||
},
|
},
|
||||||
ast::BreakExpr(_) => LifetimeContext::LabelRef,
|
ast::BreakExpr(_) => LifetimeKind::LabelRef,
|
||||||
ast::ContinueExpr(_) => LifetimeContext::LabelRef,
|
ast::ContinueExpr(_) => LifetimeKind::LabelRef,
|
||||||
ast::Label(_) => LifetimeContext::LabelDef,
|
ast::Label(_) => LifetimeKind::LabelDef,
|
||||||
_ => LifetimeContext::Lifetime,
|
_ => LifetimeKind::Lifetime,
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
|
let lifetime = find_node_at_offset(&original_file, lifetime.syntax().text_range().start());
|
||||||
|
|
||||||
|
Some(LifetimeContext { lifetime, kind })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_name(
|
fn classify_name(
|
||||||
|
@ -927,12 +1016,12 @@ impl<'a> CompletionContext<'a> {
|
||||||
) -> Option<(NameContext, Option<PatternContext>)> {
|
) -> Option<(NameContext, Option<PatternContext>)> {
|
||||||
let parent = name.syntax().parent()?;
|
let parent = name.syntax().parent()?;
|
||||||
let mut pat_ctx = None;
|
let mut pat_ctx = None;
|
||||||
let name_ctx = match_ast! {
|
let kind = match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::Const(_) => NameContext::Const,
|
ast::Const(_) => NameKind::Const,
|
||||||
ast::ConstParam(_) => NameContext::ConstParam,
|
ast::ConstParam(_) => NameKind::ConstParam,
|
||||||
ast::Enum(_) => NameContext::Enum,
|
ast::Enum(_) => NameKind::Enum,
|
||||||
ast::Fn(_) => NameContext::Function,
|
ast::Fn(_) => NameKind::Function,
|
||||||
ast::IdentPat(bind_pat) => {
|
ast::IdentPat(bind_pat) => {
|
||||||
let is_name_in_field_pat = bind_pat
|
let is_name_in_field_pat = bind_pat
|
||||||
.syntax()
|
.syntax()
|
||||||
|
@ -943,36 +1032,67 @@ impl<'a> CompletionContext<'a> {
|
||||||
pat_ctx = Some(pattern_context_for(original_file, bind_pat.into()));
|
pat_ctx = Some(pattern_context_for(original_file, bind_pat.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
NameContext::IdentPat
|
NameKind::IdentPat
|
||||||
},
|
},
|
||||||
ast::MacroDef(_) => NameContext::MacroDef,
|
ast::MacroDef(_) => NameKind::MacroDef,
|
||||||
ast::MacroRules(_) => NameContext::MacroRules,
|
ast::MacroRules(_) => NameKind::MacroRules,
|
||||||
ast::Module(module) => NameContext::Module(module),
|
ast::Module(module) => NameKind::Module(module),
|
||||||
ast::RecordField(_) => NameContext::RecordField,
|
ast::RecordField(_) => NameKind::RecordField,
|
||||||
ast::Rename(_) => NameContext::Rename,
|
ast::Rename(_) => NameKind::Rename,
|
||||||
ast::SelfParam(_) => NameContext::SelfParam,
|
ast::SelfParam(_) => NameKind::SelfParam,
|
||||||
ast::Static(_) => NameContext::Static,
|
ast::Static(_) => NameKind::Static,
|
||||||
ast::Struct(_) => NameContext::Struct,
|
ast::Struct(_) => NameKind::Struct,
|
||||||
ast::Trait(_) => NameContext::Trait,
|
ast::Trait(_) => NameKind::Trait,
|
||||||
ast::TypeAlias(_) => NameContext::TypeAlias,
|
ast::TypeAlias(_) => NameKind::TypeAlias,
|
||||||
ast::TypeParam(_) => NameContext::TypeParam,
|
ast::TypeParam(_) => NameKind::TypeParam,
|
||||||
ast::Union(_) => NameContext::Union,
|
ast::Union(_) => NameKind::Union,
|
||||||
ast::Variant(_) => NameContext::Variant,
|
ast::Variant(_) => NameKind::Variant,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Some((name_ctx, pat_ctx))
|
let name = find_node_at_offset(&original_file, name.syntax().text_range().start());
|
||||||
|
Some((NameContext { name, kind }, pat_ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_name_ref(
|
fn classify_name_ref(
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &Semantics<RootDatabase>,
|
||||||
original_file: &SyntaxNode,
|
original_file: &SyntaxNode,
|
||||||
name_ref: ast::NameRef,
|
name_ref: ast::NameRef,
|
||||||
) -> Option<(PathCompletionCtx, Option<PatternContext>)> {
|
parent: SyntaxNode,
|
||||||
let parent = name_ref.syntax().parent()?;
|
) -> (NameRefContext, Option<PatternContext>) {
|
||||||
let segment = ast::PathSegment::cast(parent)?;
|
let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
|
||||||
let path = segment.parent_path();
|
|
||||||
|
|
||||||
|
let mut nameref_ctx = NameRefContext { dot_access: None, path_ctx: None, nameref };
|
||||||
|
|
||||||
|
let segment = match_ast! {
|
||||||
|
match parent {
|
||||||
|
ast::PathSegment(segment) => segment,
|
||||||
|
ast::FieldExpr(field) => {
|
||||||
|
let receiver = find_in_original_file(field.expr(), original_file);
|
||||||
|
let receiver_is_ambiguous_float_literal = match &receiver {
|
||||||
|
Some(ast::Expr::Literal(l)) => matches! {
|
||||||
|
l.kind(),
|
||||||
|
ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.kind() == T![.])
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
nameref_ctx.dot_access = Some(DotAccess::Field { receiver, receiver_is_ambiguous_float_literal });
|
||||||
|
return (nameref_ctx, None);
|
||||||
|
},
|
||||||
|
ast::MethodCallExpr(method) => {
|
||||||
|
nameref_ctx.dot_access = Some(
|
||||||
|
DotAccess::Method {
|
||||||
|
receiver: find_in_original_file(method.receiver(), original_file),
|
||||||
|
has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some())
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return (nameref_ctx, None);
|
||||||
|
},
|
||||||
|
_ => return (nameref_ctx, None),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = segment.parent_path();
|
||||||
let mut path_ctx = PathCompletionCtx {
|
let mut path_ctx = PathCompletionCtx {
|
||||||
has_call_parens: false,
|
has_call_parens: false,
|
||||||
has_macro_bang: false,
|
has_macro_bang: false,
|
||||||
|
@ -992,7 +1112,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
};
|
};
|
||||||
|
|
||||||
path_ctx.kind = path.syntax().ancestors().find_map(|it| {
|
let kind = path.syntax().ancestors().find_map(|it| {
|
||||||
// using Option<Option<PathKind>> as extra controlflow
|
// using Option<Option<PathKind>> as extra controlflow
|
||||||
let kind = match_ast! {
|
let kind = match_ast! {
|
||||||
match it {
|
match it {
|
||||||
|
@ -1073,7 +1193,11 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Some(kind)
|
Some(kind)
|
||||||
}).flatten()?;
|
}).flatten();
|
||||||
|
match kind {
|
||||||
|
Some(kind) => path_ctx.kind = kind,
|
||||||
|
None => return (nameref_ctx, pat_ctx),
|
||||||
|
}
|
||||||
path_ctx.has_type_args = segment.generic_arg_list().is_some();
|
path_ctx.has_type_args = segment.generic_arg_list().is_some();
|
||||||
|
|
||||||
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
|
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
|
||||||
|
@ -1109,17 +1233,13 @@ impl<'a> CompletionContext<'a> {
|
||||||
is_infer_qualifier,
|
is_infer_qualifier,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Some((path_ctx, pat_ctx));
|
} else if let Some(segment) = path.segment() {
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(segment) = path.segment() {
|
|
||||||
if segment.coloncolon_token().is_some() {
|
if segment.coloncolon_token().is_some() {
|
||||||
path_ctx.is_absolute_path = true;
|
path_ctx.is_absolute_path = true;
|
||||||
return Some((path_ctx, pat_ctx));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nameref_ctx.path_ctx = Some(path_ctx);
|
||||||
Some((path_ctx, pat_ctx))
|
(nameref_ctx, pat_ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1174,6 +1294,14 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_in_original_file<N: AstNode>(x: Option<N>, original_file: &SyntaxNode) -> Option<N> {
|
||||||
|
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
|
||||||
|
let range = syntax.text_range().intersect(range)?;
|
||||||
|
syntax.covering_element(range).ancestors().find_map(N::cast)
|
||||||
|
}
|
||||||
|
x.map(|e| e.syntax().text_range()).and_then(|r| find_node_with_range(original_file, r))
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to find `node` inside `syntax` via `node`'s text range.
|
/// Attempts to find `node` inside `syntax` via `node`'s text range.
|
||||||
fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
|
fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
|
||||||
let syntax_range = syntax.text_range();
|
let syntax_range = syntax.text_range();
|
||||||
|
|
|
@ -8,7 +8,7 @@ use hir::Semantics;
|
||||||
use ide_db::RootDatabase;
|
use ide_db::RootDatabase;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::non_trivia_sibling,
|
algo::non_trivia_sibling,
|
||||||
ast::{self, HasArgList, HasLoopBody, HasName},
|
ast::{self, HasLoopBody, HasName},
|
||||||
match_ast, AstNode, Direction, SyntaxElement,
|
match_ast, AstNode, Direction, SyntaxElement,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxNode, SyntaxToken, TextRange, TextSize,
|
SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||||
|
@ -51,16 +51,6 @@ pub(crate) enum ImmediateLocation {
|
||||||
TypeBound,
|
TypeBound,
|
||||||
/// Original file ast node
|
/// Original file ast node
|
||||||
TypeAnnotation(TypeAnnotation),
|
TypeAnnotation(TypeAnnotation),
|
||||||
/// Original file ast node
|
|
||||||
MethodCall {
|
|
||||||
receiver: Option<ast::Expr>,
|
|
||||||
has_parens: bool,
|
|
||||||
},
|
|
||||||
/// Original file ast node
|
|
||||||
FieldAccess {
|
|
||||||
receiver: Option<ast::Expr>,
|
|
||||||
receiver_is_ambiguous_float_literal: bool,
|
|
||||||
},
|
|
||||||
// Only set from a type arg
|
// Only set from a type arg
|
||||||
/// Original file ast node
|
/// Original file ast node
|
||||||
GenericArgList(ast::GenericArgList),
|
GenericArgList(ast::GenericArgList),
|
||||||
|
@ -226,25 +216,6 @@ pub(crate) fn determine_location(
|
||||||
ast::GenericArgList(_) => sema
|
ast::GenericArgList(_) => sema
|
||||||
.find_node_at_offset_with_macros(original_file, offset)
|
.find_node_at_offset_with_macros(original_file, offset)
|
||||||
.map(ImmediateLocation::GenericArgList)?,
|
.map(ImmediateLocation::GenericArgList)?,
|
||||||
ast::FieldExpr(it) => {
|
|
||||||
let receiver = find_in_original_file(it.expr(), original_file);
|
|
||||||
let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver {
|
|
||||||
match l.kind() {
|
|
||||||
ast::LiteralKind::FloatNumber { .. } => l.to_string().ends_with('.'),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
ImmediateLocation::FieldAccess {
|
|
||||||
receiver,
|
|
||||||
receiver_is_ambiguous_float_literal,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ast::MethodCallExpr(it) => ImmediateLocation::MethodCall {
|
|
||||||
receiver: find_in_original_file(it.receiver(), original_file),
|
|
||||||
has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
|
|
||||||
},
|
|
||||||
ast::Const(it) => {
|
ast::Const(it) => {
|
||||||
if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) {
|
if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -272,7 +272,7 @@ fn render_resolution_simple_(
|
||||||
|
|
||||||
// Add `<>` for generic types
|
// Add `<>` for generic types
|
||||||
let type_path_no_ty_args = matches!(
|
let type_path_no_ty_args = matches!(
|
||||||
ctx.completion.path_context,
|
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.add_call_parenthesis;
|
) && ctx.completion.config.add_call_parenthesis;
|
||||||
if type_path_no_ty_args {
|
if type_path_no_ty_args {
|
||||||
|
|
|
@ -7,9 +7,8 @@ use stdx::{format_to, to_lower_snake_case};
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{CompletionContext, PathCompletionCtx, PathKind},
|
context::{CompletionContext, DotAccess, NameRefContext, PathCompletionCtx, PathKind},
|
||||||
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
|
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
|
||||||
patterns::ImmediateLocation,
|
|
||||||
render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
|
render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -196,7 +195,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
match ctx.path_context {
|
match ctx.path_context() {
|
||||||
Some(PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }) => {
|
Some(PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }) => {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -208,8 +207,8 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
ctx.completion_location,
|
ctx.nameref_ctx(),
|
||||||
Some(ImmediateLocation::MethodCall { has_parens: true, .. })
|
Some(NameRefContext { dot_access: Some(DotAccess::Method { has_parens: true, .. }), .. })
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ fn render(
|
||||||
let db = completion.db;
|
let db = completion.db;
|
||||||
let kind = thing.kind(db);
|
let kind = thing.kind(db);
|
||||||
let has_call_parens =
|
let has_call_parens =
|
||||||
matches!(completion.path_context, Some(PathCompletionCtx { has_call_parens: true, .. }));
|
matches!(completion.path_context(), Some(PathCompletionCtx { has_call_parens: true, .. }));
|
||||||
|
|
||||||
let fields = thing.fields(completion)?;
|
let fields = thing.fields(completion)?;
|
||||||
let (qualified_name, short_qualified_name, qualified) = match path {
|
let (qualified_name, short_qualified_name, qualified) = match path {
|
||||||
|
|
|
@ -33,8 +33,8 @@ fn render(
|
||||||
let is_fn_like = macro_.is_fn_like(completion.db);
|
let is_fn_like = macro_.is_fn_like(completion.db);
|
||||||
let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
|
let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
|
||||||
|
|
||||||
let needs_bang = match completion.path_context {
|
let needs_bang = match completion.path_context() {
|
||||||
Some(PathCompletionCtx { kind, has_macro_bang, .. }) => {
|
Some(&PathCompletionCtx { kind, has_macro_bang, .. }) => {
|
||||||
is_fn_like && kind != PathKind::Use && !has_macro_bang
|
is_fn_like && kind != PathKind::Use && !has_macro_bang
|
||||||
}
|
}
|
||||||
_ => is_fn_like,
|
_ => is_fn_like,
|
||||||
|
|
|
@ -78,7 +78,7 @@ fn render_pat(
|
||||||
fields_omitted: bool,
|
fields_omitted: bool,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let has_call_parens = matches!(
|
let has_call_parens = matches!(
|
||||||
ctx.completion.path_context,
|
ctx.completion.path_context(),
|
||||||
Some(PathCompletionCtx { has_call_parens: true, .. })
|
Some(PathCompletionCtx { has_call_parens: true, .. })
|
||||||
);
|
);
|
||||||
let mut pat = match kind {
|
let mut pat = match kind {
|
||||||
|
|
|
@ -257,7 +257,6 @@ fn foo() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_completions_in_comments() {
|
fn no_completions_in_comments() {
|
||||||
cov_mark::check!(no_keyword_completion_in_comments);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
completion_list(
|
completion_list(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -579,25 +579,6 @@ fn attr_on_fn() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn attr_on_expr() {
|
|
||||||
cov_mark::check!(no_keyword_completion_in_attr_of_expr);
|
|
||||||
check(
|
|
||||||
r#"fn main() { #[$0] foo() }"#,
|
|
||||||
expect![[r#"
|
|
||||||
at allow(…)
|
|
||||||
at cfg(…)
|
|
||||||
at cfg_attr(…)
|
|
||||||
at deny(…)
|
|
||||||
at forbid(…)
|
|
||||||
at warn(…)
|
|
||||||
kw crate::
|
|
||||||
kw self::
|
|
||||||
kw super::
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attr_in_source_file_end() {
|
fn attr_in_source_file_end() {
|
||||||
check(
|
check(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue