mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-24 05:33:41 +00:00
Merge pattern completion related bools into an enum
This commit is contained in:
parent
1caf79c389
commit
7d865ef071
@ -1,17 +1,18 @@
|
|||||||
//! Completes constants and paths in patterns.
|
//! Completes constants and paths in patterns.
|
||||||
|
|
||||||
use crate::{CompletionContext, Completions};
|
use crate::{context::IsPatOrConst, CompletionContext, Completions};
|
||||||
|
|
||||||
/// Completes constants and paths in patterns.
|
/// Completes constants and paths in patterns.
|
||||||
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) {
|
if ctx.is_pat_or_const == IsPatOrConst::No {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ctx.record_pat_syntax.is_some() {
|
if ctx.record_pat_syntax.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.is_irrefutable_pat_binding {
|
let refutable = ctx.is_pat_or_const == IsPatOrConst::Refutable;
|
||||||
|
if refutable {
|
||||||
if let Some(hir::Adt::Enum(e)) =
|
if let Some(hir::Adt::Enum(e)) =
|
||||||
ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
|
ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
|
||||||
{
|
{
|
||||||
@ -31,14 +32,14 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
|||||||
acc.add_struct_pat(ctx, *strukt, Some(name.clone()));
|
acc.add_struct_pat(ctx, *strukt, Some(name.clone()));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => {
|
hir::ModuleDef::Variant(variant) if refutable => {
|
||||||
acc.add_variant_pat(ctx, *variant, Some(name.clone()));
|
acc.add_variant_pat(ctx, *variant, Some(name.clone()));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
hir::ModuleDef::Adt(hir::Adt::Enum(..))
|
hir::ModuleDef::Adt(hir::Adt::Enum(..))
|
||||||
| hir::ModuleDef::Variant(..)
|
| hir::ModuleDef::Variant(..)
|
||||||
| hir::ModuleDef::Const(..)
|
| hir::ModuleDef::Const(..)
|
||||||
| hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding,
|
| hir::ModuleDef::Module(..) => refutable,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
hir::ScopeDef::MacroDef(_) => true,
|
hir::ScopeDef::MacroDef(_) => true,
|
||||||
@ -47,7 +48,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
|||||||
acc.add_struct_pat(ctx, strukt, Some(name.clone()));
|
acc.add_struct_pat(ctx, strukt, Some(name.clone()));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Some(hir::Adt::Enum(_)) => !ctx.is_irrefutable_pat_binding,
|
Some(hir::Adt::Enum(_)) => refutable,
|
||||||
_ => true,
|
_ => true,
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -88,7 +88,7 @@ fn quux(x: Option<Enum>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[""]],
|
expect![[r#""#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ fn quux(x: Option<Enum>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[""]],
|
expect![[r#""#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,13 @@ use crate::{
|
|||||||
CompletionConfig,
|
CompletionConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) enum IsPatOrConst {
|
||||||
|
No,
|
||||||
|
Refutable,
|
||||||
|
Irrefutable,
|
||||||
|
}
|
||||||
|
|
||||||
/// `CompletionContext` is created early during completion to figure out, where
|
/// `CompletionContext` is created early during completion to figure out, where
|
||||||
/// exactly is the cursor, syntax-wise.
|
/// exactly is the cursor, syntax-wise.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -42,23 +49,26 @@ pub(crate) struct CompletionContext<'a> {
|
|||||||
pub(super) expected_name: Option<NameOrNameRef>,
|
pub(super) expected_name: Option<NameOrNameRef>,
|
||||||
pub(super) expected_type: Option<Type>,
|
pub(super) expected_type: Option<Type>,
|
||||||
pub(super) name_ref_syntax: Option<ast::NameRef>,
|
pub(super) name_ref_syntax: Option<ast::NameRef>,
|
||||||
pub(super) lifetime_syntax: Option<ast::Lifetime>,
|
|
||||||
pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
|
|
||||||
pub(super) function_syntax: Option<ast::Fn>,
|
pub(super) function_syntax: Option<ast::Fn>,
|
||||||
pub(super) use_item_syntax: Option<ast::Use>,
|
pub(super) use_item_syntax: Option<ast::Use>,
|
||||||
pub(super) record_lit_syntax: Option<ast::RecordExpr>,
|
pub(super) record_lit_syntax: Option<ast::RecordExpr>,
|
||||||
pub(super) record_pat_syntax: Option<ast::RecordPat>,
|
pub(super) record_pat_syntax: Option<ast::RecordPat>,
|
||||||
pub(super) record_field_syntax: Option<ast::RecordExprField>,
|
pub(super) record_field_syntax: Option<ast::RecordExprField>,
|
||||||
|
/// The parent impl of the cursor position if it exists.
|
||||||
pub(super) impl_def: Option<ast::Impl>,
|
pub(super) impl_def: Option<ast::Impl>,
|
||||||
|
|
||||||
|
// 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) lifetime_allowed: bool,
|
pub(super) lifetime_allowed: bool,
|
||||||
|
pub(super) is_label_ref: bool,
|
||||||
|
|
||||||
|
// potentially set if we are completing a name
|
||||||
|
pub(super) is_pat_or_const: IsPatOrConst,
|
||||||
|
pub(super) is_param: bool,
|
||||||
|
|
||||||
/// FIXME: `ActiveParameter` is string-based, which is very very wrong
|
/// FIXME: `ActiveParameter` is string-based, which is very very wrong
|
||||||
pub(super) active_parameter: Option<ActiveParameter>,
|
pub(super) active_parameter: Option<ActiveParameter>,
|
||||||
pub(super) is_param: bool,
|
|
||||||
pub(super) is_label_ref: bool,
|
|
||||||
/// If a name-binding or reference to a const in a pattern.
|
|
||||||
/// Irrefutable patterns (like let) are excluded.
|
|
||||||
pub(super) is_pat_binding_or_const: bool,
|
|
||||||
pub(super) is_irrefutable_pat_binding: bool,
|
|
||||||
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
|
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
|
||||||
pub(super) is_trivial_path: bool,
|
pub(super) is_trivial_path: bool,
|
||||||
/// If not a trivial path, the prefix (qualifier).
|
/// If not a trivial path, the prefix (qualifier).
|
||||||
@ -103,7 +113,6 @@ pub(crate) struct CompletionContext<'a> {
|
|||||||
|
|
||||||
no_completion_required: bool,
|
no_completion_required: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CompletionContext<'a> {
|
impl<'a> CompletionContext<'a> {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
db: &'a RootDatabase,
|
db: &'a RootDatabase,
|
||||||
@ -160,8 +169,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
active_parameter: ActiveParameter::at(db, position),
|
active_parameter: ActiveParameter::at(db, position),
|
||||||
is_label_ref: false,
|
is_label_ref: false,
|
||||||
is_param: false,
|
is_param: false,
|
||||||
is_pat_binding_or_const: false,
|
is_pat_or_const: IsPatOrConst::No,
|
||||||
is_irrefutable_pat_binding: false,
|
|
||||||
is_trivial_path: false,
|
is_trivial_path: false,
|
||||||
path_qual: None,
|
path_qual: None,
|
||||||
after_if: false,
|
after_if: false,
|
||||||
@ -417,67 +425,19 @@ impl<'a> CompletionContext<'a> {
|
|||||||
self.expected_type = expected_type;
|
self.expected_type = expected_type;
|
||||||
self.expected_name = expected_name;
|
self.expected_name = expected_name;
|
||||||
self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
|
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) {
|
||||||
if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset)
|
Some(it) => it,
|
||||||
{
|
None => return,
|
||||||
self.classify_lifetime(original_file, lifetime, offset);
|
};
|
||||||
}
|
match name_like {
|
||||||
|
ast::NameLike::Lifetime(lifetime) => {
|
||||||
// First, let's try to complete a reference to some declaration.
|
self.classify_lifetime(original_file, lifetime, offset);
|
||||||
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
|
|
||||||
// Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
|
|
||||||
// See RFC#1685.
|
|
||||||
if is_node::<ast::Param>(name_ref.syntax()) {
|
|
||||||
self.is_param = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// FIXME: remove this (V) duplication and make the check more precise
|
ast::NameLike::NameRef(name_ref) => {
|
||||||
if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
|
self.classify_name_ref(original_file, name_ref, offset);
|
||||||
self.record_pat_syntax =
|
|
||||||
self.sema.find_node_at_offset_with_macros(&original_file, offset);
|
|
||||||
}
|
}
|
||||||
self.classify_name_ref(original_file, name_ref, offset);
|
ast::NameLike::Name(name) => {
|
||||||
}
|
self.classify_name(original_file, name, offset);
|
||||||
|
|
||||||
// Otherwise, see if this is a declaration. We can use heuristics to
|
|
||||||
// suggest declaration names, see `CompletionKind::Magic`.
|
|
||||||
if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
|
|
||||||
if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) {
|
|
||||||
self.is_pat_binding_or_const = true;
|
|
||||||
if bind_pat.at_token().is_some()
|
|
||||||
|| bind_pat.ref_token().is_some()
|
|
||||||
|| bind_pat.mut_token().is_some()
|
|
||||||
{
|
|
||||||
self.is_pat_binding_or_const = false;
|
|
||||||
}
|
|
||||||
if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
|
|
||||||
self.is_pat_binding_or_const = false;
|
|
||||||
}
|
|
||||||
if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| {
|
|
||||||
match_ast! {
|
|
||||||
match node {
|
|
||||||
ast::LetStmt(it) => Some(it.pat()),
|
|
||||||
ast::Param(it) => Some(it.pat()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
|
|
||||||
self.is_pat_binding_or_const = false;
|
|
||||||
self.is_irrefutable_pat_binding = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fill_impl_def();
|
|
||||||
}
|
|
||||||
if is_node::<ast::Param>(name.syntax()) {
|
|
||||||
self.is_param = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// FIXME: remove this (^) duplication and make the check more precise
|
|
||||||
if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
|
|
||||||
self.record_pat_syntax =
|
|
||||||
self.sema.find_node_at_offset_with_macros(&original_file, offset);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -511,12 +471,64 @@ impl<'a> CompletionContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) {
|
||||||
|
if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
|
||||||
|
self.is_pat_or_const = IsPatOrConst::Refutable;
|
||||||
|
// if any of these is here our bind pat can't be a const pat anymore
|
||||||
|
let complex_ident_pat = bind_pat.at_token().is_some()
|
||||||
|
|| bind_pat.ref_token().is_some()
|
||||||
|
|| bind_pat.mut_token().is_some();
|
||||||
|
if complex_ident_pat {
|
||||||
|
self.is_pat_or_const = IsPatOrConst::No;
|
||||||
|
} else if let Some(pat_field) =
|
||||||
|
bind_pat.syntax().parent().and_then(ast::RecordPatField::cast)
|
||||||
|
{
|
||||||
|
if pat_field.name_ref().is_none() {
|
||||||
|
self.is_pat_or_const = IsPatOrConst::No;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| {
|
||||||
|
match_ast! {
|
||||||
|
match node {
|
||||||
|
ast::LetStmt(it) => Some(it.pat()),
|
||||||
|
ast::Param(it) => Some(it.pat()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(Some(pat)) = irrefutable_pat {
|
||||||
|
// This check is here since we could be inside a pattern in the initializer expression of the let statement.
|
||||||
|
if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
|
||||||
|
self.is_pat_or_const = IsPatOrConst::Irrefutable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fill_impl_def();
|
||||||
|
}
|
||||||
|
if is_node::<ast::Param>(name.syntax()) {
|
||||||
|
self.is_param = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// FIXME: remove this (V) duplication and make the check more precise
|
||||||
|
if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
|
||||||
|
self.record_pat_syntax =
|
||||||
|
self.sema.find_node_at_offset_with_macros(&original_file, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn classify_name_ref(
|
fn classify_name_ref(
|
||||||
&mut self,
|
&mut self,
|
||||||
original_file: &SyntaxNode,
|
original_file: &SyntaxNode,
|
||||||
name_ref: ast::NameRef,
|
name_ref: ast::NameRef,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
) {
|
) {
|
||||||
|
// FIXME: remove this (^) duplication and make the check more precise
|
||||||
|
if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
|
||||||
|
self.record_pat_syntax =
|
||||||
|
self.sema.find_node_at_offset_with_macros(&original_file, offset);
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
let name_range = name_ref.syntax().text_range();
|
||||||
|
@ -20,6 +20,7 @@ use ide_db::{
|
|||||||
use syntax::TextRange;
|
use syntax::TextRange;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
context::IsPatOrConst,
|
||||||
item::{CompletionRelevanceTypeMatch, ImportEdit},
|
item::{CompletionRelevanceTypeMatch, ImportEdit},
|
||||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance,
|
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance,
|
||||||
};
|
};
|
||||||
@ -188,8 +189,7 @@ impl<'a> Render<'a> {
|
|||||||
return render_fn(self.ctx, import_to_add, Some(local_name), *func);
|
return render_fn(self.ctx, import_to_add, Some(local_name), *func);
|
||||||
}
|
}
|
||||||
ScopeDef::ModuleDef(Variant(_))
|
ScopeDef::ModuleDef(Variant(_))
|
||||||
if self.ctx.completion.is_pat_binding_or_const
|
if self.ctx.completion.is_pat_or_const != IsPatOrConst::No =>
|
||||||
| self.ctx.completion.is_irrefutable_pat_binding =>
|
|
||||||
{
|
{
|
||||||
CompletionItemKind::SymbolKind(SymbolKind::Variant)
|
CompletionItemKind::SymbolKind(SymbolKind::Variant)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user