Move path completions for patterns into pattern module

This commit is contained in:
Lukas Wirth 2022-02-02 18:09:30 +01:00
parent 33fd2d7aef
commit 9f5ee155c1
7 changed files with 215 additions and 51 deletions

View File

@ -27,6 +27,9 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
return;
}
if ctx.pattern_ctx.is_some() {
return;
}
let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
@ -117,7 +120,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
add_keyword("else if", "else if $1 {\n $0\n}");
}
if ctx.expects_ident_pat_or_ref_expr() {
if ctx.expects_ident_ref_expr() {
add_keyword("mut", "mut ");
}

View File

@ -1,20 +1,52 @@
//! Completes constants and paths in unqualified patterns.
use hir::db::DefDatabase;
use hir::{db::DefDatabase, AssocItem, ScopeDef};
use rustc_hash::FxHashSet;
use syntax::ast::Pat;
use crate::{
context::{PatternContext, PatternRefutability},
context::{PathCompletionCtx, PathQualifierCtx, PatternRefutability},
CompletionContext, Completions,
};
/// Completes constants and paths in unqualified patterns.
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
let refutable = match ctx.pattern_ctx {
Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => {
refutability == PatternRefutability::Refutable
}
let patctx = match &ctx.pattern_ctx {
Some(ctx) => ctx,
_ => return,
};
let refutable = patctx.refutability == PatternRefutability::Refutable;
if let Some(path_ctx) = &ctx.path_context {
pattern_path_completion(acc, ctx, path_ctx);
return;
}
match patctx.parent_pat.as_ref() {
Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (),
Some(Pat::RefPat(r)) => {
if r.mut_token().is_none() {
acc.add_keyword(ctx, "mut");
}
}
_ => {
let tok = ctx.token.text_range().start();
match (patctx.ref_token.as_ref(), patctx.mut_token.as_ref()) {
(None, None) => {
acc.add_keyword(ctx, "ref");
acc.add_keyword(ctx, "mut");
}
(None, Some(m)) if tok < m.text_range().start() => {
acc.add_keyword(ctx, "ref");
}
(Some(r), None) if tok > r.text_range().end() => {
acc.add_keyword(ctx, "mut");
}
_ => (),
}
}
}
let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
if let Some(hir::Adt::Enum(e)) =
@ -63,3 +95,104 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
}
});
}
fn pattern_path_completion(
acc: &mut Completions,
ctx: &CompletionContext,
PathCompletionCtx { qualifier, is_absolute_path, .. }: &PathCompletionCtx,
) {
match qualifier {
Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
if *is_super_chain {
acc.add_keyword(ctx, "super::");
}
let resolution = match resolution {
Some(it) => it,
None => return,
};
match resolution {
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
let module_scope = module.scope(ctx.db, ctx.module);
for (name, def) in module_scope {
let add_resolution = match def {
ScopeDef::MacroDef(m) if m.is_fn_like() => true,
ScopeDef::ModuleDef(_) => true,
_ => false,
};
if add_resolution {
acc.add_resolution(ctx, name, def);
}
}
}
hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
cov_mark::hit!(enum_plain_qualified_use_tree);
e.variants(ctx.db)
.into_iter()
.for_each(|variant| acc.add_enum_variant(ctx, variant, None));
}
res @ (hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_)) => {
if let Some(krate) = ctx.krate {
let ty = match res {
hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
_ => return,
};
// Note associated consts cannot be referenced in patterns
if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
e.variants(ctx.db)
.into_iter()
.for_each(|variant| acc.add_enum_variant(ctx, variant, None));
}
let traits_in_scope = ctx.scope.visible_traits();
let mut seen = FxHashSet::default();
ty.iterate_path_candidates(
ctx.db,
krate,
&traits_in_scope,
ctx.module,
None,
|_ty, item| {
// We might iterate candidates of a trait multiple times here, so deduplicate
// them.
if let AssocItem::TypeAlias(ta) = item {
if seen.insert(item) {
acc.add_type_alias(ctx, ta);
}
}
None::<()>
},
);
}
}
_ => {}
}
}
// qualifier can only be none here if we are in a TuplePat or RecordPat in which case special characters have to follow the path
// so executing the rest of this completion doesn't make sense
// fresh use tree with leading colon2, only show crate roots
None if *is_absolute_path => {
cov_mark::hit!(use_tree_crate_roots_only);
ctx.process_all_names(&mut |name, res| match res {
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
acc.add_resolution(ctx, name, res);
}
_ => (),
});
}
// only show modules in a fresh UseTree
None => {
cov_mark::hit!(unqualified_path_only_modules_in_import);
ctx.process_all_names(&mut |name, res| {
if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
acc.add_resolution(ctx, name, res);
}
});
["self::", "super::", "crate::"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
}
}
}

