/// Complete dot accesses, i.e. fields or methods.
pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
- let dot_receiver = match &ctx.dot_receiver {
+ let dot_receiver = match ctx.dot_receiver() {
Some(expr) => expr,
_ => return complete_undotted_self(acc, ctx),
};
}
fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
- if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly {
+ if !ctx.config.enable_self_on_the_fly {
+ return;
+ }
+ if !ctx.is_trivial_path || ctx.is_path_disallowed() {
return;
}
ctx.scope.process_all_names(&mut |name, def| {
Some(match import_candidate {
Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
- Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(),
+ Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
None => ctx
.name_ref_syntax
.as_ref()
.map(|name_ref| name_ref.syntax())
.or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
- .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?,
+ .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
})
}
fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
let current_module = ctx.scope.module()?;
- if let Some(dot_receiver) = &ctx.dot_receiver {
+ if let Some(dot_receiver) = ctx.dot_receiver() {
ImportAssets::for_fuzzy_method_call(
current_module,
ctx.sema.type_of_expr(dot_receiver)?,
}
// Suggest .await syntax for types that implement Future trait
- if let Some(receiver) = &ctx.dot_receiver {
+ if let Some(receiver) = ctx.dot_receiver() {
if let Some(ty) = ctx.sema.type_of_expr(receiver) {
if ty.impls_future(ctx.db) {
let mut item = kw_completion("await");
completions::postfix::format_like::add_format_like_completions,
context::CompletionContext,
item::{Builder, CompletionKind},
+ patterns::ImmediateLocation,
CompletionItem, CompletionItemKind, Completions,
};
return;
}
- let dot_receiver = match &ctx.dot_receiver {
- Some(it) => it,
- None => return,
+ let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
+ Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false),
+ Some(ImmediateLocation::FieldAccess {
+ receiver: Some(it),
+ receiver_is_ambiguous_float_literal,
+ }) => (it, *receiver_is_ambiguous_float_literal),
+ _ => return,
};
- let receiver_text =
- get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
+ let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
Some(it) => it,
// The rest of the postfix completions create an expression that moves an argument,
// so it's better to consider references now to avoid breaking the compilation
let dot_receiver = include_references(dot_receiver);
- let receiver_text =
- get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
+ let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal);
match try_enum {
Some(try_enum) => match try_enum {
pub(super) is_expr: bool,
/// Something is typed at the "top" level, in module or impl/trait.
pub(super) is_new_item: bool,
- /// The receiver if this is a field or method access, i.e. writing something.$0
- pub(super) dot_receiver: Option<ast::Expr>,
- pub(super) dot_receiver_is_ambiguous_float_literal: bool,
/// If this is a call (method or function) in particular, i.e. the () are already there.
pub(super) is_call: bool,
/// Like `is_call`, but for tuple patterns.
can_be_stmt: false,
is_expr: false,
is_new_item: false,
- dot_receiver: None,
- dot_receiver_is_ambiguous_float_literal: false,
is_call: false,
is_pattern_call: false,
is_macro_call: false,
)
}
+ pub(crate) fn has_dot_receiver(&self) -> bool {
+ matches!(
+ &self.completion_location,
+ Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver })
+ if receiver.is_some()
+ )
+ }
+
+ pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
+ match &self.completion_location {
+ Some(ImmediateLocation::MethodCall { receiver })
+ | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
+ _ => None,
+ }
+ }
+
pub(crate) fn expects_use_tree(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::Use))
}
matches!(self.completion_location, Some(ImmediateLocation::ItemList))
}
+ // fn expects_value(&self) -> bool {
pub(crate) fn expects_expression(&self) -> bool {
self.is_expr
}
.unwrap_or(false);
self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
}
-
- if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
- // The receiver comes before the point of insertion of the fake
- // ident, so it should have the same range in the non-modified file
- self.dot_receiver = field_expr
- .expr()
- .map(|e| e.syntax().text_range())
- .and_then(|r| find_node_with_range(original_file, r));
- self.dot_receiver_is_ambiguous_float_literal =
- if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
- match l.kind() {
- ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
- _ => false,
- }
- } else {
- false
- };
- }
-
- if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
- // As above
- self.dot_receiver = method_call_expr
- .receiver()
- .map(|e| e.syntax().text_range())
- .and_then(|r| find_node_with_range(original_file, r));
- self.is_call = true;
- }
+ self.is_call |=
+ matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
}
}
ast::{self, LoopBodyOwner},
match_ast, AstNode, Direction, SyntaxElement,
SyntaxKind::*,
- SyntaxNode, SyntaxToken, TextSize, T,
+ SyntaxNode, SyntaxToken, TextRange, TextSize, T,
};
#[cfg(test)]
// Fake file ast node
ModDeclaration(ast::Module),
// Original file ast node
+ MethodCall {
+ receiver: Option<ast::Expr>,
+ },
+ // Original file ast node
+ FieldAccess {
+ receiver: Option<ast::Expr>,
+ receiver_is_ambiguous_float_literal: bool,
+ },
+ // Original file ast node
/// The record expr of the field name we are completing
RecordExpr(ast::RecordExpr),
// Original file ast node
Some(TRAIT) => ImmediateLocation::Trait,
_ => return None,
},
- ast::Module(it) => if it.item_list().is_none() {
+ ast::Module(it) => {
+ if it.item_list().is_none() {
ImmediateLocation::ModDeclaration(it)
} else {
- return None
+ return None;
+ }
},
ast::Attr(it) => ImmediateLocation::Attribute(it),
+ ast::FieldExpr(it) => {
+ let receiver = it
+ .expr()
+ .map(|e| e.syntax().text_range())
+ .and_then(|r| find_node_with_range(original_file, r));
+ let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver {
+ match l.kind() {
+ ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
+ _ => false,
+ }
+ } else {
+ false
+ };
+ ImmediateLocation::FieldAccess {
+ receiver,
+ receiver_is_ambiguous_float_literal,
+ }
+ },
+ ast::MethodCallExpr(it) => ImmediateLocation::MethodCall {
+ receiver: it
+ .receiver()
+ .map(|e| e.syntax().text_range())
+ .and_then(|r| find_node_with_range(original_file, r)),
+ },
_ => return None,
}
};
name_ref.syntax().clone()
}
+fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
+ syntax.covering_element(range).ancestors().find_map(N::cast)
+}
+
pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
// Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
// where we only check the first parent with different text range.
};
let mut params_pats = Vec::new();
- let params_ty = if self.ctx.completion.dot_receiver.is_some() || self.receiver.is_some() {
+ let params_ty = if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() {
self.func.method_params(self.ctx.db()).unwrap_or_default()
} else {
if let Some(s) = ast_params.self_param() {