| File: CodeFixes\FixAllOccurrences\DocumentBasedFixAllProvider.cs | Web Access |
| Project: src\roslyn\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes; /// <summary> /// Provides a base class to write a <see cref="FixAllProvider"/> that fixes documents independently. This type /// should be used instead of <see cref="WellKnownFixAllProviders.BatchFixer"/> in the case where fixes for a <see /// cref="Diagnostic"/> only affect the <see cref="Document"/> the diagnostic was produced in. /// </summary> /// <remarks> /// This type provides suitable logic for fixing large solutions in an efficient manner. Projects are serially /// processed, with all the documents in the project being processed in parallel. Diagnostics are computed for the /// project and then appropriately bucketed by document. These are then passed to <see /// cref="FixAllAsync(FixAllContext, Document, ImmutableArray{Diagnostic})"/> for implementors to process. /// </remarks> public abstract class DocumentBasedFixAllProvider(ImmutableArray<FixAllScope> supportedFixAllScopes) : FixAllProvider { private readonly ImmutableArray<FixAllScope> _supportedFixAllScopes = supportedFixAllScopes; protected DocumentBasedFixAllProvider() : this(DefaultSupportedFixAllScopes) { } /// <summary> /// Produce a suitable title for the fix-all <see cref="CodeAction"/> this type creates in <see /// cref="GetFixAsync(FixAllContext)"/>. Override this if customizing that title is desired. /// </summary> protected virtual string GetFixAllTitle(FixAllContext fixAllContext) => fixAllContext.GetDefaultFixAllTitle(); /// <summary> /// Fix all the <paramref name="diagnostics"/> present in <paramref name="document"/>. The document returned /// will only be examined for its content (e.g. it's <see cref="SyntaxTree"/> or <see cref="SourceText"/>. No /// other aspects of (like it's properties), or changes to the <see cref="Project"/> or <see cref="Solution"/> /// it points at will be considered. /// </summary> /// <param name="fixAllContext">The context for the Fix All operation.</param> /// <param name="document">The document to fix.</param> /// <param name="diagnostics">The diagnostics to fix in the document.</param> /// <returns> /// <para>The new <see cref="Document"/> representing the content fixed document.</para> /// <para>-or-</para> /// <para><see langword="null"/>, if no changes were made to the document.</para> /// </returns> protected abstract Task<Document?> FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray<Diagnostic> diagnostics); public sealed override IEnumerable<FixAllScope> GetSupportedFixAllScopes() => _supportedFixAllScopes; public sealed override Task<CodeAction?> GetFixAsync(FixAllContext fixAllContext) => DefaultFixAllProviderHelpers.GetFixAsync( fixAllContext.GetDefaultFixAllTitle(), fixAllContext, FixAllContextsHelperAsync); private Task<Solution?> FixAllContextsHelperAsync(FixAllContext originalFixAllContext, ImmutableArray<FixAllContext> fixAllContexts) => DocumentBasedFixAllProviderHelpers.FixAllContextsAsync( originalFixAllContext, fixAllContexts, originalFixAllContext.Progress, this.GetFixAllTitle(originalFixAllContext), DetermineDiagnosticsAndGetFixedDocumentsAsync); private async Task DetermineDiagnosticsAndGetFixedDocumentsAsync( FixAllContext fixAllContext, Func<Document, Document?, ValueTask> onDocumentFixed) { var cancellationToken = fixAllContext.CancellationToken; // First, determine the diagnostics to fix. var documentToDiagnostics = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(fixAllContext).ConfigureAwait(false); // Second, get the fixes for each document+diagnostics pair in parallel, and apply them to determine the new // root/text for each doc. await Parallel.ForEachAsync( source: documentToDiagnostics, cancellationToken, async (kvp, cancellationToken) => { var (document, documentDiagnostics) = kvp; if (documentDiagnostics.IsDefaultOrEmpty) return; var newDocument = await this.FixAllAsync(fixAllContext, document, documentDiagnostics).ConfigureAwait(false); await onDocumentFixed(document, newDocument).ConfigureAwait(false); }).ConfigureAwait(false); } }