File: CodeRefactorings\FixAllOccurences\RefactorAllState.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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixesAndRefactorings;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CodeRefactorings;
 
internal sealed class RefactorAllState : CommonFixAllState<CodeRefactoringProvider, RefactorAllProvider, RefactorAllState>
{
    /// <summary>
    /// Original selection span from which refactor-all was invoked. This is used in <see
    /// cref="GetRefactorAllSpansAsync(CancellationToken)"/> to compute refactor all spans for <see
    /// cref="RefactorAllScope.ContainingMember"/> and <see cref="RefactorAllScope.ContainingType"/> scopes.
    /// </summary>
    private readonly TextSpan _selectionSpan;
 
    public override FixAllKind FixAllKind => FixAllKind.Refactoring;
 
    public string CodeActionTitle { get; }
 
    public RefactorAllState(
        RefactorAllProvider refactorAllProvider,
        Document document,
        TextSpan selectionSpan,
        CodeRefactoringProvider codeRefactoringProvider,
        RefactorAllScope refactorAllScope,
        CodeAction codeAction)
        : this(refactorAllProvider, document ?? throw new ArgumentNullException(nameof(document)), document.Project, selectionSpan, codeRefactoringProvider,
               refactorAllScope, codeAction.Title, codeAction.EquivalenceKey)
    {
    }
 
    public RefactorAllState(
        RefactorAllProvider refactorAllProvider,
        Project project,
        TextSpan selectionSpan,
        CodeRefactoringProvider codeRefactoringProvider,
        RefactorAllScope refactorAllScope,
        CodeAction codeAction)
        : this(refactorAllProvider, document: null, project ?? throw new ArgumentNullException(nameof(project)), selectionSpan, codeRefactoringProvider,
               refactorAllScope, codeAction.Title, codeAction.EquivalenceKey)
    {
    }
 
    private RefactorAllState(
        RefactorAllProvider refactorAllProvider,
        Document? document,
        Project project,
        TextSpan selectionSpan,
        CodeRefactoringProvider codeRefactoringProvider,
        RefactorAllScope refactorAllScope,
        string codeActionTitle,
        string? codeActionEquivalenceKey)
        : base(refactorAllProvider, document, project, codeRefactoringProvider, refactorAllScope.ToFixAllScope(), codeActionEquivalenceKey)
    {
        _selectionSpan = selectionSpan;
        this.CodeActionTitle = codeActionTitle;
    }
 
    protected override RefactorAllState With(Document? document, Project project, FixAllScope scope, string? codeActionEquivalenceKey)
    {
        return new RefactorAllState(
            this.FixAllProvider,
            document,
            project,
            _selectionSpan,
            this.Provider,
            scope.ToRefactorAllScope(),
            this.CodeActionTitle,
            codeActionEquivalenceKey);
    }
 
    /// <summary>
    /// Gets the spans to refactor by document for the <see cref="RefactorAllScope"/> for this refactor all occurrences
    /// action. If no spans are specified, it indicates the entire document needs to be refactored.
    /// </summary>
    internal async Task<ImmutableDictionary<Document, Optional<ImmutableArray<TextSpan>>>> GetRefactorAllSpansAsync(CancellationToken cancellationToken)
    {
        IEnumerable<Document>? documentsToRefactor = null;
        switch (this.Scope.ToRefactorAllScope())
        {
            case RefactorAllScope.ContainingType or RefactorAllScope.ContainingMember:
                Contract.ThrowIfNull(Document);
                var spanMappingService = Document.GetLanguageService<IFixAllSpanMappingService>();
                if (spanMappingService is null)
                    return ImmutableDictionary<Document, Optional<ImmutableArray<TextSpan>>>.Empty;
 
                var spansByDocument = await spanMappingService.GetFixAllSpansAsync(
                    Document, _selectionSpan, Scope, cancellationToken).ConfigureAwait(false);
                return spansByDocument.Select(kvp => KeyValuePair.Create(kvp.Key, new Optional<ImmutableArray<TextSpan>>(kvp.Value)))
                    .ToImmutableDictionaryOrEmpty();
 
            case RefactorAllScope.Document:
                Contract.ThrowIfNull(Document);
                documentsToRefactor = [Document];
                break;
 
            case RefactorAllScope.Project:
                documentsToRefactor = Project.Documents;
                break;
 
            case RefactorAllScope.Solution:
                documentsToRefactor = Project.Solution.Projects.SelectMany(p => p.Documents);
                break;
 
            default:
                return ImmutableDictionary<Document, Optional<ImmutableArray<TextSpan>>>.Empty;
        }
 
        return documentsToRefactor.ToImmutableDictionary(d => d, _ => default(Optional<ImmutableArray<TextSpan>>));
    }
}