File: AddImport\CodeActions\ProjectSymbolReferenceCodeAction.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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.AddImport;
 
internal abstract partial class AbstractAddImportFeatureService<TSimpleNameSyntax>
{
    /// <summary>
    /// Code action for adding an import when we find a symbol in source in either our
    /// starting project, or some other unreferenced project in the solution.  If we 
    /// find a source symbol in a different project, we'll also add a p2p reference when
    /// we apply the code action.
    /// </summary>
    private class ProjectSymbolReferenceCodeAction : SymbolReferenceCodeAction
    {
        /// <summary>
        /// This code action may or may not add a project reference.  If it does, it requires a non document change
        /// (and is thus restricted in which hosts it can run).  If it doesn't, it can run anywhere.
        /// </summary>
        public ProjectSymbolReferenceCodeAction(
            Document originalDocument,
            AddImportFixData fixData)
            : base(originalDocument,
                   fixData,
                   additionalTags: ShouldAddProjectReference(originalDocument, fixData) ? RequiresNonDocumentChangeTags : [])
        {
            Contract.ThrowIfFalse(fixData.Kind == AddImportFixKind.ProjectSymbol);
        }
 
        private static bool ShouldAddProjectReference(Document originalDocument, AddImportFixData fixData)
            => fixData.ProjectReferenceToAdd != null && fixData.ProjectReferenceToAdd != originalDocument.Project.Id;
 
        protected override Task<CodeActionOperation?> UpdateProjectAsync(Project project, bool isPreview, CancellationToken cancellationToken)
        {
            if (!ShouldAddProjectReference(this.OriginalDocument, this.FixData))
                return SpecializedTasks.Null<CodeActionOperation>();
 
            var projectWithAddedReference = project.AddProjectReference(new ProjectReference(FixData.ProjectReferenceToAdd));
            var applyOperation = new ApplyChangesOperation(projectWithAddedReference.Solution);
            if (isPreview)
            {
                return Task.FromResult<CodeActionOperation?>(applyOperation);
            }
 
            return Task.FromResult<CodeActionOperation?>(new AddProjectReferenceCodeActionOperation(OriginalDocument.Project.Id, FixData.ProjectReferenceToAdd, applyOperation));
        }
 
        private sealed class AddProjectReferenceCodeActionOperation(ProjectId referencingProject, ProjectId referencedProject, ApplyChangesOperation applyOperation) : CodeActionOperation
        {
            private readonly ProjectId _referencingProject = referencingProject;
            private readonly ProjectId _referencedProject = referencedProject;
            private readonly ApplyChangesOperation _applyOperation = applyOperation;
 
            internal override bool ApplyDuringTests => true;
 
            public override void Apply(Workspace workspace, CancellationToken cancellationToken)
            {
                if (!CanApply(workspace))
                    return;
 
                _applyOperation.Apply(workspace, cancellationToken);
            }
 
            internal override Task<bool> TryApplyAsync(
                Workspace workspace, Solution originalSolution, IProgress<CodeAnalysisProgress> progressTracker, CancellationToken cancellationToken)
            {
                if (!CanApply(workspace))
                    return SpecializedTasks.False;
 
                return _applyOperation.TryApplyAsync(workspace, originalSolution, progressTracker, cancellationToken);
            }
 
            private bool CanApply(Workspace workspace)
            {
                return workspace.CanAddProjectReference(_referencingProject, _referencedProject);
            }
        }
    }
}