Skip to content

Commit 1c84038

Browse files
authored
feat: LSP completion now works better in the middle of idents (#5795)
# Description ## Problem Resolves #5796 ## Summary I checked with Rust Analyzer how it works there, so now in Noir it works like Rust Analyzer: ![lsp-middle](https://github.com/user-attachments/assets/27dc4f51-d8ce-4526-b9b3-479f77c802ad) ![lsp-middle-2](https://github.com/user-attachments/assets/59b395b2-86bb-49af-bd52-ff60633fd496) ## 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 e050e93 commit 1c84038

File tree

6 files changed

+369
-58
lines changed

6 files changed

+369
-58
lines changed

docs/docs/noir/concepts/traits.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ fn main() {
227227

228228
### Associated Types and Constants
229229

230-
Traits also support associated types and constaints which can be thought of as additional generics that are referred to by name.
230+
Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name.
231231

232232
Here's an example of a trait with an associated type `Foo` and a constant `Bar`:
233233

tooling/lsp/src/requests/completion.rs

+201-34
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,20 @@ use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, Completion
1515
use noirc_errors::{Location, Span};
1616
use noirc_frontend::{
1717
ast::{
18-
AsTraitPath, BlockExpression, ConstructorExpression, Expression, ForLoopStatement, Ident,
19-
IfExpression, LValue, Lambda, LetStatement, MemberAccessExpression, NoirFunction,
20-
NoirStruct, NoirTraitImpl, Path, PathKind, PathSegment, Pattern, Statement, TraitItem,
21-
TypeImpl, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UseTree, UseTreeKind,
18+
AsTraitPath, BlockExpression, CallExpression, ConstructorExpression, Expression,
19+
ExpressionKind, ForLoopStatement, Ident, IfExpression, LValue, Lambda, LetStatement,
20+
MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, NoirTraitImpl,
21+
Path, PathKind, PathSegment, Pattern, Statement, StatementKind, TraitItem, TypeImpl,
22+
UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree,
23+
UseTreeKind,
2224
},
2325
graph::{CrateId, Dependency},
2426
hir::{
2527
def_map::{CrateDefMap, LocalModuleId, ModuleId},
2628
resolution::path_resolver::{PathResolver, StandardPathResolver},
2729
},
2830
hir_def::traits::Trait,
29-
macros_api::{ExpressionKind, ModuleDefId, NodeInterner, StatementKind, UnresolvedTypeData},
31+
macros_api::{ModuleDefId, NodeInterner},
3032
node_interner::ReferenceId,
3133
parser::{Item, ItemKind},
3234
ParsedModule, StructType, Type,
@@ -337,6 +339,71 @@ impl<'a> NodeFinder<'a> {
337339
}
338340
}
339341

342+
pub(super) fn find_in_call_expression(&mut self, call_expression: &CallExpression) {
343+
// Check if it's this case:
344+
//
345+
// foo::b>|<(...)
346+
//
347+
// In this case we want to suggest items in foo but if they are functions
348+
// we don't want to insert arguments, because they are already there (even if
349+
// they could be wrong) just because inserting them would lead to broken code.
350+
if let ExpressionKind::Variable(path) = &call_expression.func.kind {
351+
if self.includes_span(path.span) {
352+
self.find_in_path_impl(path, RequestedItems::AnyItems, true);
353+
return;
354+
}
355+
}
356+
357+
// Check if it's this case:
358+
//
359+
// foo.>|<(...)
360+
//
361+
// "foo." is actually broken, but it's parsed as "foo", so this is seen
362+
// as "foo(...)" but if we are at a dot right after "foo" it means it's
363+
// the above case and we want to suggest methods of foo's type.
364+
let after_dot = self.byte == Some(b'.');
365+
if after_dot && call_expression.func.span.end() as usize == self.byte_index - 1 {
366+
let location = Location::new(call_expression.func.span, self.file);
367+
if let Some(typ) = self.interner.type_at_location(location) {
368+
let typ = typ.follow_bindings();
369+
let prefix = "";
370+
self.complete_type_fields_and_methods(&typ, prefix, FunctionCompletionKind::Name);
371+
return;
372+
}
373+
}
374+
375+
self.find_in_expression(&call_expression.func);
376+
self.find_in_expressions(&call_expression.arguments);
377+
}
378+
379+
pub(super) fn find_in_method_call_expression(
380+
&mut self,
381+
method_call_expression: &MethodCallExpression,
382+
) {
383+
// Check if it's this case:
384+
//
385+
// foo.b>|<(...)
386+
//
387+
// In this case we want to suggest items in foo but if they are functions
388+
// we don't want to insert arguments, because they are already there (even if
389+
// they could be wrong) just because inserting them would lead to broken code.
390+
if self.includes_span(method_call_expression.method_name.span()) {
391+
let location = Location::new(method_call_expression.object.span, self.file);
392+
if let Some(typ) = self.interner.type_at_location(location) {
393+
let typ = typ.follow_bindings();
394+
let prefix = method_call_expression.method_name.to_string();
395+
let offset =
396+
self.byte_index - method_call_expression.method_name.span().start() as usize;
397+
let prefix = prefix[0..offset].to_string();
398+
self.complete_type_fields_and_methods(&typ, &prefix, FunctionCompletionKind::Name);
399+
return;
400+
}
401+
}
402+
403+
self.find_in_expression(&method_call_expression.object);
404+
self.find_in_expressions(&method_call_expression.arguments);
405+
}
406+
340407
fn find_in_block_expression(&mut self, block_expression: &BlockExpression) {
341408
let old_local_variables = self.local_variables.clone();
342409
for statement in &block_expression.statements {
@@ -417,7 +484,11 @@ impl<'a> NodeFinder<'a> {
417484
{
418485
let typ = self.interner.definition_type(definition_id);
419486
let prefix = "";
420-
self.complete_type_fields_and_methods(&typ, prefix);
487+
self.complete_type_fields_and_methods(
488+
&typ,
489+
prefix,
490+
FunctionCompletionKind::NameAndParameters,
491+
);
421492
}
422493
}
423494
}
@@ -507,7 +578,11 @@ impl<'a> NodeFinder<'a> {
507578
if let Some(typ) = self.interner.type_at_location(location) {
508579
let typ = typ.follow_bindings();
509580
let prefix = "";
510-
self.complete_type_fields_and_methods(&typ, prefix);
581+
self.complete_type_fields_and_methods(
582+
&typ,
583+
prefix,
584+
FunctionCompletionKind::NameAndParameters,
585+
);
511586
}
512587
}
513588
}
@@ -569,7 +644,11 @@ impl<'a> NodeFinder<'a> {
569644
if let Some(typ) = self.interner.type_at_location(location) {
570645
let typ = typ.follow_bindings();
571646
let prefix = ident.to_string().to_case(Case::Snake);
572-
self.complete_type_fields_and_methods(&typ, &prefix);
647+
self.complete_type_fields_and_methods(
648+
&typ,
649+
&prefix,
650+
FunctionCompletionKind::NameAndParameters,
651+
);
573652
return;
574653
}
575654
}
@@ -662,15 +741,61 @@ impl<'a> NodeFinder<'a> {
662741
}
663742

664743
fn find_in_path(&mut self, path: &Path, requested_items: RequestedItems) {
665-
// Only offer completions if we are right at the end of the path
666-
if self.byte_index != path.span.end() as usize {
744+
self.find_in_path_impl(path, requested_items, false);
745+
}
746+
747+
fn find_in_path_impl(
748+
&mut self,
749+
path: &Path,
750+
requested_items: RequestedItems,
751+
mut in_the_middle: bool,
752+
) {
753+
if !self.includes_span(path.span) {
667754
return;
668755
}
669756

670757
let after_colons = self.byte == Some(b':');
671758

672-
let mut idents: Vec<Ident> =
673-
path.segments.iter().map(|segment| segment.ident.clone()).collect();
759+
let mut idents: Vec<Ident> = Vec::new();
760+
761+
// Find in which ident we are in, and in which part of it
762+
// (it could be that we are completting in the middle of an ident)
763+
for segment in &path.segments {
764+
let ident = &segment.ident;
765+
766+
// Check if we are at the end of the ident
767+
if self.byte_index == ident.span().end() as usize {
768+
idents.push(ident.clone());
769+
break;
770+
}
771+
772+
// Check if we are in the middle of an ident
773+
if self.includes_span(ident.span()) {
774+
// If so, take the substring and push that as the list of idents
775+
// we'll do autocompletion for
776+
let offset = self.byte_index - ident.span().start() as usize;
777+
let substring = ident.0.contents[0..offset].to_string();
778+
let ident = Ident::new(
779+
substring,
780+
Span::from(ident.span().start()..ident.span().start() + offset as u32),
781+
);
782+
idents.push(ident);
783+
in_the_middle = true;
784+
break;
785+
}
786+
787+
idents.push(ident.clone());
788+
789+
// Stop if the cursor is right after this ident and '::'
790+
if after_colons && self.byte_index == ident.span().end() as usize + 2 {
791+
break;
792+
}
793+
}
794+
795+
if idents.len() < path.segments.len() {
796+
in_the_middle = true;
797+
}
798+
674799
let prefix;
675800
let at_root;
676801

@@ -687,6 +812,21 @@ impl<'a> NodeFinder<'a> {
687812
let is_single_segment = !after_colons && idents.is_empty() && path.kind == PathKind::Plain;
688813
let module_id;
689814

815+
let module_completion_kind = if after_colons || !idents.is_empty() {
816+
ModuleCompletionKind::DirectChildren
817+
} else {
818+
ModuleCompletionKind::AllVisibleItems
819+
};
820+
821+
// When completing in the middle of an ident, we don't want to complete
822+
// with function parameters because there might already be function parameters,
823+
// and in the middle of a path it leads to code that won't compile
824+
let function_completion_kind = if in_the_middle {
825+
FunctionCompletionKind::Name
826+
} else {
827+
FunctionCompletionKind::NameAndParameters
828+
};
829+
690830
if idents.is_empty() {
691831
module_id = self.module_id;
692832
} else {
@@ -702,6 +842,7 @@ impl<'a> NodeFinder<'a> {
702842
&Type::Struct(struct_type, vec![]),
703843
&prefix,
704844
FunctionKind::Any,
845+
function_completion_kind,
705846
);
706847
return;
707848
}
@@ -712,25 +853,28 @@ impl<'a> NodeFinder<'a> {
712853
ModuleDefId::TypeAliasId(type_alias_id) => {
713854
let type_alias = self.interner.get_type_alias(type_alias_id);
714855
let type_alias = type_alias.borrow();
715-
self.complete_type_methods(&type_alias.typ, &prefix, FunctionKind::Any);
856+
self.complete_type_methods(
857+
&type_alias.typ,
858+
&prefix,
859+
FunctionKind::Any,
860+
function_completion_kind,
861+
);
716862
return;
717863
}
718864
ModuleDefId::TraitId(trait_id) => {
719865
let trait_ = self.interner.get_trait(trait_id);
720-
self.complete_trait_methods(trait_, &prefix, FunctionKind::Any);
866+
self.complete_trait_methods(
867+
trait_,
868+
&prefix,
869+
FunctionKind::Any,
870+
function_completion_kind,
871+
);
721872
return;
722873
}
723874
ModuleDefId::GlobalId(_) => return,
724875
}
725876
}
726877

727-
let module_completion_kind = if after_colons {
728-
ModuleCompletionKind::DirectChildren
729-
} else {
730-
ModuleCompletionKind::AllVisibleItems
731-
};
732-
let function_completion_kind = FunctionCompletionKind::NameAndParameters;
733-
734878
self.complete_in_module(
735879
module_id,
736880
&prefix,
@@ -745,15 +889,15 @@ impl<'a> NodeFinder<'a> {
745889
match requested_items {
746890
RequestedItems::AnyItems => {
747891
self.local_variables_completion(&prefix);
748-
self.builtin_functions_completion(&prefix);
892+
self.builtin_functions_completion(&prefix, function_completion_kind);
749893
self.builtin_values_completion(&prefix);
750894
}
751895
RequestedItems::OnlyTypes => {
752896
self.builtin_types_completion(&prefix);
753897
self.type_parameters_completion(&prefix);
754898
}
755899
}
756-
self.complete_auto_imports(&prefix, requested_items);
900+
self.complete_auto_imports(&prefix, requested_items, function_completion_kind);
757901
}
758902
}
759903

@@ -924,17 +1068,30 @@ impl<'a> NodeFinder<'a> {
9241068
};
9251069
}
9261070

927-
fn complete_type_fields_and_methods(&mut self, typ: &Type, prefix: &str) {
1071+
fn complete_type_fields_and_methods(
1072+
&mut self,
1073+
typ: &Type,
1074+
prefix: &str,
1075+
function_completion_kind: FunctionCompletionKind,
1076+
) {
9281077
match typ {
9291078
Type::Struct(struct_type, generics) => {
9301079
self.complete_struct_fields(&struct_type.borrow(), generics, prefix);
9311080
}
9321081
Type::MutableReference(typ) => {
933-
return self.complete_type_fields_and_methods(typ, prefix);
1082+
return self.complete_type_fields_and_methods(
1083+
typ,
1084+
prefix,
1085+
function_completion_kind,
1086+
);
9341087
}
9351088
Type::Alias(type_alias, _) => {
9361089
let type_alias = type_alias.borrow();
937-
return self.complete_type_fields_and_methods(&type_alias.typ, prefix);
1090+
return self.complete_type_fields_and_methods(
1091+
&type_alias.typ,
1092+
prefix,
1093+
function_completion_kind,
1094+
);
9381095
}
9391096
Type::Tuple(types) => {
9401097
self.complete_tuple_fields(types);
@@ -958,10 +1115,21 @@ impl<'a> NodeFinder<'a> {
9581115
| Type::Error => (),
9591116
}
9601117

961-
self.complete_type_methods(typ, prefix, FunctionKind::SelfType(typ));
1118+
self.complete_type_methods(
1119+
typ,
1120+
prefix,
1121+
FunctionKind::SelfType(typ),
1122+
function_completion_kind,
1123+
);
9621124
}
9631125

964-
fn complete_type_methods(&mut self, typ: &Type, prefix: &str, function_kind: FunctionKind) {
1126+
fn complete_type_methods(
1127+
&mut self,
1128+
typ: &Type,
1129+
prefix: &str,
1130+
function_kind: FunctionKind,
1131+
function_completion_kind: FunctionCompletionKind,
1132+
) {
9651133
let Some(methods_by_name) = self.interner.get_type_methods(typ) else {
9661134
return;
9671135
};
@@ -971,7 +1139,7 @@ impl<'a> NodeFinder<'a> {
9711139
if name_matches(name, prefix) {
9721140
if let Some(completion_item) = self.function_completion_item(
9731141
func_id,
974-
FunctionCompletionKind::NameAndParameters,
1142+
function_completion_kind,
9751143
function_kind,
9761144
) {
9771145
self.completion_items.push(completion_item);
@@ -987,14 +1155,13 @@ impl<'a> NodeFinder<'a> {
9871155
trait_: &Trait,
9881156
prefix: &str,
9891157
function_kind: FunctionKind,
1158+
function_completion_kind: FunctionCompletionKind,
9901159
) {
9911160
for (name, func_id) in &trait_.method_ids {
9921161
if name_matches(name, prefix) {
993-
if let Some(completion_item) = self.function_completion_item(
994-
*func_id,
995-
FunctionCompletionKind::NameAndParameters,
996-
function_kind,
997-
) {
1162+
if let Some(completion_item) =
1163+
self.function_completion_item(*func_id, function_completion_kind, function_kind)
1164+
{
9981165
self.completion_items.push(completion_item);
9991166
self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id));
10001167
}

0 commit comments

Comments
 (0)