private async Task <Solution?> FixAllContextsAsync( FixAllContext originalFixAllContext, ImmutableArray <FixAllContext> fixAllContexts ) { var progressTracker = originalFixAllContext.GetProgressTracker(); progressTracker.Description = this.GetFixAllTitle(originalFixAllContext); var solution = originalFixAllContext.Solution; // We have 3 pieces of work per project. Computing diagnostics, computing fixes, and applying fixes. progressTracker.AddItems(fixAllContexts.Length * 3); // Process each context one at a time, allowing us to dump any information we computed for each once done with it. var currentSolution = solution; foreach (var fixAllContext in fixAllContexts) { Contract.ThrowIfFalse( fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project ); currentSolution = await FixSingleContextAsync( currentSolution, fixAllContext, progressTracker ) .ConfigureAwait(false); } return(currentSolution); }
private async Task <CodeAction> GetFixAsync( ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap, FixAllContext fixAllContext) { // Process all documents in parallel. var progressTracker = fixAllContext.GetProgressTracker(); progressTracker.Description = WorkspaceExtensionsResources.Applying_fix_all; progressTracker.AddItems(documentsAndDiagnosticsToFixMap.Count); var updatedDocumentTasks = documentsAndDiagnosticsToFixMap.Select( kvp => FixDocumentAsync(kvp.Key, kvp.Value, fixAllContext, progressTracker)); await Task.WhenAll(updatedDocumentTasks).ConfigureAwait(false); var currentSolution = fixAllContext.Solution; foreach (var task in updatedDocumentTasks) { // 'await' the tasks so that if any completed in a canceled manner then we'll // throw the right exception here. Calling .Result on the tasks might end up // with AggregateExceptions being thrown instead. var updatedDocument = await task.ConfigureAwait(false); currentSolution = currentSolution.WithDocumentSyntaxRoot( updatedDocument.Id, await updatedDocument.GetRequiredSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false)); } var title = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); return(new CustomCodeActions.SolutionChangeAction(title, _ => Task.FromResult(currentSolution))); }
private async Task <Solution> GetSolutionFixesAsync(FixAllContext fixAllContext, ImmutableArray <Document> documents) { var documentDiagnosticsToFix = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(fixAllContext).ConfigureAwait(false); using var _1 = PooledHashSet <Document> .GetInstance(out var documentsToFix); using var _2 = PooledDictionary <DocumentId, Task <SyntaxNode?> > .GetInstance(out var documentIdToNewNode); // Determine the set of documents to actually fix. We can also use this to update the progress bar with // the amount of remaining work to perform. We'll update the progress bar as we perform each fix in // FixAllInDocumentAsync. foreach (var document in documents) { // Don't bother examining any documents that aren't in the list of docs that // actually have diagnostics. if (!documentDiagnosticsToFix.TryGetValue(document, out var diagnostics)) { continue; } documentsToFix.Add(document); } var progressTracker = fixAllContext.GetProgressTracker(); progressTracker.Description = WorkspaceExtensionsResources.Applying_fix_all; progressTracker.AddItems(documentsToFix.Count); foreach (var document in documentsToFix) { // Upper loop ensures that this indexing will always succeed. var diagnostics = documentDiagnosticsToFix[document]; documentIdToNewNode.Add(document.Id, FixAllInDocumentAsync(fixAllContext, progressTracker, document, diagnostics)); } // Allow the processing of all the documents to happen concurrently. await Task.WhenAll(documentIdToNewNode.Values).ConfigureAwait(false); var solution = fixAllContext.Solution; foreach (var(docId, syntaxNodeTask) in documentIdToNewNode) { var newDocumentRoot = await syntaxNodeTask.ConfigureAwait(false); if (newDocumentRoot == null) { continue; } solution = solution.WithDocumentSyntaxRoot(docId, newDocumentRoot); } return(solution); }
private async Task <Solution?> FixAllContextsAsync( FixAllContext originalFixAllContext, ImmutableArray <FixAllContext> fixAllContexts ) { var cancellationToken = originalFixAllContext.CancellationToken; var progressTracker = originalFixAllContext.GetProgressTracker(); progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle( originalFixAllContext ); // We have 2*P + 1 pieces of work. Computing diagnostics and fixes/changes per context, and then one pass // applying fixes. progressTracker.AddItems(fixAllContexts.Length * 2 + 1); // Mapping from document to the cumulative text changes created for that document. var docIdToTextMerger = new Dictionary <DocumentId, TextChangeMerger>(); // Process each context one at a time, allowing us to dump most of the information we computed for each once // done with it. The only information we need to preserve is the data we store in docIdToTextMerger foreach (var fixAllContext in fixAllContexts) { Contract.ThrowIfFalse( fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project ); await FixSingleContextAsync(fixAllContext, progressTracker, docIdToTextMerger) .ConfigureAwait(false); } // Finally, merge in all text changes into the solution. We can't do this per-project as we have to have // process *all* diagnostics in the solution to find the changes made to all documents. using (progressTracker.ItemCompletedScope()) { if (docIdToTextMerger.Count == 0) { return(null); } var currentSolution = originalFixAllContext.Solution; foreach (var group in docIdToTextMerger.GroupBy(kvp => kvp.Key.ProjectId)) { currentSolution = await ApplyChangesAsync( currentSolution, group.SelectAsArray(kvp => (kvp.Key, kvp.Value)), cancellationToken ) .ConfigureAwait(false); } return(currentSolution); } }
private async Task <ImmutableArray <(Diagnostic diagnostic, CodeAction action)> > GetDiagnosticsAndCodeActionsAsync( ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap, FixAllContext fixAllContext) { var cancellationToken = fixAllContext.CancellationToken; var fixAllState = fixAllContext.State; var fixesBag = new ConcurrentBag <(Diagnostic diagnostic, CodeAction action)>(); using (Logger.LogBlock( FunctionId.CodeFixes_FixAllOccurrencesComputation_Document_Fixes, FixAllLogger.CreateCorrelationLogMessage(fixAllState.CorrelationId), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); var progressTracker = fixAllContext.GetProgressTracker(); progressTracker.Description = WorkspaceExtensionsResources.Applying_fix_all; using var _1 = ArrayBuilder <Task> .GetInstance(out var tasks); using var _2 = ArrayBuilder <Document> .GetInstance(out var documentsToFix); // Determine the set of documents to actually fix. We can also use this to update the progress bar with // the amount of remaining work to perform. We'll update the progress bar as we compute each fix in // AddDocumentFixesAsync. foreach (var(document, diagnosticsToFix) in documentsAndDiagnosticsToFixMap) { if (!diagnosticsToFix.IsDefaultOrEmpty) { documentsToFix.Add(document); } } progressTracker.AddItems(documentsToFix.Count); foreach (var document in documentsToFix) { var diagnosticsToFix = documentsAndDiagnosticsToFixMap[document]; tasks.Add(AddDocumentFixesAsync( document, diagnosticsToFix, fixesBag, fixAllState, progressTracker, cancellationToken)); } await Task.WhenAll(tasks).ConfigureAwait(false); } return(fixesBag.ToImmutableArray()); }
public static async Task <ImmutableDictionary <Document, ImmutableArray <Diagnostic> > > GetDocumentDiagnosticsToFixAsync(FixAllContext fixAllContext) { var cancellationToken = fixAllContext.CancellationToken; var allDiagnostics = ImmutableArray <Diagnostic> .Empty; var document = fixAllContext.Document; var project = fixAllContext.Project; var progressTracker = fixAllContext.GetProgressTracker(); switch (fixAllContext.Scope) { case FixAllScope.Document: if (document != null && !await document.IsGeneratedCodeAsync(cancellationToken).ConfigureAwait(false)) { var documentDiagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(document).ConfigureAwait(false); return(ImmutableDictionary <Document, ImmutableArray <Diagnostic> > .Empty.SetItem(document, documentDiagnostics)); } break; case FixAllScope.Project: allDiagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); break; case FixAllScope.Solution: var projectsToFix = project.Solution.Projects .Where(p => p.Language == project.Language) .ToImmutableArray(); // Update the progress dialog with the count of projects to actually fix. We'll update the progress // bar as we get all the documents in AddDocumentDiagnosticsAsync. progressTracker.AddItems(projectsToFix.Length); var diagnostics = new ConcurrentDictionary <ProjectId, ImmutableArray <Diagnostic> >(); using (var _ = ArrayBuilder <Task> .GetInstance(projectsToFix.Length, out var tasks)) { foreach (var projectToFix in projectsToFix) { tasks.Add(Task.Run(async() => await AddDocumentDiagnosticsAsync(diagnostics, projectToFix).ConfigureAwait(false), cancellationToken)); } await Task.WhenAll(tasks).ConfigureAwait(false); allDiagnostics = allDiagnostics.AddRange(diagnostics.SelectMany(i => i.Value)); } break; } if (allDiagnostics.IsEmpty) { return(ImmutableDictionary <Document, ImmutableArray <Diagnostic> > .Empty); } return(await GetDocumentDiagnosticsToFixAsync( fixAllContext.Solution, allDiagnostics, fixAllContext.CancellationToken).ConfigureAwait(false)); async Task AddDocumentDiagnosticsAsync(ConcurrentDictionary <ProjectId, ImmutableArray <Diagnostic> > diagnostics, Project projectToFix) { try { var projectDiagnostics = await fixAllContext.GetAllDiagnosticsAsync(projectToFix).ConfigureAwait(false); diagnostics.TryAdd(projectToFix.Id, projectDiagnostics); } finally { progressTracker.ItemCompleted(); } } }
private Task <Solution?> FixAllContextsHelperAsync(FixAllContext originalFixAllContext, ImmutableArray <FixAllContext> fixAllContexts) => DocumentBasedFixAllProviderHelpers.FixAllContextsAsync(originalFixAllContext, fixAllContexts, originalFixAllContext.GetProgressTracker(), this.GetFixAllTitle(originalFixAllContext), DetermineDiagnosticsAndGetFixedDocumentsAsync);