File: GenerateType\AbstractGenerateTypeService.CodeAction.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.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.ProjectManagement;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.GenerateType;
 
internal abstract partial class AbstractGenerateTypeService<TService, TSimpleNameSyntax, TObjectCreationExpressionSyntax, TExpressionSyntax, TTypeDeclarationSyntax, TArgumentSyntax>
{
    private sealed class GenerateTypeCodeAction : CodeAction
    {
        private readonly bool _intoNamespace;
        private readonly bool _inNewFile;
        private readonly TService _service;
        private readonly Document _document;
        private readonly State _state;
        private readonly string _equivalenceKey;
 
        public GenerateTypeCodeAction(
            TService service,
            Document document,
            State state,
            bool intoNamespace,
            bool inNewFile)
        {
            _service = service;
            _document = document;
            _state = state;
            _intoNamespace = intoNamespace;
            _inNewFile = inNewFile;
            _equivalenceKey = Title;
        }
 
        private static string FormatDisplayText(
            State state,
            bool inNewFile,
            bool isNested)
        {
            if (inNewFile)
            {
                return string.Format(FeaturesResources.Generate_0_1_in_new_file,
                    state.IsStruct ? "struct" : state.IsInterface ? "interface" : "class",
                    state.Name);
            }
            else
            {
                return string.Format(isNested ? FeaturesResources.Generate_nested_0_1 : FeaturesResources.Generate_0_1,
                    state.IsStruct ? "struct" : state.IsInterface ? "interface" : "class",
                    state.Name);
            }
        }
 
        protected override async Task<ImmutableArray<CodeActionOperation>> ComputeOperationsAsync(
            IProgress<CodeAnalysisProgress> progress, CancellationToken cancellationToken)
        {
            var semanticDocument = await SemanticDocument.CreateAsync(_document, cancellationToken).ConfigureAwait(false);
 
            var editor = new Editor(_service, semanticDocument, _state, _intoNamespace, _inNewFile, cancellationToken);
 
            return await editor.GetOperationsAsync().ConfigureAwait(false);
        }
 
        public override string Title
            => _intoNamespace
                ? FormatDisplayText(_state, _inNewFile, isNested: false)
                : FormatDisplayText(_state, inNewFile: false, isNested: true);
 
        public override string EquivalenceKey => _equivalenceKey;
    }
 
    private sealed class GenerateTypeCodeActionWithOption : CodeActionWithOptions
    {
        private readonly TService _service;
        private readonly Document _document;
        private readonly State _state;
 
        internal GenerateTypeCodeActionWithOption(TService service, Document document, State state)
        {
            _service = service;
            _document = document;
            _state = state;
        }
 
        public override string Title => FeaturesResources.Generate_new_type;
 
        public override string EquivalenceKey => _state.Name;
 
        public override object GetOptions(CancellationToken cancellationToken)
        {
            var generateTypeOptionsService = _document.Project.Solution.Services.GetRequiredService<IGenerateTypeOptionsService>();
            var notificationService = _document.Project.Solution.Services.GetService<INotificationService>();
            var projectManagementService = _document.Project.Solution.Services.GetService<IProjectManagementService>();
            var syntaxFactsService = _document.GetLanguageService<ISyntaxFactsService>();
            var typeKindValue = GetTypeKindOption(_state);
            var isPublicOnlyAccessibility = IsPublicOnlyAccessibility(_state, _document.Project);
            return generateTypeOptionsService.GetGenerateTypeOptions(
                _state.Name,
                new GenerateTypeDialogOptions(isPublicOnlyAccessibility, typeKindValue, _state.IsAttribute),
                _document,
                notificationService,
                projectManagementService,
                syntaxFactsService);
        }
 
        private bool IsPublicOnlyAccessibility(State state, Project project)
            => _service.IsPublicOnlyAccessibility(state.NameOrMemberAccessExpression, project) || _service.IsPublicOnlyAccessibility(state.SimpleName, project);
 
        private TypeKindOptions GetTypeKindOption(State state)
        {
            var gotPreassignedTypeOptions = GetPredefinedTypeKindOption(state, out var typeKindValue);
            if (!gotPreassignedTypeOptions)
            {
                typeKindValue = state.IsSimpleNameGeneric ? TypeKindOptionsHelper.RemoveOptions(typeKindValue, TypeKindOptions.GenericInCompatibleTypes) : typeKindValue;
                typeKindValue = state.IsMembersWithModule ? TypeKindOptionsHelper.AddOption(typeKindValue, TypeKindOptions.Module) : typeKindValue;
                typeKindValue = state.IsInterfaceOrEnumNotAllowedInTypeContext ? TypeKindOptionsHelper.RemoveOptions(typeKindValue, TypeKindOptions.Interface, TypeKindOptions.Enum) : typeKindValue;
                typeKindValue = state.IsDelegateAllowed ? typeKindValue : TypeKindOptionsHelper.RemoveOptions(typeKindValue, TypeKindOptions.Delegate);
                typeKindValue = state.IsEnumNotAllowed ? TypeKindOptionsHelper.RemoveOptions(typeKindValue, TypeKindOptions.Enum) : typeKindValue;
            }
 
            return typeKindValue;
        }
 
        private bool GetPredefinedTypeKindOption(State state, out TypeKindOptions typeKindValueFinal)
        {
            if (state.IsAttribute)
            {
                typeKindValueFinal = TypeKindOptions.Attribute;
                return true;
            }
 
            if (_service.TryGetBaseList(state.NameOrMemberAccessExpression, out var typeKindValue))
            {
                typeKindValueFinal = typeKindValue;
                return true;
            }
 
            if (state.IsClassInterfaceTypes)
            {
                typeKindValueFinal = TypeKindOptions.BaseList;
                return true;
            }
 
            if (state.IsDelegateOnly)
            {
                typeKindValueFinal = TypeKindOptions.Delegate;
                return true;
            }
 
            if (state.IsTypeGeneratedIntoNamespaceFromMemberAccess)
            {
                typeKindValueFinal = state.IsSimpleNameGeneric ? TypeKindOptionsHelper.RemoveOptions(TypeKindOptions.MemberAccessWithNamespace, TypeKindOptions.GenericInCompatibleTypes) : TypeKindOptions.MemberAccessWithNamespace;
                typeKindValueFinal = state.IsEnumNotAllowed ? TypeKindOptionsHelper.RemoveOptions(typeKindValueFinal, TypeKindOptions.Enum) : typeKindValueFinal;
                return true;
            }
 
            typeKindValueFinal = TypeKindOptions.AllOptions;
            return false;
        }
 
        protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(
            object options, IProgress<CodeAnalysisProgress> progressTracker, CancellationToken cancellationToken)
        {
            var operations = SpecializedCollections.EmptyEnumerable<CodeActionOperation>();
 
            if (options is GenerateTypeOptionsResult generateTypeOptions && !generateTypeOptions.IsCancelled)
            {
                var semanticDocument = await SemanticDocument.CreateAsync(_document, cancellationToken).ConfigureAwait(false);
                var editor = new Editor(_service, semanticDocument, _state, fromDialog: true, generateTypeOptions, cancellationToken);
                operations = await editor.GetOperationsAsync().ConfigureAwait(false);
            }
 
            return operations;
        }
    }
}