use crate::{
patterns::{
- for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent,
- has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent,
- has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token,
+ determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block,
+ is_in_loop_body, previous_token, ImmediateLocation, ImmediatePrevSibling,
},
CompletionConfig,
};
Irrefutable,
}
-/// Direct parent container of the cursor position
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub(crate) enum ImmediateLocation {
- Impl,
- Trait,
- RecordFieldList,
- RefPatOrExpr,
- IdentPat,
- BlockExpr,
- ItemList,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub(crate) enum PrevSibling {
- Trait,
- Impl,
-}
-
/// `CompletionContext` is created early during completion to figure out, where
/// exactly is the cursor, syntax-wise.
#[derive(Debug)]
/// The parent impl of the cursor position if it exists.
pub(super) impl_def: Option<ast::Impl>,
- /// RecordExpr the token is a field of
- pub(super) record_lit_syntax: Option<ast::RecordExpr>,
- /// RecordPat the token is a field of
- pub(super) record_pat_syntax: Option<ast::RecordPat>,
-
// potentially set if we are completing a lifetime
pub(super) lifetime_syntax: Option<ast::Lifetime>,
pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
pub(super) is_param: bool,
pub(super) completion_location: Option<ImmediateLocation>,
+ pub(super) prev_sibling: Option<ImmediatePrevSibling>,
+ pub(super) attribute_under_caret: Option<ast::Attr>,
/// FIXME: `ActiveParameter` is string-based, which is very very wrong
pub(super) active_parameter: Option<ActiveParameter>,
pub(super) is_trivial_path: bool,
/// If not a trivial path, the prefix (qualifier).
pub(super) path_qual: Option<ast::Path>,
- pub(super) after_if: bool,
/// `true` if we are a statement or a last expr in the block.
pub(super) can_be_stmt: bool,
/// `true` if we expect an expression at the cursor position.
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.
pub(super) is_macro_call: bool,
pub(super) is_path_type: bool,
pub(super) has_type_args: bool,
- pub(super) attribute_under_caret: Option<ast::Attr>,
- pub(super) mod_declaration_under_caret: Option<ast::Module>,
pub(super) locals: Vec<(String, Local)>,
- // keyword patterns
pub(super) previous_token: Option<SyntaxToken>,
- pub(super) prev_sibling: Option<PrevSibling>,
pub(super) in_loop_body: bool,
- pub(super) is_match_arm: bool,
pub(super) incomplete_let: bool,
no_completion_required: bool,
lifetime_param_syntax: None,
function_def: None,
use_item_syntax: None,
- record_lit_syntax: None,
- record_pat_syntax: None,
impl_def: None,
active_parameter: ActiveParameter::at(db, position),
is_label_ref: false,
is_pat_or_const: None,
is_trivial_path: false,
path_qual: None,
- after_if: false,
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,
is_path_type: false,
has_type_args: false,
- attribute_under_caret: None,
- mod_declaration_under_caret: None,
previous_token: None,
in_loop_body: false,
completion_location: None,
prev_sibling: None,
- is_match_arm: false,
no_completion_required: false,
incomplete_let: false,
+ attribute_under_caret: None,
locals,
};
break;
}
}
- ctx.fill_keyword_patterns(&speculative_file, offset);
ctx.fill(&original_file, speculative_file, offset);
Some(ctx)
}
)
}
+ 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))
+ }
+
pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::Impl))
}
matches!(self.completion_location, Some(ImmediateLocation::ItemList))
}
+ // fn expects_value(&self) -> bool {
+ pub(crate) fn expects_expression(&self) -> bool {
+ self.is_expr
+ }
+
pub(crate) fn has_block_expr_parent(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
}
- pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool {
+ pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
matches!(
self.completion_location,
- Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr)
+ Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
)
}
pub(crate) fn expect_record_field(&self) -> bool {
- matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList))
+ matches!(self.completion_location, Some(ImmediateLocation::RecordField))
}
pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
- self.prev_sibling.is_some()
+ matches!(
+ self.prev_sibling,
+ Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName)
+ )
}
- pub(crate) fn is_path_disallowed(&self) -> bool {
- self.record_lit_syntax.is_some()
- || self.record_pat_syntax.is_some()
- || self.attribute_under_caret.is_some()
- || self.mod_declaration_under_caret.is_some()
+ pub(crate) fn after_if(&self) -> bool {
+ matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
}
- fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
- dbg!(file_with_fake_ident);
- let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
- let syntax_element = NodeOrToken::Token(fake_ident_token);
- self.previous_token = previous_token(syntax_element.clone());
- self.in_loop_body = is_in_loop_body(syntax_element.clone());
- self.is_match_arm = is_match_arm(syntax_element.clone());
- if has_prev_sibling(syntax_element.clone(), IMPL) {
- self.prev_sibling = Some(PrevSibling::Impl)
- } else if has_prev_sibling(syntax_element.clone(), TRAIT) {
- self.prev_sibling = Some(PrevSibling::Trait)
- }
-
- if has_block_expr_parent(syntax_element.clone()) {
- self.completion_location = Some(ImmediateLocation::BlockExpr);
- } else if has_bind_pat_parent(syntax_element.clone()) {
- self.completion_location = Some(ImmediateLocation::IdentPat);
- } else if has_ref_parent(syntax_element.clone()) {
- self.completion_location = Some(ImmediateLocation::RefPatOrExpr);
- } else if has_impl_parent(syntax_element.clone()) {
- self.completion_location = Some(ImmediateLocation::Impl);
- } else if has_field_list_parent(syntax_element.clone()) {
- self.completion_location = Some(ImmediateLocation::RecordFieldList);
- } else if has_trait_parent(syntax_element.clone()) {
- self.completion_location = Some(ImmediateLocation::Trait);
- } else if has_item_list_or_source_file_parent(syntax_element.clone()) {
- self.completion_location = Some(ImmediateLocation::ItemList);
- }
-
- self.mod_declaration_under_caret =
- find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
- .filter(|module| module.item_list().is_none());
- self.incomplete_let =
- syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
- it.syntax().text_range().end() == syntax_element.text_range().end()
- });
-
- let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
- let fn_is_prev = self.previous_token_is(T![fn]);
- let for_is_prev2 = for_is_prev2(syntax_element.clone());
- self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
+ pub(crate) fn is_path_disallowed(&self) -> bool {
+ matches!(
+ self.completion_location,
+ Some(ImmediateLocation::Attribute(_))
+ | Some(ImmediateLocation::ModDeclaration(_))
+ | Some(ImmediateLocation::RecordPat(_))
+ | Some(ImmediateLocation::RecordExpr(_))
+ ) || self.attribute_under_caret.is_some()
}
fn fill_impl_def(&mut self) {
file_with_fake_ident: SyntaxNode,
offset: TextSize,
) {
+ let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
+ let syntax_element = NodeOrToken::Token(fake_ident_token);
+ self.previous_token = previous_token(syntax_element.clone());
+ self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
+ self.no_completion_required = {
+ let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
+ let fn_is_prev = self.previous_token_is(T![fn]);
+ let for_is_prev2 = for_is_prev2(syntax_element.clone());
+ (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
+ };
+ self.in_loop_body = is_in_loop_body(syntax_element.clone());
+
+ self.incomplete_let =
+ syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
+ it.syntax().text_range().end() == syntax_element.text_range().end()
+ });
+
let (expected_type, expected_name) = self.expected_type_and_name();
self.expected_type = expected_type;
self.expected_name = expected_name;
- self.attribute_under_caret = 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,
None => return,
};
+ self.completion_location =
+ determine_location(&self.sema, original_file, offset, &name_like);
+ self.prev_sibling = determine_prev_sibling(&name_like);
match name_like {
ast::NameLike::Lifetime(lifetime) => {
self.classify_lifetime(original_file, lifetime, offset);
}
ast::NameLike::NameRef(name_ref) => {
- self.classify_name_ref(original_file, name_ref, offset);
+ self.classify_name_ref(original_file, name_ref);
}
ast::NameLike::Name(name) => {
- self.classify_name(original_file, name, offset);
+ self.classify_name(name);
}
}
}
}
}
- fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) {
+ fn classify_name(&mut self, name: ast::Name) {
if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
self.is_pat_or_const = Some(PatternRefutability::Refutable);
// if any of these is here our bind pat can't be a const pat anymore
self.fill_impl_def();
}
+
self.is_param |= is_node::<ast::Param>(name.syntax());
- if ast::RecordPatField::for_field_name(&name).is_some() {
- self.record_pat_syntax =
- self.sema.find_node_at_offset_with_macros(&original_file, offset);
- }
}
- fn classify_name_ref(
- &mut self,
- original_file: &SyntaxNode,
- name_ref: ast::NameRef,
- offset: TextSize,
- ) {
+ fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) {
self.fill_impl_def();
- if ast::RecordExprField::for_field_name(&name_ref).is_some() {
- self.record_lit_syntax =
- self.sema.find_node_at_offset_with_macros(original_file, offset);
- }
- if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
- self.record_pat_syntax =
- self.sema.find_node_at_offset_with_macros(&original_file, offset);
- }
self.name_ref_syntax =
find_node_at_offset(original_file, name_ref.syntax().text_range().start());
- let name_range = name_ref.syntax().text_range();
- 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;
+ if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
return;
}
None => return,
};
- if let Some(segment) = ast::PathSegment::cast(parent.clone()) {
+ if let Some(segment) = ast::PathSegment::cast(parent) {
let path = segment.parent_path();
self.is_call = path
.syntax()
})
.unwrap_or(false);
self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
-
- if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
- if let Some(if_expr) =
- self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
- {
- if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
- {
- self.after_if = true;
- }
- }
- }
- }
-
- 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 { .. }));
}
}