| File: CodeFixesAndRefactorings\AbstractFixAllCodeAction.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// 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.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// <summary> /// Fix all code action for a code action registered by /// a <see cref="CodeFixes.CodeFixProvider"/> or a <see cref="CodeRefactorings.CodeRefactoringProvider"/>. /// </summary> internal sealed class RefactorOrFixAllCodeAction( IRefactorOrFixAllState refactorOrFixAllState, bool showPreviewChangesDialog, string? title = null, string? message = null) : CodeAction { private static readonly ISet<string> s_predefinedProviderNames = typeof(PredefinedCodeFixProviderNames).GetTypeInfo().DeclaredFields.Concat(typeof(PredefinedCodeRefactoringProviderNames).GetTypeInfo().DeclaredFields) .Where(field => field.IsStatic) .Select(field => (string)field.GetValue(null)!) .ToSet(); private bool _showPreviewChangesDialog = showPreviewChangesDialog; public IRefactorOrFixAllState RefactorOrFixAllState { get; } = refactorOrFixAllState; // We don't need to post process changes here as the inner code action created for Fix multiple code fix already executes. internal sealed override CodeActionCleanup Cleanup => CodeActionCleanup.None; /// <summary> /// Creates a new <see cref="IRefactorOrFixAllContext"/> with the given parameters. /// </summary> private static IRefactorOrFixAllContext CreateFixAllContext(IRefactorOrFixAllState state, IProgress<CodeAnalysisProgress> progressTracker, CancellationToken cancellationToken) { return state switch { FixAllState fixAllState => new FixAllContext(fixAllState, progressTracker, cancellationToken), RefactorAllState refactorAllState => new RefactorAllContext(refactorAllState, progressTracker, cancellationToken), _ => throw ExceptionUtilities.UnexpectedValue(state), }; } public override string Title => title ?? (this.RefactorOrFixAllState.Scope switch { FixAllScope.Document => FeaturesResources.Document, FixAllScope.Project => FeaturesResources.Project, FixAllScope.Solution => FeaturesResources.Solution, FixAllScope.ContainingMember => FeaturesResources.Containing_Member, FixAllScope.ContainingType => FeaturesResources.Containing_Type, _ => throw ExceptionUtilities.UnexpectedValue(this.RefactorOrFixAllState.Scope), }); internal override string Message => message ?? FeaturesResources.Computing_fix_all_occurrences_code_fix; protected sealed override Task<ImmutableArray<CodeActionOperation>> ComputeOperationsAsync( IProgress<CodeAnalysisProgress> progressTracker, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); FixAllLogger.LogState(RefactorOrFixAllState, IsInternalProvider(RefactorOrFixAllState)); var service = RefactorOrFixAllState.Project.Solution.Services.GetRequiredService<IFixAllGetFixesService>(); var fixAllContext = CreateFixAllContext(RefactorOrFixAllState, progressTracker, cancellationToken); progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultTitle())); return service.GetFixAllOperationsAsync(fixAllContext, _showPreviewChangesDialog); } protected sealed override Task<Solution?> GetChangedSolutionAsync( IProgress<CodeAnalysisProgress> progressTracker, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); FixAllLogger.LogState(RefactorOrFixAllState, IsInternalProvider(RefactorOrFixAllState)); var service = RefactorOrFixAllState.Project.Solution.Services.GetRequiredService<IFixAllGetFixesService>(); var fixAllContext = CreateFixAllContext(RefactorOrFixAllState, progressTracker, cancellationToken); progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultTitle())); return service.GetFixAllChangedSolutionAsync(fixAllContext); } /// <summary> /// Determine if the <see cref="IRefactorOrFixAllState.Provider"/> is an internal first-party provider or not. /// </summary> private static bool IsInternalProvider(IRefactorOrFixAllState fixAllState) { var exportAttributes = fixAllState.Provider.GetType().GetTypeInfo().GetCustomAttributes(typeof(ExportCodeFixProviderAttribute), false); if (exportAttributes?.FirstOrDefault() is ExportCodeFixProviderAttribute codeFixAttribute) { return !string.IsNullOrEmpty(codeFixAttribute.Name) && s_predefinedProviderNames.Contains(codeFixAttribute.Name); } exportAttributes = fixAllState.Provider.GetType().GetTypeInfo().GetCustomAttributes(typeof(ExportCodeRefactoringProviderAttribute), false); if (exportAttributes?.FirstOrDefault() is ExportCodeRefactoringProviderAttribute codeRefactoringAttribute) { return !string.IsNullOrEmpty(codeRefactoringAttribute.Name) && s_predefinedProviderNames.Contains(codeRefactoringAttribute.Name); } return false; } // internal for testing purposes. internal TestAccessor GetTestAccessor() => new(this); // internal for testing purposes. internal readonly struct TestAccessor { private readonly RefactorOrFixAllCodeAction _fixAllCodeAction; internal TestAccessor(RefactorOrFixAllCodeAction fixAllCodeAction) => _fixAllCodeAction = fixAllCodeAction; /// <summary> /// Gets a reference to <see cref="_showPreviewChangesDialog"/>, which can be read or written by test code. /// </summary> public ref bool ShowPreviewChangesDialog => ref _fixAllCodeAction._showPreviewChangesDialog; } }