File: src\Analyzers\Core\CodeFixes\OrderModifiers\AbstractOrderModifiersCodeFixProvider.cs
Web Access
Project: src\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.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.OrderModifiers;
 
internal abstract class AbstractOrderModifiersCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
    private readonly ISyntaxFacts _syntaxFacts;
    private readonly AbstractOrderModifiersHelpers _helpers;
 
    protected AbstractOrderModifiersCodeFixProvider(
        ISyntaxFacts syntaxFacts,
        AbstractOrderModifiersHelpers helpers)
    {
        _syntaxFacts = syntaxFacts;
        _helpers = helpers;
    }
 
    protected abstract ImmutableArray<string> FixableCompilerErrorIds { get; }
    protected abstract CodeStyleOption2<string> GetCodeStyleOption(AnalyzerOptionsProvider options);
 
    public sealed override ImmutableArray<string> FixableDiagnosticIds
        => FixableCompilerErrorIds.Add(IDEDiagnosticIds.OrderModifiersDiagnosticId);
 
    public override async Task RegisterCodeFixesAsync(CodeFixContext context)
    {
        var syntaxTree = await context.Document.GetRequiredSyntaxTreeAsync(context.CancellationToken).ConfigureAwait(false);
        var syntaxNode = Location.Create(syntaxTree, context.Span).FindNode(context.CancellationToken);
 
        if (_syntaxFacts.GetModifiers(syntaxNode) != default)
        {
            RegisterCodeFix(context, AnalyzersResources.Order_modifiers, nameof(AnalyzersResources.Order_modifiers));
        }
    }
 
    protected override async Task FixAllAsync(
        Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken)
    {
        var options = await document.GetAnalyzerOptionsProviderAsync(cancellationToken).ConfigureAwait(false);
        var option = GetCodeStyleOption(options);
        if (!_helpers.TryGetOrComputePreferredOrder(option.Value, out var preferredOrder))
        {
            return;
        }
 
        foreach (var diagnostic in diagnostics)
        {
            var memberDeclaration = diagnostic.Location.FindNode(cancellationToken);
 
            editor.ReplaceNode(memberDeclaration, (currentNode, _) =>
            {
                var modifiers = _syntaxFacts.GetModifiers(currentNode);
                var orderedModifiers = new SyntaxTokenList(
                    modifiers.OrderBy(CompareModifiers)
                             .Select((t, i) => t.WithTriviaFrom(modifiers[i])));
 
                var updatedMemberDeclaration = _syntaxFacts.WithModifiers(currentNode, orderedModifiers);
                return updatedMemberDeclaration;
            });
        }
 
        return;
 
        // Local functions
 
        int CompareModifiers(SyntaxToken t1, SyntaxToken t2)
            => GetOrder(t1) - GetOrder(t2);
 
        int GetOrder(SyntaxToken token)
            => preferredOrder.TryGetValue(token.RawKind, out var value) ? value : int.MaxValue;
    }
}