Skip to content

Commit 4507382

Browse files
committed
Move unprefixed field/method completion to dot
1 parent d346f5b commit 4507382

File tree

3 files changed

+101
-93
lines changed

3 files changed

+101
-93
lines changed

crates/ide_completion/src/completions.rs

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ pub(crate) mod unqualified_path;
1818

1919
use std::iter;
2020

21-
use either::Either;
22-
use hir::{known, HasVisibility};
21+
use hir::known;
2322
use ide_db::SymbolKind;
24-
use rustc_hash::FxHashSet;
2523

2624
use crate::{
2725
item::{Builder, CompletionKind},
@@ -254,44 +252,3 @@ fn complete_enum_variants(
254252
}
255253
}
256254
}
257-
258-
fn complete_fields(
259-
ctx: &CompletionContext,
260-
receiver: &hir::Type,
261-
mut f: impl FnMut(Either<hir::Field, usize>, hir::Type),
262-
) {
263-
for receiver in receiver.autoderef(ctx.db) {
264-
for (field, ty) in receiver.fields(ctx.db) {
265-
if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
266-
// Skip private field. FIXME: If the definition location of the
267-
// field is editable, we should show the completion
268-
continue;
269-
}
270-
f(Either::Left(field), ty);
271-
}
272-
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
273-
// FIXME: Handle visibility
274-
f(Either::Right(i), ty);
275-
}
276-
}
277-
}
278-
279-
fn complete_methods(
280-
ctx: &CompletionContext,
281-
receiver: &hir::Type,
282-
mut f: impl FnMut(hir::Function),
283-
) {
284-
if let Some(krate) = ctx.krate {
285-
let mut seen_methods = FxHashSet::default();
286-
let traits_in_scope = ctx.scope.traits_in_scope();
287-
receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
288-
if func.self_param(ctx.db).is_some()
289-
&& ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
290-
&& seen_methods.insert(func.name(ctx.db))
291-
{
292-
f(func);
293-
}
294-
None::<()>
295-
});
296-
}
297-
}

crates/ide_completion/src/completions/dot.rs

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
//! Completes references after dot (fields and method calls).
22
33
use either::Either;
4+
use hir::{HasVisibility, ScopeDef};
5+
use rustc_hash::FxHashSet;
46

57
use crate::{context::CompletionContext, Completions};
68

79
/// Complete dot accesses, i.e. fields or methods.
810
pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
911
let dot_receiver = match &ctx.dot_receiver {
1012
Some(expr) => expr,
11-
_ => return,
13+
_ => return complete_undotted_self(acc, ctx),
1214
};
1315

1416
let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
@@ -19,12 +21,77 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
1921
if ctx.is_call {
2022
cov_mark::hit!(test_no_struct_field_completion_for_method_call);
2123
} else {
22-
super::complete_fields(ctx, &receiver_ty, |field, ty| match field {
24+
complete_fields(ctx, &receiver_ty, |field, ty| match field {
2325
Either::Left(field) => acc.add_field(ctx, None, field, &ty),
2426
Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty),
2527
});
2628
}
27-
super::complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None));
29+
complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None));
30+
}
31+
32+
fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33+
if !ctx.is_trivial_path {
34+
return;
35+
}
36+
ctx.scope.process_all_names(&mut |name, def| {
37+
if let ScopeDef::Local(local) = &def {
38+
if local.is_self(ctx.db) {
39+
let ty = local.ty(ctx.db);
40+
complete_fields(ctx, &ty, |field, ty| match field {
41+
either::Either::Left(field) => {
42+
acc.add_field(ctx, Some(name.clone()), field, &ty)
43+
}
44+
either::Either::Right(tuple_idx) => {
45+
acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty)
46+
}
47+
});
48+
complete_methods(ctx, &ty, |func| {
49+
acc.add_method(ctx, func, Some(name.clone()), None)
50+
});
51+
}
52+
}
53+
});
54+
}
55+
56+
fn complete_fields(
57+
ctx: &CompletionContext,
58+
receiver: &hir::Type,
59+
mut f: impl FnMut(Either<hir::Field, usize>, hir::Type),
60+
) {
61+
for receiver in receiver.autoderef(ctx.db) {
62+
for (field, ty) in receiver.fields(ctx.db) {
63+
if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
64+
// Skip private field. FIXME: If the definition location of the
65+
// field is editable, we should show the completion
66+
continue;
67+
}
68+
f(Either::Left(field), ty);
69+
}
70+
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
71+
// FIXME: Handle visibility
72+
f(Either::Right(i), ty);
73+
}
74+
}
75+
}
76+
77+
fn complete_methods(
78+
ctx: &CompletionContext,
79+
receiver: &hir::Type,
80+
mut f: impl FnMut(hir::Function),
81+
) {
82+
if let Some(krate) = ctx.krate {
83+
let mut seen_methods = FxHashSet::default();
84+
let traits_in_scope = ctx.scope.traits_in_scope();
85+
receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
86+
if func.self_param(ctx.db).is_some()
87+
&& ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
88+
&& seen_methods.insert(func.name(ctx.db))
89+
{
90+
f(func);
91+
}
92+
None::<()>
93+
});
94+
}
2895
}
2996

