-
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
Make fix-all code action more parallel #73356
Conversation
IProgress<CodeAnalysisProgress> progressTracker) | ||
{ | ||
// First, determine the diagnostics to fix. | ||
var diagnostics = await DetermineDiagnosticsAsync(fixAllContext, progressTracker).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 method was inlined.
var diagnostics = await DetermineDiagnosticsAsync(fixAllContext, progressTracker).ConfigureAwait(false); | ||
|
||
// Second, get the fixes for all the diagnostics, and apply them to determine the new root/text for each doc. | ||
return await GetFixedDocumentsAsync(fixAllContext, progressTracker, diagnostics).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 method was inlined.
/// </summary> | ||
private static async Task<ImmutableDictionary<Document, ImmutableArray<Diagnostic>>> DetermineDiagnosticsAsync(FixAllContext fixAllContext, IProgress<CodeAnalysisProgress> progressTracker) | ||
{ | ||
using var _ = progressTracker.ItemCompletedScope(); |
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 went away. the caller is responsible for this now.
var workItemCount = fixAllKind == FixAllKind.CodeFix ? 3 : 2; | ||
progressTracker.AddItems(fixAllContexts.Length * workItemCount); | ||
// One work item for each context. Then one final item to for cleaning the solution. | ||
progressTracker.AddItems(fixAllContexts.Length + 1); |
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.
the math computation was originally wrong. it was not hte case that we did 3 items for fixes per context, and 2 per refactoring per context. Because this was just broken. i simplified this. the computation of progress length is done here, and the updating happens here as well. this is my preference for progress as having length computation and item-finish calls separated means it's too easy to get wrong.
progressTracker.AddItems(fixAllContexts.Length + 1); | ||
|
||
var (dirtySolution, changedRootDocumentIds) = await GetInitialUncleanedSolutionAsync().ConfigureAwait(false); | ||
return await CleanSolutionAsync(dirtySolution, changedRootDocumentIds).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.
broke into two nested functions to help out there.
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.
view with whitespace off.
{ | ||
Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project | ||
or FixAllScope.ContainingMember or FixAllScope.ContainingType); | ||
|
||
var cancellationToken = fixAllContext.CancellationToken; | ||
|
||
using var _1 = progressTracker.ItemCompletedScope(); |
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.
we definitely weren't doing 'two' "completed" calls here. so this was entirely busted for progress.
fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project or FixAllScope.ContainingMember or FixAllScope.ContainingType); | ||
|
||
var singleContextDocIdToNewRootOrText = await args.getFixedDocumentsAsync(fixAllContext).ConfigureAwait(false); | ||
callback(singleContextDocIdToNewRootOrText); |
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 likely going to change this to a callback as well, to avoid this intermediary dictionary.
await foreach (var (docId, nodeOrText) in results) | ||
docIdToNewRootOrText[docId] = nodeOrText; | ||
|
||
return docIdToNewRootOrText; |
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.
no need for a producerconsumer here anymore. we're not making the intermediary dictionary. we're just calling back into callback
with the doc data produced.
// Process all documents in parallel to get the change for each doc. | ||
var documentsAndSpansToFix = await fixAllContext.GetFixAllSpansAsync(cancellationToken).ConfigureAwait(false); | ||
|
||
return await ProducerConsumer<(DocumentId, (SyntaxNode? node, SourceText? text))>.RunParallelAsync( | ||
await RoslynParallel.ForEachAsync( |
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.
no need for a producerconsumer here anymore. we're not making the intermediary dictionary. we're just calling back into callback
with the doc data produced.
var workItemCount = fixAllKind == FixAllKind.CodeFix ? 3 : 2; | ||
progressTracker.AddItems(fixAllContexts.Length * workItemCount); | ||
// One work item for each context. | ||
progressTracker.AddItems(fixAllContexts.Length); |
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.
the math computation was originally wrong. it was not hte case that we did 3 items for fixes per context, and 2 per refactoring per context. Because this was just broken. i simplified this. the computation of progress length is done here, and the updating happens here as well. this is my preference for progress as having length computation and item-finish calls separated means it's too easy to get wrong.
@@ -492,4 +492,7 @@ | |||
<data name="Unexpected_value_0_in_DocumentKinds_array" xml:space="preserve"> | |||
<value>Unexpected value '{0}' in DocumentKinds array.</value> | |||
</data> | |||
<data name="Cleaning_fixed_documents" xml:space="preserve"> | |||
<value>Cleaning fixed documents</value> |
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 love this text. perhaps "Running code cleanup on fixed documents"?
var dirtyDocument = args.dirtySolution.GetRequiredDocument(documentId); | ||
var cleanedDocument = await PostProcessCodeAction.Instance.PostProcessChangesAsync(dirtyDocument, cancellationToken).ConfigureAwait(false); | ||
var cleanedText = await cleanedDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); | ||
callback((dirtyDocument.Id, cleanedText)); |
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 have a followup PR that moves this cleanup step to OOP. so we can benefit from .net core execution wins.
@ToddGrun ptal. |
return dirtySolution; | ||
|
||
// Clear out the progress so far. We're starting a new progress pass for the final cleanup. | ||
progressTracker.Report(CodeAnalysisProgress.Clear()); |
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.
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.
Yes. Exactly
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.
@jasonmalinowski For review when you get back. |
No description provided.