View File

@ -15,6 +15,9 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
return;
}
if ctx.pattern_ctx.is_some() {
return;
}
let (qualifier, kind) = match ctx.path_context {
// let ... else, syntax would come in really handy here right now
Some(PathCompletionCtx { qualifier: Some(ref qualifier), kind, .. }) => (qualifier, kind),
@ -60,10 +63,9 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
}
match kind {
Some(PathKind::Attr { .. } | PathKind::Vis { .. } | PathKind::Use) => {
Some(PathKind::Pat | PathKind::Attr { .. } | PathKind::Vis { .. } | PathKind::Use) => {
return;
}
Some(PathKind::Pat) => (),
_ => {
// Add associated types on type parameters and `Self`.
ctx.scope.assoc_type_shorthand_candidates(&resolution, |_, alias| {

View File

@ -17,7 +17,13 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
}
match ctx.path_context {
Some(PathCompletionCtx {
kind: Some(PathKind::Vis { .. } | PathKind::Attr { .. } | PathKind::Use { .. }),
kind:
Some(
PathKind::Vis { .. }
| PathKind::Attr { .. }
| PathKind::Use { .. }
| PathKind::Pat,
),
..
}) => return,
Some(PathCompletionCtx { is_absolute_path: false, qualifier: None, .. }) => (),

View File

@ -85,6 +85,9 @@ pub(super) struct PatternContext {
pub(super) refutability: PatternRefutability,
pub(super) param_ctx: Option<(ast::ParamList, ast::Param, ParamKind)>,
pub(super) has_type_ascription: bool,
pub(super) parent_pat: Option<ast::Pat>,
pub(super) ref_token: Option<SyntaxToken>,
pub(super) mut_token: Option<SyntaxToken>,
}
#[derive(Debug)]
@ -219,11 +222,8 @@ impl<'a> CompletionContext<'a> {
matches!(self.completion_location, Some(ImmediateLocation::StmtList))
}
pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
matches!(
self.completion_location,
Some(ImmediateLocation::IdentPat | ImmediateLocation::RefExpr)
)
pub(crate) fn expects_ident_ref_expr(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
}
pub(crate) fn expect_field(&self) -> bool {
@ -789,9 +789,6 @@ impl<'a> CompletionContext<'a> {
if is_name_in_field_pat {
return None;
}
if !bind_pat.is_simple_ident() {
return None;
}
Some(pattern_context_for(original_file, bind_pat.into()))
}
@ -949,7 +946,18 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
};
(refutability, false)
});
PatternContext { refutability, param_ctx: is_param, has_type_ascription }
let (ref_token, mut_token) = match &pat {
ast::Pat::IdentPat(it) => (it.ref_token(), it.mut_token()),
_ => (None, None),
};
PatternContext {
refutability,
param_ctx: is_param,
has_type_ascription,
parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
mut_token,
ref_token,
}
}
fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {

View File

@ -17,6 +17,7 @@ fn baz(file$0) {}
"#,
expect![[r#"
bn file_id: usize
kw ref
kw mut
"#]],
);
@ -32,6 +33,7 @@ fn baz(foo: (), file$0) {}
"#,
expect![[r#"
bn file_id: usize
kw ref
kw mut
"#]],
);
@ -47,6 +49,7 @@ fn baz(file$0 id: u32) {}
"#,
expect![[r#"
bn file_id: usize,
kw ref
kw mut
"#]],
);
@ -60,6 +63,7 @@ fn foo(file_id: usize) {}
fn bar(file_id: u32, $0) {}
"#,
expect![[r#"
kw ref
kw mut
"#]],
);
@ -76,6 +80,7 @@ pub(crate) trait SourceRoot {
"#,
expect![[r#"
bn file_id: usize
kw ref
kw mut
"#]],
);
@ -91,6 +96,7 @@ fn outer(text: &str) {
"#,
expect![[r#"
bn text: &str
kw ref
kw mut
"#]],
)
@ -106,6 +112,7 @@ fn foo2($0) {}
"#,
expect![[r#"
bn Bar { bar }: Bar
kw ref
kw mut
bn Bar Bar { bar$1 }: Bar$0
st Bar
@ -130,6 +137,7 @@ impl A {
bn mut self
bn &mut self
bn file_id: usize
kw ref
kw mut
sp Self
st A
@ -150,6 +158,7 @@ impl A {
"#,
expect![[r#"
bn file_id: usize
kw ref
kw mut
sp Self
st A
@ -178,6 +187,7 @@ fn outer() {
bn foo: i32
bn baz: i32
bn bar: i32
kw ref
kw mut
"#]],
)
@ -202,6 +212,22 @@ fn outer() {
bn baz: i32
bn bar: i32
bn foo: i32
kw ref
kw mut
"#]],
)
}
#[test]
fn completes_fully_equal() {
check(
r#"
fn foo(bar: u32) {}
fn bar(bar$0) {}
"#,
expect![[r#"
bn bar: u32
kw ref
kw mut
"#]],
)

View File

@ -22,6 +22,7 @@ fn quux() {
}
"#,
expect![[r#"
kw ref
kw mut
"#]],
);
@ -53,16 +54,13 @@ fn quux() {
#[test]
fn ident_ref_mut_pat() {
// FIXME mut is already here, don't complete it again
check_empty(
r#"
fn quux() {
let ref mut en$0
}
"#,
expect![[r#"
kw mut
"#]],
expect![[r#""#]],
);
check_empty(
r#"
@ -70,9 +68,7 @@ fn quux() {
let ref mut en$0 @ x
}
"#,
expect![[r#"
kw mut
"#]],
expect![[r#""#]],
);
}
@ -88,16 +84,13 @@ fn quux() {
kw mut
"#]],
);
// FIXME mut is already here, don't complete it again
check_empty(
r#"
fn quux() {
let &mut en$0
}
"#,
expect![[r#"
kw mut
"#]],
expect![[r#""#]],
);
}
@ -110,6 +103,7 @@ fn foo() {
}
"#,
expect![[r##"
kw ref
kw mut
en Enum
bn Record Record { field$1 }$0
@ -139,6 +133,7 @@ fn foo() {
}
"#,
expect![[r##"
kw ref
kw mut
bn Record Record { field$1 }$0
st Record
@ -160,6 +155,7 @@ fn foo(a$0) {
}
"#,
expect![[r##"
kw ref
kw mut
bn Record Record { field$1 }: Record$0
st Record
@ -175,6 +171,7 @@ fn foo(a$0: Tuple) {
}
"#,
expect![[r##"
kw ref
kw mut
bn Record Record { field$1 }$0
st Record
@ -200,6 +197,7 @@ fn foo() {
}
"#,
expect![[r#"
kw ref
kw mut
ma m!() macro_rules! m
"#]],
@ -218,6 +216,7 @@ fn foo() {
}
"#,
expect![[r#"
kw ref
kw mut
ev E::X ()
en E
@ -242,6 +241,7 @@ fn outer() {
}
"#,
expect![[r#"
kw ref
kw mut
bn Record Record { field$1, .. }$0
st Record
@ -267,6 +267,7 @@ impl Foo {
}
"#,
expect![[r#"
kw ref
kw mut
bn Self Self($1)$0
sp Self
@ -278,7 +279,6 @@ impl Foo {
#[test]
fn enum_qualified() {
// FIXME: Don't show functions, they aren't patterns
check(
r#"
impl Enum {
@ -291,12 +291,9 @@ fn func() {
}
"#,
expect![[r#"
ev TupleV() (u32)
ev RecordV {field: u32}
ev UnitV ()
ct ASSOC_CONST const ASSOC_CONST: ()
fn assoc_fn() fn()
ta AssocType type AssocType = ()
ev TupleV() (u32)
ev RecordV {field: u32}
ev UnitV ()
"#]],
);
}
@ -310,6 +307,7 @@ struct Bar(u32);
fn outer(Foo { bar: $0 }: Foo) {}
"#,
expect![[r#"
kw ref
kw mut
bn Foo Foo { bar$1 }$0
st Foo
@ -340,6 +338,7 @@ struct Bar(u32);
fn foo($0) {}
"#,
expect![[r#"
kw ref
kw mut
bn Foo Foo { bar$1 }: Foo$0
st Foo
@ -360,6 +359,7 @@ fn foo() {
}
"#,
expect![[r#"
kw ref
kw mut
bn Foo Foo { bar$1 }$0
st Foo
@ -368,17 +368,3 @@ fn foo() {
"#]],
)
}
#[test]
fn completes_fully_equal() {
check_empty(
r#"
fn foo(bar: u32) {}
fn bar(bar$0) {}
"#,
expect![[r#"
bn bar: u32
kw mut
"#]],
)
}