3097
#[cfg(test)]
@@ -453,4 +520,34 @@ impl S {
453520
"#]],
454521
);
455522
}
523+
524+
#[test]
525+
fn completes_bare_fields_and_methods_in_methods() {
526+
check(
527+
r#"
528+
struct Foo { field: i32 }
529+
530+
impl Foo { fn foo(&self) { $0 } }"#,
531+
expect![[r#"
532+
lc self &Foo
533+
sp Self
534+
st Foo
535+
fd self.field i32
536+
me self.foo() fn(&self)
537+
"#]],
538+
);
539+
check(
540+
r#"
541+
struct Foo(i32);
542+
543+
impl Foo { fn foo(&mut self) { $0 } }"#,
544+
expect![[r#"
545+
lc self &mut Foo
546+
sp Self
547+
st Foo
548+
fd self.0 i32
549+
me self.foo() fn(&mut self)
550+
"#]],
551+
);
552+
}
456553
}

crates/ide_completion/src/completions/unqualified_path.rs

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
4747
cov_mark::hit!(skip_lifetime_completion);
4848
return;
4949
}
50-
if let ScopeDef::Local(local) = &res {
51-
if local.is_self(ctx.db) {
52-
let ty = local.ty(ctx.db);
53-
super::complete_fields(ctx, &ty, |field, ty| match field {
54-
either::Either::Left(field) => {
55-
acc.add_field(ctx, Some(name.clone()), field, &ty)
56-
}
57-
either::Either::Right(tuple_idx) => {
58-
acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty)
59-
}
60-
});
61-
super::complete_methods(ctx, &ty, |func| {
62-
acc.add_method(ctx, func, Some(name.clone()), None)
63-
});
64-
}
65-
}
6650
acc.add_resolution(ctx, name, &res);
6751
});
6852
}
@@ -393,36 +377,6 @@ fn foo() {
393377
);
394378
}
395379

396-
#[test]
397-
fn completes_qualified_fields_and_methods_in_methods() {
398-
check(
399-
r#"
400-
struct Foo { field: i32 }
401-
402-
impl Foo { fn foo(&self) { $0 } }"#,
403-
expect![[r#"
404-
fd self.field i32
405-
me self.foo() fn(&self)
406-
lc self &Foo
407-
sp Self
408-
st Foo
409-
"#]],
410-
);
411-
check(
412-
r#"
413-
struct Foo(i32);
414-
415-
impl Foo { fn foo(&mut self) { $0 } }"#,
416-
expect![[r#"
417-
fd self.0 i32
418-
me self.foo() fn(&mut self)
419-
lc self &mut Foo
420-
sp Self
421-
st Foo
422-
"#]],
423-
);
424-
}
425-
426380
#[test]
427381
fn completes_prelude() {
428382
check(

0 commit comments

Comments
 (0)