mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Merge the different identifier contexts into one enum
This commit is contained in:
parent
99fa37d6e3
commit
44c3cc100b
@ -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,
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
|
|
||||||
/// 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_access, dot_receiver) = match &ctx.nameref_ctx {
|
let (dot_access, dot_receiver) = match ctx.nameref_ctx() {
|
||||||
Some(NameRefContext {
|
Some(NameRefContext {
|
||||||
dot_access:
|
dot_access:
|
||||||
Some(
|
Some(
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -17,7 +17,7 @@ use crate::{
|
|||||||
|
|
||||||
/// 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, lifetime) = match &ctx.lifetime_ctx {
|
let (lp, lifetime) = match ctx.lifetime_ctx() {
|
||||||
Some(LifetimeContext { kind: LifetimeKind::Lifetime, lifetime }) => (None, lifetime),
|
Some(LifetimeContext { kind: LifetimeKind::Lifetime, lifetime }) => (None, lifetime),
|
||||||
Some(LifetimeContext {
|
Some(LifetimeContext {
|
||||||
kind: LifetimeKind::LifetimeParam { is_decl: false, param },
|
kind: LifetimeKind::LifetimeParam { is_decl: false, param },
|
||||||
@ -49,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 { kind: LifetimeKind::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| {
|
||||||
|
@ -16,7 +16,7 @@ use crate::{
|
|||||||
|
|
||||||
/// Complete mod declaration, i.e. `mod ;`
|
/// Complete mod declaration, i.e. `mod ;`
|
||||||
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 { kind: NameKind::Module(mod_under_caret), .. })
|
Some(NameContext { kind: NameKind::Module(mod_under_caret), .. })
|
||||||
if mod_under_caret.item_list().is_none() =>
|
if mod_under_caret.item_list().is_none() =>
|
||||||
{
|
{
|
||||||
|
@ -23,7 +23,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.nameref_ctx {
|
let (dot_receiver, receiver_is_ambiguous_float_literal) = match ctx.nameref_ctx() {
|
||||||
Some(NameRefContext {
|
Some(NameRefContext {
|
||||||
dot_access: Some(DotAccess::Method { receiver: Some(it), .. }),
|
dot_access: Some(DotAccess::Method { receiver: Some(it), .. }),
|
||||||
..
|
..
|
||||||
|
@ -11,7 +11,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
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, name_ref) = match &ctx.nameref_ctx {
|
let (&is_absolute_path, qualifier, name_ref) = match ctx.nameref_ctx() {
|
||||||
Some(NameRefContext {
|
Some(NameRefContext {
|
||||||
path_ctx:
|
path_ctx:
|
||||||
Some(PathCompletionCtx { kind: PathKind::Use, is_absolute_path, qualifier, .. }),
|
Some(PathCompletionCtx { kind: PathKind::Use, is_absolute_path, qualifier, .. }),
|
||||||
|
@ -15,7 +15,7 @@ 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, HasArgList, 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,
|
||||||
};
|
};
|
||||||
@ -169,6 +169,21 @@ pub(super) struct NameRefContext {
|
|||||||
pub(super) path_ctx: Option<PathCompletionCtx>,
|
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)]
|
#[derive(Debug)]
|
||||||
pub(super) enum DotAccess {
|
pub(super) enum DotAccess {
|
||||||
Field {
|
Field {
|
||||||
@ -223,12 +238,9 @@ pub(crate) struct CompletionContext<'a> {
|
|||||||
|
|
||||||
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) nameref_ctx: Option<NameRefContext>,
|
|
||||||
|
|
||||||
pub(super) pattern_ctx: Option<PatternContext>,
|
pub(super) pattern_ctx: Option<PatternContext>,
|
||||||
|
|
||||||
@ -262,8 +274,29 @@ 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.nameref_ctx {
|
match self.nameref_ctx() {
|
||||||
Some(NameRefContext {
|
Some(NameRefContext {
|
||||||
dot_access:
|
dot_access:
|
||||||
Some(DotAccess::Method { receiver, .. } | DotAccess::Field { receiver, .. }),
|
Some(DotAccess::Method { receiver, .. } | DotAccess::Field { receiver, .. }),
|
||||||
@ -282,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 { kind: NameKind::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 {
|
||||||
@ -307,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 { kind: NameKind::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.
|
||||||
@ -345,13 +378,13 @@ impl<'a> CompletionContext<'a> {
|
|||||||
Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_))
|
Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_))
|
||||||
)
|
)
|
||||||
|| matches!(
|
|| matches!(
|
||||||
self.name_ctx,
|
self.name_ctx(),
|
||||||
Some(NameContext { kind: NameKind::Module(_) | NameKind::Rename, .. })
|
Some(NameContext { kind: NameKind::Module(_) | NameKind::Rename, .. })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
|
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
|
||||||
self.nameref_ctx.as_ref().and_then(|ctx| ctx.path_ctx.as_ref())
|
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 {
|
||||||
@ -501,7 +534,9 @@ impl<'a> CompletionContext<'a> {
|
|||||||
file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
|
file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
|
||||||
|
|
||||||
let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
|
let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
|
||||||
|
dbg!(&original_token);
|
||||||
let token = sema.descend_into_macros_single(original_token.clone());
|
let token = sema.descend_into_macros_single(original_token.clone());
|
||||||
|
dbg!(&token);
|
||||||
let scope = sema.scope_at_offset(&token.parent()?, offset)?;
|
let scope = sema.scope_at_offset(&token.parent()?, offset)?;
|
||||||
let krate = scope.krate();
|
let krate = scope.krate();
|
||||||
let module = scope.module();
|
let module = scope.module();
|
||||||
@ -530,11 +565,9 @@ impl<'a> CompletionContext<'a> {
|
|||||||
incomplete_let: false,
|
incomplete_let: false,
|
||||||
completion_location: None,
|
completion_location: None,
|
||||||
prev_sibling: None,
|
prev_sibling: None,
|
||||||
fake_attribute_under_caret: None,
|
|
||||||
previous_token: None,
|
previous_token: None,
|
||||||
name_ctx: None,
|
// dummy value, will be overwritten
|
||||||
lifetime_ctx: None,
|
ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
|
||||||
nameref_ctx: None,
|
|
||||||
pattern_ctx: None,
|
pattern_ctx: None,
|
||||||
existing_derives: Default::default(),
|
existing_derives: Default::default(),
|
||||||
locals,
|
locals,
|
||||||
@ -544,7 +577,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
file_with_fake_ident.syntax().clone(),
|
file_with_fake_ident.syntax().clone(),
|
||||||
offset,
|
offset,
|
||||||
fake_ident_token,
|
fake_ident_token,
|
||||||
);
|
)?;
|
||||||
Some(ctx)
|
Some(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,7 +590,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;
|
||||||
|
|
||||||
@ -687,7 +720,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.
|
||||||
@ -835,7 +868,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()) {
|
||||||
@ -844,11 +877,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| {
|
||||||
@ -870,21 +902,49 @@ 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)
|
||||||
{
|
{
|
||||||
if let Some(parent) = name_ref.syntax().parent() {
|
let parent = name_ref.syntax().parent()?;
|
||||||
let (mut nameref_ctx, _) =
|
let (mut nameref_ctx, _) =
|
||||||
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
|
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
|
||||||
if let Some(path_ctx) = &mut nameref_ctx.path_ctx {
|
if let Some(path_ctx) = &mut nameref_ctx.path_ctx {
|
||||||
path_ctx.kind = PathKind::Derive;
|
path_ctx.kind = PathKind::Derive;
|
||||||
}
|
|
||||||
self.nameref_ctx = Some(nameref_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);
|
||||||
@ -902,25 +962,26 @@ 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(parent) = name_ref.syntax().parent() {
|
let parent = name_ref.syntax().parent()?;
|
||||||
let (nameref_ctx, pat_ctx) =
|
let (nameref_ctx, pat_ctx) =
|
||||||
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
|
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
|
||||||
self.nameref_ctx = Some(nameref_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(
|
||||||
|
@ -207,7 +207,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
ctx.nameref_ctx,
|
ctx.nameref_ctx(),
|
||||||
Some(NameRefContext { dot_access: Some(DotAccess::Method { has_parens: true, .. }), .. })
|
Some(NameRefContext { dot_access: Some(DotAccess::Method { has_parens: true, .. }), .. })
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user