-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add async modifier on await completion #48352
Conversation
65354d1
to
d60bef3
Compare
src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs
Outdated
Show resolved
Hide resolved
src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs
Outdated
Show resolved
Hide resolved
Pinging @genlu for review. |
var method = syntaxContext.TargetToken.GetAncestor(node => node.IsAsyncSupportingFunctionSyntax()); | ||
if (method is not null && !method.GetModifiers().Any(SyntaxKind.AsyncKeyword)) | ||
{ | ||
context.AddItem(CommonCompletionItem.Create("await", string.Empty, CompletionItemRules.Default, inlineDescription: "Make container async")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might end up conflicting with the item provided by keyword recommender, and one would get deduplicated: since they have seem to have identical full display text (prefix + text + suffix), this will be decided by the order of providers being called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hiding the keyword might be the behavior we want, if so, we should figure out a way to make it explicit.
Otherwise, we need to show them both and give one higher priority . Before we get to the detail on how to implement this, let's figure out which approach we'd like to take.
In reply to: 517718403 [](ancestors = 517718403)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have reached a conclusion on this yet. Current behavior relies on the implementation detail of completion service which is fragile. One way to resolve the interaction issue between two providers that provide "await" item is to fold the keyword provider into this one.
@CyrusNajmabadi thoughts?
src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs
Outdated
Show resolved
Hide resolved
src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs
Outdated
Show resolved
Hide resolved
src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs
Outdated
Show resolved
Hide resolved
|
||
namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers | ||
{ | ||
[ExportCompletionProvider(nameof(AwaitCompletionProvider), LanguageNames.CSharp)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be valuable in VB too. Any plan to add VB support in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@genlu I'll work on supporting VB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 would def want VB here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you planning to add VB support in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question still remains. but i'm ok with no vb support initially if you'd prefer to punt on that for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CyrusNajmabadi I'll likely do that in a separate PR :(
src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs
Outdated
Show resolved
Hide resolved
{ | ||
} | ||
|
||
internal override ImmutableHashSet<char> TriggerCharacters => CompletionUtilities.CommonTriggerCharactersWithArgumentList; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dibarbet @allisonchou is this the proper TriggerCharacters to use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping @dibarbet @allisonchou
|
||
// MethodDeclarationSyntax, LocalFunctionStatementSyntax, AnonymousMethodExpressionSyntax, ParenthesizedLambdaExpressionSyntax, SimpleLambdaExpressionSyntax. | ||
|
||
var newDeclaration = AddAsyncModifier(declaration, SyntaxGenerator.GetGenerator(document)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work in debugging scenarios? e.g. EnC, watch window, etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@genlu I'm not sure.
FYI @JoeRobich @333fred This is a new completion provider that make changes outside of current span, I guess it might need some additional work in OmniSharp to enable it in VS Code? |
Could you please added some tests as well? Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with pass. Thanks for the contribution :)
In general, I like the approach of creating a separate provider. One of the major question I have is about dealing with item of await keyword and item from this provider. @CyrusNajmabadi have you discussed this with @Youssef1313 already?
Hi @Youssef1313, any update on this PR? Please let me know if there's anything I can do to help moving this forward. Thanks! :) |
@genlu Thanks for pinging. I might take some time to be able to work on this again. I'm okay if you (or any other external contributor) would like to take this over and continue the work. |
8423322
to
f1df660
Compare
Ping @CyrusNajmabadi |
|
||
return; | ||
|
||
static CompletionItem GetCompletionItem(bool shouldMakeContainerAsync) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this helper method seems pointless. inline?
var documentWithAsyncModifier = document.WithSyntaxRoot(root.ReplaceNode(declaration, AddAsyncModifier(declaration))); | ||
using var _ = ArrayBuilder<TextChange>.GetInstance(out var builder); | ||
|
||
builder.AddRange(await documentWithAsyncModifier.GetTextChangesAsync(document, cancellationToken).ConfigureAwait(false)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this part is actually a bit terrifying. tehre is no invariant that GetTextChanges returns good edits. Instead of going this route, i would instead either:
- just literally hardcode in the logic for determining the span where
async
should go. - use AddAsyncModifier, then directly check the new declaration for the
async
token and figure out the span from taht
Either of those will give a nice narrow text edit that we can depend on.
This is a blocker for this PR currently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: This was following what's currently done in another completion provider:
Line 156 in 5624337
var importChanges = await formattedDocumentWithImport.GetTextChangesAsync(document, cancellationToken).ConfigureAwait(false); |
I'll try with either of the approaches you mentioned.
tehre is no invariant that GetTextChanges returns good edits
I'm curious what "good edits" mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious what "good edits" mean?
So if you added async
, you'd ideally get an edit like 'async ' was added at position 15
. But there's no guarantee of that. it might literally say: i added the entire contents of the file at position 0
.
return CompletionChange.Create(CodeAnalysis.Completion.Utilities.Collapse(newText, builder.ToImmutableArray())); | ||
} | ||
|
||
private static SyntaxNode AddAsyncModifier(SyntaxNode declaration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since we're manually adding the async modifier anyways, it's fairly trivial for us to instead just determine the text-edit for where the async modifier should go directly. please switch to that. thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
definitely some necesxsary changes to make :)
@CyrusNajmabadi This is ready for another look. |
Ping @CyrusNajmabadi |
Thanks! really looking forward to this :) |
Fixes #45368
(Outdated)![await](https://user-images.githubusercontent.com/31348972/110206522-e2c1c800-7e86-11eb-8827-7bda1c4edcdf.gif)
TODO: