Skip to content

Commit

Permalink
feat: let LSP suggest trait impl methods as you are typing them (#6029)
Browse files Browse the repository at this point in the history
# Description

## Problem

Part of #1577

## Summary

Now that we have the "Implement missing members" code action, it's a
small step to have autocompletion suggest a trait method as you are
typing its name (like in Rust Analyzer). The only tricky part was that
`fn foo` produced a parse error so I made that parse to a function
without arguments and without body (but still reporting a parser error).
A nice side-effect of this is that if you type that and you think "Hm,
what should the parameters be" the rest of the LSP features don't stop
working (like, inlay hints don't disappear, etc.)

Even though "Implement missing members" might seem to be better than
offering completions one by one, this completion has two advantages:
1. It offers default methods
2. It teaches the user that LSP can suggest trait methods, so they could
think "hm, I wonder if there's a code action that suggests all of them"
(at least this was my experience for discovering the code action)


![lsp-suggest-trait-impl-method](https://github.com/user-attachments/assets/00d1a6f8-597b-4686-ab20-78cc145f22f4)

## 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.
  • Loading branch information
asterite authored Sep 13, 2024
1 parent 359caaf commit dfed81b
Show file tree
Hide file tree
Showing 8 changed files with 667 additions and 461 deletions.
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/parser/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub enum ParserErrorReason {
ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterImplType,
#[error("expected <, where or {{ after trait impl for type")]
ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType,
#[error("expected ( or < after function name")]
ExpectedLeftParenOrLeftBracketAfterFunctionName,
#[error("Expected a ; separating these two statements")]
MissingSeparatingSemi,
#[error("constrain keyword is deprecated")]
Expand Down
78 changes: 57 additions & 21 deletions compiler/noirc_frontend/src/parser/parser/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,30 +48,66 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser<NoirFunct
.then_ignore(keyword(Keyword::Fn))
.then(ident())
.then(generics())
.then(parenthesized(function_parameters(allow_self)))
.then(function_return_type())
.then(where_clause())
.then(body_or_error)
.validate(|(((args, ret), where_clause), (body, body_span)), span, emit| {
let ((((attributes, modifiers), name), generics), parameters) = args;
.then(
parenthesized(function_parameters(allow_self))
.then(function_return_type())
.then(where_clause())
.then(body_or_error)
// Allow parsing just `fn foo` for recovery and LSP autocompletion
.or_not(),
)
.validate(|args, span, emit| {
let (
(((attributes, (is_unconstrained, visibility, is_comptime)), name), generics),
params_and_others,
) = args;

// Validate collected attributes, filtering them into function and secondary variants
let attributes = validate_attributes(attributes, span, emit);
FunctionDefinition {
span: body_span,
name,
attributes,
is_unconstrained: modifiers.0,
visibility: modifiers.1,
is_comptime: modifiers.2,
generics,
parameters,
body,
where_clause,
return_type: ret.1,
return_visibility: ret.0,
}
.into()

let function_definition = if let Some(params_and_others) = params_and_others {
let (
((parameters, (return_visibility, return_type)), where_clause),
(body, body_span),
) = params_and_others;

FunctionDefinition {
span: body_span,
name,
attributes,
is_unconstrained,
visibility,
is_comptime,
generics,
parameters,
body,
where_clause,
return_type,
return_visibility,
}
} else {
emit(ParserError::with_reason(
ParserErrorReason::ExpectedLeftParenOrLeftBracketAfterFunctionName,
span,
));

let empty_span = Span::from(span.end()..span.end());
FunctionDefinition {
span: empty_span,
name,
attributes,
is_unconstrained,
visibility,
is_comptime,
generics,
parameters: Vec::new(),
body: BlockExpression { statements: vec![] },
where_clause: Vec::new(),
return_type: FunctionReturnType::Default(empty_span),
return_visibility: Visibility::Private,
}
};
function_definition.into()
})
}

Expand Down
1 change: 1 addition & 0 deletions tooling/lsp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ mod modules;
mod notifications;
mod requests;
mod solver;
mod trait_impl_method_stub_generator;
mod types;
mod utils;
mod visibility;
Expand Down
Loading

0 comments on commit dfed81b

Please sign in to comment.