Skip to content

Commit f57ce85

Browse files
authored
feat: LSP now suggests self fields and methods (#5955)
# Description ## Problem While working with Rust I noticed that if you type a field name or a method name and there's a `self` variable, those fields and methods are suggested without you having to explicitly write `self`. I think that's useful as you sometimes forget about the self prefix, or just because you end up typing less. ## Summary This PR implements that. ![lsp-self](https://github.com/user-attachments/assets/976151ab-cdb2-4690-8b62-21514314229a) ## Additional Context ## Documentation Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.
1 parent 3dab4dd commit f57ce85

File tree

3 files changed

+121
-14
lines changed

3 files changed

+121
-14
lines changed

tooling/lsp/src/requests/completion.rs

+70-8
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct NodeFinder<'a> {
106106
nesting: usize,
107107
/// The line where an auto_import must be inserted
108108
auto_import_line: usize,
109+
self_type: Option<Type>,
109110
}
110111

111112
impl<'a> NodeFinder<'a> {
@@ -147,6 +148,7 @@ impl<'a> NodeFinder<'a> {
147148
suggested_module_def_ids: HashSet::new(),
148149
nesting: 0,
149150
auto_import_line: 0,
151+
self_type: None,
150152
}
151153
}
152154

@@ -191,8 +193,9 @@ impl<'a> NodeFinder<'a> {
191193
fields.remove(&field.0.contents);
192194
}
193195

196+
let self_prefix = false;
194197
for (field, typ) in fields {
195-
self.completion_items.push(struct_field_completion_item(field, typ));
198+
self.completion_items.push(struct_field_completion_item(field, typ, self_prefix));
196199
}
197200
}
198201

