Skip to content

Commit b184bfa

Browse files
committed
Add completions for patterns
1 parent f312555 commit b184bfa

File tree

6 files changed

+315
-26
lines changed

6 files changed

+315
-26
lines changed

crates/completion/src/completions.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ use hir::{ModPath, ScopeDef, Type};
1919
use crate::{
2020
item::Builder,
2121
render::{
22-
const_::render_const, enum_variant::render_variant, function::render_fn,
23-
macro_::render_macro, render_field, render_resolution, render_tuple_field,
24-
type_alias::render_type_alias, RenderContext,
22+
const_::render_const,
23+
enum_variant::render_variant,
24+
function::render_fn,
25+
macro_::render_macro,
26+
pattern::{render_struct_pat, render_variant_pat},
27+
render_field, render_resolution, render_tuple_field,
28+
type_alias::render_type_alias,
29+
RenderContext,
2530
},
2631
CompletionContext, CompletionItem,
2732
};
@@ -105,6 +110,28 @@ impl Completions {
105110
self.add(item)
106111
}
107112

113+
pub(crate) fn add_variant_pat(
114+
&mut self,
115+
ctx: &CompletionContext,
116+
variant: hir::Variant,
117+
local_name: Option<hir::Name>,
118+
) {
119+
if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name) {
120+
self.add(item);
121+
}
122+
}
123+
124+
pub(crate) fn add_struct_pat(
125+
&mut self,
126+
ctx: &CompletionContext,
127+
strukt: hir::Struct,
128+
local_name: Option<hir::Name>,
129+
) {
130+
if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) {
131+
self.add(item);
132+
}
133+
}
134+
108135
pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
109136
if let Some(item) = render_const(RenderContext::new(ctx), constant) {
110137
self.add(item);

crates/completion/src/completions/pattern.rs

Lines changed: 139 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//! Completes constats and paths in patterns.
22
3+
use hir::StructKind;
4+
35
use crate::{CompletionContext, Completions};
46

5-
/// Completes constats and paths in patterns.
7+
/// Completes constants and paths in patterns.
68
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7-
if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_let_pat_binding) {
9+
if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) {
810
return;
911
}
1012
if ctx.record_pat_syntax.is_some() {
@@ -15,20 +17,25 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
1517
// suggest variants + auto-imports
1618
ctx.scope.process_all_names(&mut |name, res| {
1719
let add_resolution = match &res {
18-
hir::ScopeDef::ModuleDef(def) => {
19-
if ctx.is_irrefutable_let_pat_binding {
20-
matches!(def, hir::ModuleDef::Adt(hir::Adt::Struct(_)))
21-
} else {
22-
matches!(
23-
def,
24-
hir::ModuleDef::Adt(hir::Adt::Enum(..))
25-
| hir::ModuleDef::Adt(hir::Adt::Struct(..))
26-
| hir::ModuleDef::Variant(..)
27-
| hir::ModuleDef::Const(..)
28-
| hir::ModuleDef::Module(..)
29-
)
20+
hir::ScopeDef::ModuleDef(def) => match def {
21+
hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
22+
acc.add_struct_pat(ctx, strukt.clone(), Some(name.clone()));
23+
true
24+
}
25+
hir::ModuleDef::Variant(variant)
26+
if !ctx.is_irrefutable_pat_binding
27+
// render_resolution already does some pattern completion tricks for tuple variants
28+
&& variant.kind(ctx.db) == StructKind::Record =>
29+
{
30+
acc.add_variant_pat(ctx, variant.clone(), Some(name.clone()));
31+
true
3032
}
31-
}
33+
hir::ModuleDef::Adt(hir::Adt::Enum(..))
34+
| hir::ModuleDef::Variant(..)
35+
| hir::ModuleDef::Const(..)
36+
| hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding,
37+
_ => false,
38+
},
3239
hir::ScopeDef::MacroDef(_) => true,
3340
_ => false,
3441
};
@@ -49,6 +56,11 @@ mod tests {
4956
expect.assert_eq(&actual)
5057
}
5158

59+
fn check_snippet(ra_fixture: &str, expect: Expect) {
60+
let actual = completion_list(ra_fixture, CompletionKind::Snippet);
61+
expect.assert_eq(&actual)
62+
}
63+
5264
#[test]
5365
fn completes_enum_variants_and_modules() {
5466
check(
@@ -114,4 +126,116 @@ fn foo() {
114126
"#]],
115127
);
116128
}
129+
130+
#[test]
131+
fn completes_in_param() {
132+
check(
133+
r#"
134+
enum E { X }
135+
136+
static FOO: E = E::X;
137+
struct Bar { f: u32 }
138+
139+
fn foo(<|>) {
140+
}
141+
"#,
142+
expect![[r#"
143+
st Bar
144+
"#]],
145+
);
146+
}
147+
148+
#[test]
149+
fn completes_pat_in_let() {
150+
check_snippet(
151+
r#"
152+
struct Bar { f: u32 }
153+
154+
fn foo() {
155+
let <|>
156+
}
157+
"#,
158+
expect![[r#"
159+
bn Bar Bar { f }$0
160+
"#]],
161+
);
162+
}
163+
164+
#[test]
165+
fn completes_param_pattern() {
166+
check_snippet(
167+
r#"
168+
struct Foo { bar: String, baz: String }
169+
struct Bar(String, String);
170+
struct Baz;
171+
fn outer(<|>) {}
172+
"#,
173+
expect![[r#"
174+
bn Foo Foo { bar, baz }: Foo$0
175+
bn Bar Bar($1, $2): Bar$0
176+
"#]],
177+
)
178+
}
179+
180+
#[test]
181+
fn completes_let_pattern() {
182+
check_snippet(
183+
r#"
184+
struct Foo { bar: String, baz: String }
185+
struct Bar(String, String);
186+
struct Baz;
187+
fn outer() {
188+
let <|>
189+
}
190+
"#,
191+
expect![[r#"
192+
bn Foo Foo { bar, baz }$0
193+
bn Bar Bar($1, $2)$0
194+
"#]],
195+
)
196+
}
197+
198+
#[test]
199+
fn completes_refutable_pattern() {
200+
check_snippet(
201+
r#"
202+
struct Foo { bar: i32, baz: i32 }
203+
struct Bar(String, String);
204+
struct Baz;
205+
fn outer() {
206+
match () {
207+
<|>
208+
}
209+
}
210+
"#,
211+
expect![[r#"
212+
bn Foo Foo { bar, baz }$0
213+
bn Bar Bar($1, $2)$0
214+
"#]],
215+
)
216+
}
217+
218+
#[test]
219+
fn omits_private_fields_pat() {
220+
check_snippet(
221+
r#"
222+
mod foo {
223+
pub struct Foo { pub bar: i32, baz: i32 }
224+
pub struct Bar(pub String, String);
225+
pub struct Invisible(String, String);
226+
}
227+
use foo::*;
228+
229+
fn outer() {
230+
match () {
231+
<|>
232+
}
233+
}
234+
"#,
235+
expect![[r#"
236+
bn Foo Foo { bar, .. }$0
237+
bn Bar Bar($1, ..)$0
238+
"#]],
239+
)
240+
}
117241
}

crates/completion/src/context.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub(crate) struct CompletionContext<'a> {
5151
/// If a name-binding or reference to a const in a pattern.
5252
/// Irrefutable patterns (like let) are excluded.
5353
pub(super) is_pat_binding_or_const: bool,
54-
pub(super) is_irrefutable_let_pat_binding: bool,
54+
pub(super) is_irrefutable_pat_binding: bool,
5555
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
5656
pub(super) is_trivial_path: bool,
5757
/// If not a trivial path, the prefix (qualifier).
@@ -147,7 +147,7 @@ impl<'a> CompletionContext<'a> {
147147
active_parameter: ActiveParameter::at(db, position),
148148
is_param: false,
149149
is_pat_binding_or_const: false,
150-
is_irrefutable_let_pat_binding: false,
150+
is_irrefutable_pat_binding: false,
151151
is_trivial_path: false,
152152
path_qual: None,
153153
after_if: false,
@@ -327,14 +327,19 @@ impl<'a> CompletionContext<'a> {
327327
if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
328328
self.is_pat_binding_or_const = false;
329329
}
330-
if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) {
331-
if let Some(pat) = let_stmt.pat() {
332-
if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range())
333-
{
334-
self.is_pat_binding_or_const = false;
335-
self.is_irrefutable_let_pat_binding = true;
330+
if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| {
331+
match_ast! {
332+
match node {
333+
ast::LetStmt(it) => Some(it.pat()),
334+
ast::Param(it) => Some(it.pat()),
335+
_ => None,
336336
}
337337
}
338+
}) {
339+
if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
340+
self.is_pat_binding_or_const = false;
341+
self.is_irrefutable_pat_binding = true;
342+
}
338343
}
339344
}
340345
if is_node::<ast::Param>(name.syntax()) {

crates/completion/src/render.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub(crate) mod macro_;
55
pub(crate) mod function;
66
pub(crate) mod enum_variant;
77
pub(crate) mod const_;
8+
pub(crate) mod pattern;
89
pub(crate) mod type_alias;
910

1011
mod builder_ext;

0 commit comments

Comments
 (0)