Skip to content

Commit 4a2f60c

Browse files
bors[bot]Veykril
andauthored
Merge #6964
6964: Add full pattern completions for Struct and Variant patterns r=matklad a=Veykril Just gonna call it full pattern completion as pattern completion is already implemented in a sense by showing idents in pattern position. What this does is basically complete struct and variant patterns where applicable(function params, let statements and refutable pattern locations). This does not replace just completing the corresponding idents of the structs and variants, instead two completions are shown for these, a completion for the ident itself and a completion for the pattern(if the pattern make sense to be used that is). I figured in some cases one would rather type out the pattern manually if it has a lot of fields but you only care about one since this completion would cause one more work in the end since you would have to delete all the extra matched fields again. These completions are tagged as `CompletionKind::Snippet`, not sure if that is the right one here. <details> <summary>some gifs</summary> ![dx2lxgzhj3](https://user-images.githubusercontent.com/3757771/102719967-6987ef80-42f1-11eb-8ae0-8aff53777860.gif) ![EP2E7sJLkB](https://user-images.githubusercontent.com/3757771/102785777-c7264580-439e-11eb-8a64-f142e19fb65b.gif) ![JMNHHWknr9](https://user-images.githubusercontent.com/3757771/102785796-d1e0da80-439e-11eb-934b-218ada31b51c.gif) </details> Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
2 parents 94f661c + 83121ef commit 4a2f60c

File tree

8 files changed

+363
-74
lines changed

8 files changed

+363
-74
lines changed

crates/completion/src/completions.rs

+30-3
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

+161-17
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
33
use crate::{CompletionContext, Completions};
44

5-
/// Completes constats and paths in patterns.
5+
/// Completes constants and paths in patterns.
66
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7-
if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_let_pat_binding) {
7+
if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) {
88
return;
99
}
1010
if ctx.record_pat_syntax.is_some() {
@@ -15,20 +15,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
1515
// suggest variants + auto-imports
1616
ctx.scope.process_all_names(&mut |name, res| {
1717
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-
)
18+
hir::ScopeDef::ModuleDef(def) => match def {
19+
hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
20+
acc.add_struct_pat(ctx, strukt.clone(), Some(name.clone()));
21+
true
3022
}
31-
}
23+
hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => {
24+
acc.add_variant_pat(ctx, variant.clone(), Some(name.clone()));
25+
true
26+
}
27+
hir::ModuleDef::Adt(hir::Adt::Enum(..))
28+
| hir::ModuleDef::Variant(..)
29+
| hir::ModuleDef::Const(..)
30+
| hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding,
31+
_ => false,
32+
},
3233
hir::ScopeDef::MacroDef(_) => true,
3334
_ => false,
3435
};
@@ -42,13 +43,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
4243
mod tests {
4344
use expect_test::{expect, Expect};
4445

45-
use crate::{test_utils::completion_list, CompletionKind};
46+
use crate::{
47+
test_utils::{check_edit, completion_list},
48+
CompletionKind,
49+
};
4650

4751
fn check(ra_fixture: &str, expect: Expect) {
4852
let actual = completion_list(ra_fixture, CompletionKind::Reference);
4953
expect.assert_eq(&actual)
5054
}
5155

56+
fn check_snippet(ra_fixture: &str, expect: Expect) {
57+
let actual = completion_list(ra_fixture, CompletionKind::Snippet);
58+
expect.assert_eq(&actual)
59+
}
60+
5261
#[test]
5362
fn completes_enum_variants_and_modules() {
5463
check(
@@ -69,7 +78,7 @@ fn foo() {
6978
en E
7079
ct Z
7180
st Bar
72-
ev X ()
81+
ev X
7382
md m
7483
"#]],
7584
);
@@ -114,4 +123,139 @@ fn foo() {
114123
"#]],
115124
);
116125
}
126+
127+
#[test]
128+
fn completes_in_param() {
129+
check(
130+
r#"
131+
enum E { X }
132+
133+
static FOO: E = E::X;
134+
struct Bar { f: u32 }
135+
136+
fn foo(<|>) {
137+
}
138+
"#,
139+
expect![[r#"
140+
st Bar
141+
"#]],
142+
);
143+
}
144+
145+
#[test]
146+
fn completes_pat_in_let() {
147+
check_snippet(
148+
r#"
149+
struct Bar { f: u32 }
150+
151+
fn foo() {
152+
let <|>
153+
}
154+
"#,
155+
expect![[r#"
156+
bn Bar Bar { f$1 }$0
157+
"#]],
158+
);
159+
}
160+
161+
#[test]
162+
fn completes_param_pattern() {
163+
check_snippet(
164+
r#"
165+
struct Foo { bar: String, baz: String }
166+
struct Bar(String, String);
167+
struct Baz;
168+
fn outer(<|>) {}
169+
"#,
170+
expect![[r#"
171+
bn Foo Foo { bar$1, baz$2 }: Foo$0
172+
bn Bar Bar($1, $2): Bar$0
173+
"#]],
174+
)
175+
}
176+
177+
#[test]
178+
fn completes_let_pattern() {
179+
check_snippet(
180+
r#"
181+
struct Foo { bar: String, baz: String }
182+
struct Bar(String, String);
183+
struct Baz;
184+
fn outer() {
185+
let <|>
186+
}
187+
"#,
188+
expect![[r#"
189+
bn Foo Foo { bar$1, baz$2 }$0
190+
bn Bar Bar($1, $2)$0
191+
"#]],
192+
)
193+
}
194+
195+
#[test]
196+
fn completes_refutable_pattern() {
197+
check_snippet(
198+
r#"
199+
struct Foo { bar: i32, baz: i32 }
200+
struct Bar(String, String);
201+
struct Baz;
202+
fn outer() {
203+
match () {
204+
<|>
205+
}
206+
}
207+
"#,
208+
expect![[r#"
209+
bn Foo Foo { bar$1, baz$2 }$0
210+
bn Bar Bar($1, $2)$0
211+
"#]],
212+
)
213+
}
214+
215+
#[test]
216+
fn omits_private_fields_pat() {
217+
check_snippet(
218+
r#"
219+
mod foo {
220+
pub struct Foo { pub bar: i32, baz: i32 }
221+
pub struct Bar(pub String, String);
222+
pub struct Invisible(String, String);
223+
}
224+
use foo::*;
225+
226+
fn outer() {
227+
match () {
228+
<|>
229+
}
230+
}
231+
"#,
232+
expect![[r#"
233+
bn Foo Foo { bar$1, .. }$0
234+
bn Bar Bar($1, ..)$0
235+
"#]],
236+
)
237+
}
238+
239+
#[test]
240+
fn only_shows_ident_completion() {
241+
check_edit(
242+
"Foo",
243+
r#"
244+
struct Foo(i32);
245+
fn main() {
246+
match Foo(92) {
247+
<|>(92) => (),
248+
}
249+
}
250+
"#,
251+
r#"
252+
struct Foo(i32);
253+
fn main() {
254+
match Foo(92) {
255+
Foo(92) => (),
256+
}
257+
}
258+
"#,
259+
);
260+
}
117261
}

crates/completion/src/context.rs

+13-8
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

+7
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;
@@ -159,6 +160,12 @@ impl<'a> Render<'a> {
159160
let item = render_fn(self.ctx, import_to_add, Some(local_name), *func);
160161
return Some(item);
161162
}
163+
ScopeDef::ModuleDef(Variant(_))
164+
if self.ctx.completion.is_pat_binding_or_const
165+
| self.ctx.completion.is_irrefutable_pat_binding =>
166+
{
167+
CompletionItemKind::EnumVariant
168+
}
162169
ScopeDef::ModuleDef(Variant(var)) => {
163170
let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None);
164171
return Some(item);

crates/completion/src/render/builder_ext.rs

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ impl Builder {
3434
return false;
3535
}
3636
if ctx.is_pattern_call {
37-
mark::hit!(dont_duplicate_pattern_parens);
3837
return false;
3938
}
4039
if ctx.is_call {

crates/completion/src/render/enum_variant.rs

-45
Original file line numberDiff line numberDiff line change
@@ -124,51 +124,6 @@ use Option::*;
124124
fn main() -> Option<i32> {
125125
Some($0)
126126
}
127-
"#,
128-
);
129-
check_edit(
130-
"Some",
131-
r#"
132-
enum Option<T> { Some(T), None }
133-
use Option::*;
134-
fn main(value: Option<i32>) {
135-
match value {
136-
Som<|>
137-
}
138-
}
139-
"#,
140-
r#"
141-
enum Option<T> { Some(T), None }
142-
use Option::*;
143-
fn main(value: Option<i32>) {
144-
match value {
145-
Some($0)
146-
}
147-
}
148-
"#,
149-
);
150-
}
151-
152-
#[test]
153-
fn dont_duplicate_pattern_parens() {
154-
mark::check!(dont_duplicate_pattern_parens);
155-
check_edit(
156-
"Var",
157-
r#"
158-
enum E { Var(i32) }
159-
fn main() {
160-
match E::Var(92) {
161-
E::<|>(92) => (),
162-
}
163-
}
164-
"#,
165-
r#"
166-
enum E { Var(i32) }
167-
fn main() {
168-
match E::Var(92) {
169-
E::Var(92) => (),
170-
}
171-
}
172127
"#,
173128
);
174129
}

0 commit comments

Comments
 (0)