@@ -293,6 +296,7 @@ impl<'a> NodeFinder<'a> {
293296
&prefix,
294297
FunctionKind::Any,
295298
function_completion_kind,
299+
false, // self_prefix
296300
);
297301
return;
298302
}
@@ -308,6 +312,7 @@ impl<'a> NodeFinder<'a> {
308312
&prefix,
309313
FunctionKind::Any,
310314
function_completion_kind,
315+
false, // self_prefix
311316
);
312317
return;
313318
}
@@ -340,6 +345,15 @@ impl<'a> NodeFinder<'a> {
340345
self.local_variables_completion(&prefix);
341346
self.builtin_functions_completion(&prefix, function_completion_kind);
342347
self.builtin_values_completion(&prefix);
348+
if let Some(self_type) = &self.self_type {
349+
let self_prefix = true;
350+
self.complete_type_fields_and_methods(
351+
&self_type.clone(),
352+
&prefix,
353+
function_completion_kind,
354+
self_prefix,
355+
);
356+
}
343357
}
344358
RequestedItems::OnlyTypes => {
345359
self.builtin_types_completion(&prefix);
@@ -518,16 +532,18 @@ impl<'a> NodeFinder<'a> {
518532
typ: &Type,
519533
prefix: &str,
520534
function_completion_kind: FunctionCompletionKind,
535+
self_prefix: bool,
521536
) {
522537
match typ {
523538
Type::Struct(struct_type, generics) => {
524-
self.complete_struct_fields(&struct_type.borrow(), generics, prefix);
539+
self.complete_struct_fields(&struct_type.borrow(), generics, prefix, self_prefix);
525540
}
526541
Type::MutableReference(typ) => {
527542
return self.complete_type_fields_and_methods(
528543
typ,
529544
prefix,
530545
function_completion_kind,
546+
self_prefix,
531547
);
532548
}
533549
Type::Alias(type_alias, _) => {
@@ -536,10 +552,11 @@ impl<'a> NodeFinder<'a> {
536552
&type_alias.typ,
537553
prefix,
538554
function_completion_kind,
555+
self_prefix,
539556
);
540557
}
541558
Type::Tuple(types) => {
542-
self.complete_tuple_fields(types);
559+
self.complete_tuple_fields(types, self_prefix);
543560
}
544561
Type::FieldElement
545562
| Type::Array(_, _)
@@ -565,6 +582,7 @@ impl<'a> NodeFinder<'a> {
565582
prefix,
566583
FunctionKind::SelfType(typ),
567584
function_completion_kind,
585+
self_prefix,
568586
);
569587
}
570588

@@ -574,6 +592,7 @@ impl<'a> NodeFinder<'a> {
574592
prefix: &str,
575593
function_kind: FunctionKind,
576594
function_completion_kind: FunctionCompletionKind,
595+
self_prefix: bool,
577596
) {
578597
let Some(methods_by_name) = self.interner.get_type_methods(typ) else {
579598
return;
@@ -587,6 +606,7 @@ impl<'a> NodeFinder<'a> {
587606
func_id,
588607
function_completion_kind,
589608
function_kind,
609+
self_prefix,
590610
) {
591611
self.completion_items.push(completion_item);
592612
self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id));
@@ -603,13 +623,16 @@ impl<'a> NodeFinder<'a> {
603623
function_kind: FunctionKind,
604624
function_completion_kind: FunctionCompletionKind,
605625
) {
626+
let self_prefix = false;
627+
606628
for (name, func_id) in &trait_.method_ids {
607629
if name_matches(name, prefix) {
608630
if let Some(completion_item) = self.function_completion_item(
609631
name,
610632
*func_id,
611633
function_completion_kind,
612634
function_kind,
635+
self_prefix,
613636
) {
614637
self.completion_items.push(completion_item);
615638
self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id));
@@ -623,17 +646,19 @@ impl<'a> NodeFinder<'a> {
623646
struct_type: &StructType,
624647
generics: &[Type],
625648
prefix: &str,
649+
self_prefix: bool,
626650
) {
627651
for (name, typ) in &struct_type.get_fields(generics) {
628652
if name_matches(name, prefix) {
629-
self.completion_items.push(struct_field_completion_item(name, typ));
653+
self.completion_items.push(struct_field_completion_item(name, typ, self_prefix));
630654
}
631655
}
632656
}
633657

634-
fn complete_tuple_fields(&mut self, types: &[Type]) {
658+
fn complete_tuple_fields(&mut self, types: &[Type], self_prefix: bool) {
635659
for (index, typ) in types.iter().enumerate() {
636-
self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string()));
660+
let name = index.to_string();
661+
self.completion_items.push(field_completion_item(&name, typ.to_string(), self_prefix));
637662
}
638663
}
639664

@@ -761,6 +786,23 @@ impl<'a> NodeFinder<'a> {
761786
None
762787
}
763788

789+
fn try_set_self_type(&mut self, pattern: &Pattern) {
790+
match pattern {
791+
Pattern::Identifier(ident) => {
792+
if ident.0.contents == "self" {
793+
let location = Location::new(ident.span(), self.file);
794+
if let Some(ReferenceId::Local(definition_id)) =
795+
self.interner.find_referenced(location)
796+
{
797+
self.self_type = Some(self.interner.definition_type(definition_id));
798+
}
799+
}
800+
}
801+
Pattern::Mutable(pattern, ..) => self.try_set_self_type(pattern),
802+
Pattern::Tuple(..) | Pattern::Struct(..) => (),
803+
}
804+
}
805+
764806
fn includes_span(&self, span: Span) -> bool {
765807
span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize
766808
}
@@ -817,6 +859,7 @@ impl<'a> Visitor for NodeFinder<'a> {
817859
self.collect_type_parameters_in_generics(&noir_function.def.generics);
818860

819861
for param in &noir_function.def.parameters {
862+
self.try_set_self_type(&param.pattern);
820863
param.typ.accept(self);
821864
}
822865

@@ -830,6 +873,7 @@ impl<'a> Visitor for NodeFinder<'a> {
830873
noir_function.def.body.accept(Some(span), self);
831874

832875
self.type_parameters = old_type_parameters;
876+
self.self_type = None;
833877

834878
false
835879
}
@@ -945,7 +989,13 @@ impl<'a> Visitor for NodeFinder<'a> {
945989
if let Some(typ) = self.interner.type_at_location(location) {
946990
let typ = typ.follow_bindings();
947991
let prefix = "";
948-
self.complete_type_fields_and_methods(&typ, prefix, FunctionCompletionKind::Name);
992+
let self_prefix = false;
993+
self.complete_type_fields_and_methods(
994+
&typ,
995+
prefix,
996+
FunctionCompletionKind::Name,
997+
self_prefix,
998+
);
949999
return false;
9501000
}
9511001
}
@@ -973,7 +1023,13 @@ impl<'a> Visitor for NodeFinder<'a> {
9731023
let offset =
9741024
self.byte_index - method_call_expression.method_name.span().start() as usize;
9751025
let prefix = prefix[0..offset].to_string();
976-
self.complete_type_fields_and_methods(&typ, &prefix, FunctionCompletionKind::Name);
1026+
let self_prefix = false;
1027+
self.complete_type_fields_and_methods(
1028+
&typ,
1029+
&prefix,
1030+
FunctionCompletionKind::Name,
1031+
self_prefix,
1032+
);
9771033
return false;
9781034
}
9791035
}
@@ -1042,10 +1098,12 @@ impl<'a> Visitor for NodeFinder<'a> {
10421098
{
10431099
let typ = self.interner.definition_type(definition_id);
10441100
let prefix = "";
1101+
let self_prefix = false;
10451102
self.complete_type_fields_and_methods(
10461103
&typ,
10471104
prefix,
10481105
FunctionCompletionKind::NameAndParameters,
1106+
self_prefix,
10491107
);
10501108
}
10511109
}
@@ -1072,10 +1130,12 @@ impl<'a> Visitor for NodeFinder<'a> {
10721130
if let Some(typ) = self.interner.type_at_location(location) {
10731131
let typ = typ.follow_bindings();
10741132
let prefix = "";
1133+
let self_prefix = false;
10751134
self.complete_type_fields_and_methods(
10761135
&typ,
10771136
prefix,
10781137
FunctionCompletionKind::NameAndParameters,
1138+
self_prefix,
10791139
);
10801140
}
10811141
}
@@ -1136,10 +1196,12 @@ impl<'a> Visitor for NodeFinder<'a> {
11361196
if let Some(typ) = self.interner.type_at_location(location) {
11371197
let typ = typ.follow_bindings();
11381198
let prefix = ident.to_string().to_case(Case::Snake);
1199+
let self_prefix = false;
11391200
self.complete_type_fields_and_methods(
11401201
&typ,
11411202
&prefix,
11421203
FunctionCompletionKind::NameAndParameters,
1204+
self_prefix,
11431205
);
11441206
return false;
11451207
}

tooling/lsp/src/requests/completion/completion_items.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ impl<'a> NodeFinder<'a> {
4343
func_id,
4444
function_completion_kind,
4545
function_kind,
46+
false, // self_prefix
4647
),
4748
ModuleDefId::TypeId(..) => Some(self.struct_completion_item(name)),
4849
ModuleDefId::TypeAliasId(..) => Some(self.type_alias_completion_item(name)),
@@ -77,6 +78,7 @@ impl<'a> NodeFinder<'a> {
7778
func_id: FuncId,
7879
function_completion_kind: FunctionCompletionKind,
7980
function_kind: FunctionKind,
81+
self_prefix: bool,
8082
) -> Option<CompletionItem> {
8183
let func_meta = self.interner.function_meta(&func_id);
8284

@@ -135,6 +137,8 @@ impl<'a> NodeFinder<'a> {
135137
} else {
136138
false
137139
};
140+
let name = if self_prefix { format!("self.{}", name) } else { name.clone() };
141+
let name = &name;
138142
let description = func_meta_type_to_string(func_meta, func_self_type.is_some());
139143

140144
let completion_item = match function_completion_kind {
@@ -294,12 +298,24 @@ fn type_to_self_string(typ: &Type, string: &mut String) {
294298
}
295299
}
296300

297-
pub(super) fn struct_field_completion_item(field: &str, typ: &Type) -> CompletionItem {
298-
field_completion_item(field, typ.to_string())
301+
pub(super) fn struct_field_completion_item(
302+
field: &str,
303+
typ: &Type,
304+
self_type: bool,
305+
) -> CompletionItem {
306+
field_completion_item(field, typ.to_string(), self_type)
299307
}
300308

301-
pub(super) fn field_completion_item(field: &str, typ: impl Into<String>) -> CompletionItem {
302-
simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into()))
309+
pub(super) fn field_completion_item(
310+
field: &str,
311+
typ: impl Into<String>,
312+
self_type: bool,
313+
) -> CompletionItem {
314+
if self_type {
315+
simple_completion_item(format!("self.{field}"), CompletionItemKind::FIELD, Some(typ.into()))
316+
} else {
317+
simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into()))
318+
}
303319
}
304320

305321
pub(super) fn simple_completion_item(

tooling/lsp/src/requests/completion/tests.rs

+31-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ mod completion_tests {
77
completion_items::{
88
completion_item_with_sort_text,
99
completion_item_with_trigger_parameter_hints_command, crate_completion_item,
10-
field_completion_item, module_completion_item, simple_completion_item,
11-
snippet_completion_item,
10+
module_completion_item, simple_completion_item, snippet_completion_item,
1211
},
1312
sort_text::{auto_import_sort_text, self_mismatch_sort_text},
1413
},
@@ -116,6 +115,10 @@ mod completion_tests {
116115
))
117116
}
118117

118+
fn field_completion_item(field: &str, typ: impl Into<String>) -> CompletionItem {
119+
crate::requests::completion::field_completion_item(field, typ, false)
120+
}
121+
119122
#[test]
120123
async fn test_use_first_segment() {
121124
let src = r#"
@@ -1888,4 +1891,30 @@ mod completion_tests {
18881891
Some("(use super::barbaz)".to_string()),
18891892
);
18901893
}
1894+
1895+
#[test]
1896+
async fn test_suggests_self_fields_and_methods() {
1897+
let src = r#"
1898+
struct Foo {
1899+
foobar: Field,
1900+
}
1901+
1902+
impl Foo {
1903+
fn foobarbaz(self) {}
1904+
1905+
fn some_method(self) {
1906+
foob>|<
1907+
}
1908+
}
1909+
"#;
1910+
1911+
assert_completion_excluding_auto_import(
1912+
src,
1913+
vec![
1914+
field_completion_item("self.foobar", "Field"),
1915+
function_completion_item("self.foobarbaz()", "self.foobarbaz()", "fn(self)"),
1916+
],
1917+
)
1918+
.await;
1919+
}
18911920
}

0 commit comments

Comments
 (0)