File: CodeRefactorings\SyntaxEditorBasedCodeRefactoringProvider.cs
Web Access
Project: src\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.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.CodeRefactorings;
 
internal abstract partial class SyntaxEditorBasedCodeRefactoringProvider : CodeRefactoringProvider
{
    protected static readonly ImmutableArray<RefactorAllScope> DefaultRefactorAllScopes = [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution];
    protected static readonly ImmutableArray<RefactorAllScope> AllRefactorAllScopes = [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution, RefactorAllScope.ContainingType, RefactorAllScope.ContainingMember];
 
    protected abstract ImmutableArray<RefactorAllScope> SupportedRefactorAllScopes { get; }
    protected virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default;
 
    public sealed override RefactorAllProvider? GetRefactorAllProvider()
    {
        if (SupportedRefactorAllScopes.IsEmpty)
            return null;
 
        return RefactorAllProvider.Create(
            async (refactorAllContext, document, refactorAllSpans) =>
                await this.RefactorAllAsync(document, refactorAllSpans, refactorAllContext.CodeActionEquivalenceKey, refactorAllContext.CancellationToken).ConfigureAwait(false),
            SupportedRefactorAllScopes,
            this.Cleanup);
    }
 
    protected Task<Document> RefactorAsync(
        Document document,
        TextSpan refactorAllSpan,
        string? equivalenceKey,
        CancellationToken cancellationToken)
    {
        return RefactorAllWithEditorAsync(document,
            editor => RefactorAllAsync(document, [refactorAllSpan], editor, equivalenceKey, cancellationToken),
            cancellationToken);
    }
 
    protected Task<Document> RefactorAllAsync(
        Document document,
        Optional<ImmutableArray<TextSpan>> refactorAllSpans,
        string? equivalenceKey,
        CancellationToken cancellationToken)
    {
        return RefactorAllWithEditorAsync(document, RefactorAllAsync, cancellationToken);
 
        // Local functions
        Task RefactorAllAsync(SyntaxEditor editor)
        {
            // Refactor the entire document if there are no sub-spans to refactor.
            var spans = refactorAllSpans.HasValue ? refactorAllSpans.Value : [editor.OriginalRoot.FullSpan];
            return this.RefactorAllAsync(document, spans, editor, equivalenceKey, cancellationToken);
        }
    }
 
    internal static async Task<Document> RefactorAllWithEditorAsync(
        Document document,
        Func<SyntaxEditor, Task> editAsync,
        CancellationToken cancellationToken)
    {
        var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
        var editor = new SyntaxEditor(root, document.Project.Solution.Services);
 
        await editAsync(editor).ConfigureAwait(false);
 
        var newRoot = editor.GetChangedRoot();
        return document.WithSyntaxRoot(newRoot);
    }
 
    protected abstract Task RefactorAllAsync(
        Document document,
        ImmutableArray<TextSpan> refactorAllSpans,
        SyntaxEditor editor,
        string? equivalenceKey,
        CancellationToken cancellationToken);
}