|
// 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;
}
}
}
|