Merge #9110
9110: internal: simplify r=Veykril a=Veykril bors r+ Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
9d7343719d
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
/// 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_receiver = match ctx.dot_receiver() {
|
||||||
Some(expr) => expr,
|
Some(expr) => expr,
|
||||||
_ => return complete_undotted_self(acc, ctx),
|
_ => return complete_undotted_self(acc, ctx),
|
||||||
};
|
};
|
||||||
@ -30,7 +30,10 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
ctx.scope.process_all_names(&mut |name, def| {
|
ctx.scope.process_all_names(&mut |name, def| {
|
||||||
|
@ -162,19 +162,19 @@ pub(crate) fn position_for_import<'a>(
|
|||||||
Some(match import_candidate {
|
Some(match import_candidate {
|
||||||
Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
|
Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
|
||||||
Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.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
|
None => ctx
|
||||||
.name_ref_syntax
|
.name_ref_syntax
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|name_ref| name_ref.syntax())
|
.map(|name_ref| name_ref.syntax())
|
||||||
.or_else(|| ctx.path_qual.as_ref().map(|path| path.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> {
|
fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
|
||||||
let current_module = ctx.scope.module()?;
|
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(
|
ImportAssets::for_fuzzy_method_call(
|
||||||
current_module,
|
current_module,
|
||||||
ctx.sema.type_of_expr(dot_receiver)?,
|
ctx.sema.type_of_expr(dot_receiver)?,
|
||||||
|
@ -31,7 +31,7 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suggest .await syntax for types that implement Future trait
|
// 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 let Some(ty) = ctx.sema.type_of_expr(receiver) {
|
||||||
if ty.impls_future(ctx.db) {
|
if ty.impls_future(ctx.db) {
|
||||||
let mut item = kw_completion("await");
|
let mut item = kw_completion("await");
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// Ideally this should be removed and moved into `(un)qualified_path` respectively
|
// Ideally this should be removed and moved into `(un)qualified_path` respectively
|
||||||
pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
// Show only macros in top level.
|
// Show only macros in top level.
|
||||||
if !ctx.is_new_item {
|
if !ctx.expects_item() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
completions::postfix::format_like::add_format_like_completions,
|
completions::postfix::format_like::add_format_like_completions,
|
||||||
context::CompletionContext,
|
context::CompletionContext,
|
||||||
item::{Builder, CompletionKind},
|
item::{Builder, CompletionKind},
|
||||||
|
patterns::ImmediateLocation,
|
||||||
CompletionItem, CompletionItemKind, Completions,
|
CompletionItem, CompletionItemKind, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,13 +23,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dot_receiver = match &ctx.dot_receiver {
|
let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
|
||||||
Some(it) => it,
|
Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false),
|
||||||
None => return,
|
Some(ImmediateLocation::FieldAccess {
|
||||||
|
receiver: Some(it),
|
||||||
|
receiver_is_ambiguous_float_literal,
|
||||||
|
}) => (it, *receiver_is_ambiguous_float_literal),
|
||||||
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let receiver_text =
|
let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
|
||||||
get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
|
|
||||||
|
|
||||||
let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
|
let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
@ -123,8 +127,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
|||||||
// The rest of the postfix completions create an expression that moves an argument,
|
// 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
|
// so it's better to consider references now to avoid breaking the compilation
|
||||||
let dot_receiver = include_references(dot_receiver);
|
let dot_receiver = include_references(dot_receiver);
|
||||||
let receiver_text =
|
let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal);
|
||||||
get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
|
|
||||||
|
|
||||||
match try_enum {
|
match try_enum {
|
||||||
Some(try_enum) => match try_enum {
|
Some(try_enum) => match try_enum {
|
||||||
|
@ -29,7 +29,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) {
|
||||||
if !ctx.is_new_item {
|
if !ctx.expects_item() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let cap = match ctx.config.snippet_cap {
|
let cap = match ctx.config.snippet_cap {
|
||||||
|
@ -78,11 +78,6 @@ pub(crate) struct CompletionContext<'a> {
|
|||||||
pub(super) can_be_stmt: bool,
|
pub(super) can_be_stmt: bool,
|
||||||
/// `true` if we expect an expression at the cursor position.
|
/// `true` if we expect an expression at the cursor position.
|
||||||
pub(super) is_expr: bool,
|
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.
|
/// If this is a call (method or function) in particular, i.e. the () are already there.
|
||||||
pub(super) is_call: bool,
|
pub(super) is_call: bool,
|
||||||
/// Like `is_call`, but for tuple patterns.
|
/// Like `is_call`, but for tuple patterns.
|
||||||
@ -158,9 +153,6 @@ pub(super) fn new(
|
|||||||
path_qual: None,
|
path_qual: None,
|
||||||
can_be_stmt: false,
|
can_be_stmt: false,
|
||||||
is_expr: false,
|
is_expr: false,
|
||||||
is_new_item: false,
|
|
||||||
dot_receiver: None,
|
|
||||||
dot_receiver_is_ambiguous_float_literal: false,
|
|
||||||
is_call: false,
|
is_call: false,
|
||||||
is_pattern_call: false,
|
is_pattern_call: false,
|
||||||
is_macro_call: false,
|
is_macro_call: false,
|
||||||
@ -255,6 +247,22 @@ pub(crate) fn expects_assoc_item(&self) -> bool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub(crate) fn expects_use_tree(&self) -> bool {
|
||||||
matches!(self.completion_location, Some(ImmediateLocation::Use))
|
matches!(self.completion_location, Some(ImmediateLocation::Use))
|
||||||
}
|
}
|
||||||
@ -267,6 +275,7 @@ pub(crate) fn expects_item(&self) -> bool {
|
|||||||
matches!(self.completion_location, Some(ImmediateLocation::ItemList))
|
matches!(self.completion_location, Some(ImmediateLocation::ItemList))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fn expects_value(&self) -> bool {
|
||||||
pub(crate) fn expects_expression(&self) -> bool {
|
pub(crate) fn expects_expression(&self) -> bool {
|
||||||
self.is_expr
|
self.is_expr
|
||||||
}
|
}
|
||||||
@ -540,16 +549,7 @@ fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameR
|
|||||||
self.name_ref_syntax =
|
self.name_ref_syntax =
|
||||||
find_node_at_offset(original_file, name_ref.syntax().text_range().start());
|
find_node_at_offset(original_file, name_ref.syntax().text_range().start());
|
||||||
|
|
||||||
let name_range = name_ref.syntax().text_range();
|
if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
|
||||||
let top_node = name_ref
|
|
||||||
.syntax()
|
|
||||||
.ancestors()
|
|
||||||
.take_while(|it| it.text_range() == name_range)
|
|
||||||
.last()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) {
|
|
||||||
self.is_new_item = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,33 +623,8 @@ fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameR
|
|||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
|
self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
|
||||||
}
|
}
|
||||||
|
self.is_call |=
|
||||||
if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
|
matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,13 +7,13 @@
|
|||||||
ast::{self, LoopBodyOwner},
|
ast::{self, LoopBodyOwner},
|
||||||
match_ast, AstNode, Direction, SyntaxElement,
|
match_ast, AstNode, Direction, SyntaxElement,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxNode, SyntaxToken, TextSize, T,
|
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
|
use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
|
||||||
|
|
||||||
/// Direct parent container of the cursor position
|
/// Immediate previous node to what we are completing.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum ImmediatePrevSibling {
|
pub(crate) enum ImmediatePrevSibling {
|
||||||
IfExpr,
|
IfExpr,
|
||||||
@ -21,7 +21,7 @@ pub(crate) enum ImmediatePrevSibling {
|
|||||||
ImplDefType,
|
ImplDefType,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Direct parent container of the cursor position
|
/// Direct parent "thing" of what we are currently completing.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum ImmediateLocation {
|
pub(crate) enum ImmediateLocation {
|
||||||
Use,
|
Use,
|
||||||
@ -37,6 +37,15 @@ pub(crate) enum ImmediateLocation {
|
|||||||
// Fake file ast node
|
// Fake file ast node
|
||||||
ModDeclaration(ast::Module),
|
ModDeclaration(ast::Module),
|
||||||
// Original file ast node
|
// 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
|
/// The record expr of the field name we are completing
|
||||||
RecordExpr(ast::RecordExpr),
|
RecordExpr(ast::RecordExpr),
|
||||||
// Original file ast node
|
// Original file ast node
|
||||||
@ -164,12 +173,38 @@ pub(crate) fn determine_location(
|
|||||||
Some(TRAIT) => ImmediateLocation::Trait,
|
Some(TRAIT) => ImmediateLocation::Trait,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
ast::Module(it) => if it.item_list().is_none() {
|
ast::Module(it) => {
|
||||||
|
if it.item_list().is_none() {
|
||||||
ImmediateLocation::ModDeclaration(it)
|
ImmediateLocation::ModDeclaration(it)
|
||||||
} else {
|
} else {
|
||||||
return None
|
return None;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ast::Attr(it) => ImmediateLocation::Attribute(it),
|
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,
|
_ => return None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -194,6 +229,10 @@ fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
|
|||||||
name_ref.syntax().clone()
|
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 {
|
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`,
|
// 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.
|
// where we only check the first parent with different text range.
|
||||||
|
@ -154,7 +154,7 @@ fn params(&self) -> Params {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut params_pats = Vec::new();
|
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()
|
self.func.method_params(self.ctx.db()).unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
if let Some(s) = ast_params.self_param() {
|
if let Some(s) = ast_params.self_param() {
|
||||||
|
Loading…
Reference in New Issue
Block a user