diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index c1081dbde3..3f9314bbb3 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -541,8 +541,8 @@ pub(super) fn complete_name_ref( NameRefKind::Keyword(item) => { keyword::complete_for_and_where(acc, ctx, item); } - NameRefKind::RecordExpr(record_expr) => { - record::complete_record_expr_fields(acc, ctx, record_expr); + NameRefKind::RecordExpr { dot_prefix, expr } => { + record::complete_record_expr_fields(acc, ctx, expr, dot_prefix); } NameRefKind::Pattern(pattern_ctx) => complete_patterns(acc, ctx, pattern_ctx), } diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 37e042a160..3115971c85 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -33,6 +33,7 @@ pub(crate) use self::derive::complete_derive_path; pub(crate) fn complete_known_attribute_input( acc: &mut Completions, ctx: &CompletionContext, + &colon_prefix: &bool, fake_attribute_under_caret: &ast::Attr, ) -> Option<()> { let attribute = fake_attribute_under_caret; @@ -47,7 +48,9 @@ pub(crate) fn complete_known_attribute_input( match path.text().as_str() { "repr" => repr::complete_repr(acc, ctx, tt), - "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES), + "feature" => { + lint::complete_lint(acc, ctx, colon_prefix, &parse_tt_as_comma_sep_paths(tt)?, FEATURES) + } "allow" | "warn" | "deny" | "forbid" => { let existing_lints = parse_tt_as_comma_sep_paths(tt)?; @@ -60,7 +63,7 @@ pub(crate) fn complete_known_attribute_input( .cloned() .collect(); - lint::complete_lint(acc, ctx, &existing_lints, &lints); + lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints); } "cfg" => cfg::complete_cfg(acc, ctx), _ => (), diff --git a/crates/ide-completion/src/completions/attribute/lint.rs b/crates/ide-completion/src/completions/attribute/lint.rs index 8991d657e8..5c04810318 100644 --- a/crates/ide-completion/src/completions/attribute/lint.rs +++ b/crates/ide-completion/src/completions/attribute/lint.rs @@ -1,16 +1,16 @@ //! Completion for lints use ide_db::{generated::lints::Lint, SymbolKind}; -use syntax::{ast, T}; +use syntax::ast; use crate::{context::CompletionContext, item::CompletionItem, Completions}; pub(super) fn complete_lint( acc: &mut Completions, ctx: &CompletionContext, + is_qualified: bool, existing_lints: &[ast::Path], lints_completions: &[Lint], ) { - let is_qualified = ctx.previous_token_is(T![:]); for &Lint { label, description } in lints_completions { let (qual, name) = { // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead? diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 12c449bf35..9f834e1ca1 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -1,9 +1,6 @@ //! Complete fields in record literals and patterns. use ide_db::SymbolKind; -use syntax::{ - ast::{self, Expr}, - T, -}; +use syntax::ast::{self, Expr}; use crate::{ context::{ExprCtx, PathCompletionCtx, PatternContext, Qualified}, @@ -24,6 +21,7 @@ pub(crate) fn complete_record_expr_fields( acc: &mut Completions, ctx: &CompletionContext, record_expr: &ast::RecordExpr, + &dot_prefix: &bool, ) { let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone())); @@ -45,7 +43,7 @@ pub(crate) fn complete_record_expr_fields( let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); add_default_update(acc, ctx, ty, &missing_fields); - if ctx.previous_token_is(T![.]) { + if dot_prefix { let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), ".."); item.insert_text("."); diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 8c73709f4c..9aee1e8b49 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -259,8 +259,11 @@ pub(super) enum NameRefKind { DotAccess(DotAccess), /// Position where we are only interested in keyword completions Keyword(ast::Item), - /// The record expression this nameref is a field of - RecordExpr(ast::RecordExpr), + /// The record expression this nameref is a field of and whether a dot precedes the completion identifier. + RecordExpr { + dot_prefix: bool, + expr: ast::RecordExpr, + }, Pattern(PatternContext), } @@ -279,6 +282,7 @@ pub(super) enum IdentContext { }, /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)` UnexpandedAttrTT { + colon_prefix: bool, fake_attribute_under_caret: Option, }, } @@ -334,9 +338,6 @@ pub(crate) struct CompletionContext<'a> { /// The expected type of what we are completing. pub(super) expected_type: Option, - // FIXME: This shouldn't exist - pub(super) previous_token: Option, - // We might wanna split these out of CompletionContext pub(super) ident_ctx: IdentContext, pub(super) qualifier_ctx: QualifierCtx, @@ -361,11 +362,6 @@ impl<'a> CompletionContext<'a> { } } - // FIXME: This shouldn't exist - pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool { - self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) - } - pub(crate) fn famous_defs(&self) -> FamousDefs { FamousDefs(&self.sema, self.krate) } @@ -507,9 +503,11 @@ impl<'a> CompletionContext<'a> { module, expected_name: None, expected_type: None, - previous_token: None, // dummy value, will be overwritten - ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None }, + ident_ctx: IdentContext::UnexpandedAttrTT { + fake_attribute_under_caret: None, + colon_prefix: false, + }, qualifier_ctx: Default::default(), locals, }; diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 551fa7fb86..1f691f3baf 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -254,7 +254,9 @@ impl<'a> CompletionContext<'a> { // match foo { $0 } // match foo { ..., pat => $0 } ast::MatchExpr(it) => { - let ty = if self.previous_token_is(T![=>]) { + let on_arrow = previous_non_trivia_token(self.token.clone()).map_or(false, |it| T![=>] == it.kind()); + + let ty = if on_arrow { // match foo { ..., pat => $0 } cov_mark::hit!(expected_type_match_arm_body_without_leading_char); cov_mark::hit!(expected_type_match_arm_body_with_leading_char); @@ -327,9 +329,6 @@ impl<'a> CompletionContext<'a> { return None; } - self.previous_token = - syntax_element.clone().into_token().and_then(previous_non_trivia_token); - (self.expected_type, self.expected_name) = self.expected_type_and_name(); // Overwrite the path kind for derives @@ -368,19 +367,19 @@ impl<'a> CompletionContext<'a> { } 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 token = + syntax::algo::skip_trivia_token(self.token.clone(), Direction::Prev)?; let p = token.parent()?; if p.kind() == SyntaxKind::TOKEN_TREE && p.ancestors().any(|it| it.kind() == SyntaxKind::META) { + let colon_prefix = previous_non_trivia_token(self.token.clone()) + .map_or(false, |it| T![:] == it.kind()); self.ident_ctx = IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: syntax_element .ancestors() .find_map(ast::Attr::cast), + colon_prefix, }; } else { return None; @@ -493,12 +492,15 @@ impl<'a> CompletionContext<'a> { |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default()); if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) { + let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone()) + .map_or(false, |it| T![.] == it.kind()); + return find_node_in_file_compensated( sema, original_file, &record_field.parent_record_lit(), ) - .map(NameRefKind::RecordExpr) + .map(|expr| NameRefKind::RecordExpr { expr, dot_prefix }) .map(make_res); } if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) { @@ -1180,8 +1182,12 @@ pub(crate) fn is_in_loop_body(node: &SyntaxNode) -> bool { .is_some() } -fn previous_non_trivia_token(token: SyntaxToken) -> Option { - let mut token = token.prev_token(); +fn previous_non_trivia_token(e: impl Into) -> Option { + let mut token = match e.into() { + SyntaxElement::Node(n) => n.first_token()?, + SyntaxElement::Token(t) => t, + } + .prev_token(); while let Some(inner) = token { if !inner.kind().is_trivia() { return Some(inner); diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 90e2628439..bf75dcf26b 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -181,8 +181,16 @@ pub fn completions( completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); } - IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: Some(attr) } => { - completions::attribute::complete_known_attribute_input(acc, ctx, attr); + IdentContext::UnexpandedAttrTT { + colon_prefix, + fake_attribute_under_caret: Some(attr), + } => { + completions::attribute::complete_known_attribute_input( + acc, + ctx, + colon_prefix, + attr, + ); } IdentContext::UnexpandedAttrTT { .. } | IdentContext::String { .. } => (), }