|
// 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.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Metadata;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeGeneration;
internal abstract partial class AbstractCodeGenerationService<TCodeGenerationContextInfo> : ICodeGenerationService
where TCodeGenerationContextInfo : CodeGenerationContextInfo
{
private readonly ISymbolDeclarationService _symbolDeclarationService;
protected AbstractCodeGenerationService(
LanguageServices languageServices)
{
LanguageServices = languageServices;
_symbolDeclarationService = languageServices.GetRequiredService<ISymbolDeclarationService>();
}
public LanguageServices LanguageServices { get; }
public abstract CodeGenerationOptions DefaultOptions { get; }
public abstract CodeGenerationOptions GetCodeGenerationOptions(IOptionsReader options);
public abstract TCodeGenerationContextInfo GetInfo(CodeGenerationContext context, CodeGenerationOptions options, ParseOptions parseOptions);
CodeGenerationContextInfo ICodeGenerationService.GetInfo(CodeGenerationContext context, CodeGenerationOptions options, ParseOptions parseOptions)
=> GetInfo(context, options, parseOptions);
#region ICodeGenerationService
public TDeclarationNode AddEvent<TDeclarationNode>(TDeclarationNode destination, IEventSymbol @event, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> WithAnnotations(AddEvent(destination, @event, (TCodeGenerationContextInfo)info, GetAvailableInsertionIndices(destination, cancellationToken), cancellationToken), info);
public TDeclarationNode AddField<TDeclarationNode>(TDeclarationNode destination, IFieldSymbol field, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> WithAnnotations(AddField(destination, field, (TCodeGenerationContextInfo)info, GetAvailableInsertionIndices(destination, cancellationToken), cancellationToken), info);
public TDeclarationNode AddMethod<TDeclarationNode>(TDeclarationNode destination, IMethodSymbol method, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> WithAnnotations(AddMethod(destination, method, (TCodeGenerationContextInfo)info, GetAvailableInsertionIndices(destination, cancellationToken), cancellationToken), info);
public TDeclarationNode AddProperty<TDeclarationNode>(TDeclarationNode destination, IPropertySymbol property, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> WithAnnotations(AddProperty(destination, property, (TCodeGenerationContextInfo)info, GetAvailableInsertionIndices(destination, cancellationToken), cancellationToken), info);
public TDeclarationNode AddNamedType<TDeclarationNode>(TDeclarationNode destination, INamedTypeSymbol namedType, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> WithAnnotations(AddNamedType(destination, namedType, (TCodeGenerationContextInfo)info, GetAvailableInsertionIndices(destination, cancellationToken), cancellationToken), info);
public TDeclarationNode AddNamespace<TDeclarationNode>(TDeclarationNode destination, INamespaceSymbol @namespace, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> WithAnnotations(AddNamespace(destination, @namespace, (TCodeGenerationContextInfo)info, GetAvailableInsertionIndices(destination, cancellationToken), cancellationToken), info);
public TDeclarationNode AddMembers<TDeclarationNode>(TDeclarationNode destination, IEnumerable<ISymbol> members, CodeGenerationContextInfo info, CancellationToken cancellationToken)
where TDeclarationNode : SyntaxNode
=> WithAnnotations(AddMembers(destination, members, GetAvailableInsertionIndices(destination, cancellationToken), (TCodeGenerationContextInfo)info, cancellationToken), info);
private static TNode WithAnnotations<TNode>(TNode node, CodeGenerationContextInfo info) where TNode : SyntaxNode
{
return info.Context.AddImports
? node.WithAdditionalAnnotations(Simplifier.AddImportsAnnotation)
: node;
}
public SyntaxNode CreateEventDeclaration(IEventSymbol @event, CodeGenerationDestination destination, CodeGenerationContextInfo info, CancellationToken cancellationToken)
=> CreateEventDeclaration(@event, destination, (TCodeGenerationContextInfo)info, cancellationToken);
public SyntaxNode CreateFieldDeclaration(IFieldSymbol field, CodeGenerationDestination destination, CodeGenerationContextInfo info, CancellationToken cancellationToken)
=> CreateFieldDeclaration(field, destination, (TCodeGenerationContextInfo)info, cancellationToken);
public SyntaxNode? CreateMethodDeclaration(IMethodSymbol method, CodeGenerationDestination destination, CodeGenerationContextInfo info, CancellationToken cancellationToken)
=> CreateMethodDeclaration(method, destination, (TCodeGenerationContextInfo)info, cancellationToken);
public SyntaxNode CreatePropertyDeclaration(IPropertySymbol property, CodeGenerationDestination destination, CodeGenerationContextInfo info, CancellationToken cancellationToken)
=> CreatePropertyDeclaration(property, destination, (TCodeGenerationContextInfo)info, cancellationToken);
public SyntaxNode CreateNamedTypeDeclaration(INamedTypeSymbol namedType, CodeGenerationDestination destination, CodeGenerationContextInfo info, CancellationToken cancellationToken)
=> CreateNamedTypeDeclaration(namedType, destination, (TCodeGenerationContextInfo)info, cancellationToken);
public SyntaxNode CreateNamespaceDeclaration(INamespaceSymbol @namespace, CodeGenerationDestination destination, CodeGenerationContextInfo info, CancellationToken cancellationToken)
=> CreateNamespaceDeclaration(@namespace, destination, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode AddParameters<TDeclarationNode>(TDeclarationNode destination, IEnumerable<IParameterSymbol> parameters, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> AddParameters(destination, parameters, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode AddAttributes<TDeclarationNode>(TDeclarationNode destination, IEnumerable<AttributeData> attributes, SyntaxToken? target, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> AddAttributes(destination, attributes, target, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode RemoveAttribute<TDeclarationNode>(TDeclarationNode destination, SyntaxNode attributeToRemove, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> RemoveAttribute(destination, attributeToRemove, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode RemoveAttribute<TDeclarationNode>(TDeclarationNode destination, AttributeData attributeToRemove, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> RemoveAttribute(destination, attributeToRemove, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode UpdateDeclarationModifiers<TDeclarationNode>(TDeclarationNode declaration, IEnumerable<SyntaxToken> newModifiers, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> UpdateDeclarationModifiers(declaration, newModifiers, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode UpdateDeclarationAccessibility<TDeclarationNode>(TDeclarationNode declaration, Accessibility newAccessibility, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> UpdateDeclarationAccessibility(declaration, newAccessibility, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode UpdateDeclarationType<TDeclarationNode>(TDeclarationNode declaration, ITypeSymbol newType, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> UpdateDeclarationType(declaration, newType, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode UpdateDeclarationMembers<TDeclarationNode>(TDeclarationNode declaration, IList<ISymbol> newMembers, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> UpdateDeclarationMembers(declaration, newMembers, (TCodeGenerationContextInfo)info, cancellationToken);
public TDeclarationNode AddStatements<TDeclarationNode>(TDeclarationNode destination, IEnumerable<SyntaxNode> statements, CodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
=> AddStatements(destination, statements, (TCodeGenerationContextInfo)info, cancellationToken);
#endregion
protected abstract TDeclarationNode AddEvent<TDeclarationNode>(TDeclarationNode destination, IEventSymbol @event, TCodeGenerationContextInfo info, IList<bool>? availableIndices, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddField<TDeclarationNode>(TDeclarationNode destination, IFieldSymbol field, TCodeGenerationContextInfo info, IList<bool>? availableIndices, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddMethod<TDeclarationNode>(TDeclarationNode destination, IMethodSymbol method, TCodeGenerationContextInfo info, IList<bool>? availableIndices, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddProperty<TDeclarationNode>(TDeclarationNode destination, IPropertySymbol property, TCodeGenerationContextInfo info, IList<bool>? availableIndices, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddNamedType<TDeclarationNode>(TDeclarationNode destination, INamedTypeSymbol namedType, TCodeGenerationContextInfo info, IList<bool>? availableIndices, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddNamespace<TDeclarationNode>(TDeclarationNode destination, INamespaceSymbol @namespace, TCodeGenerationContextInfo info, IList<bool>? availableIndices, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddMembers<TDeclarationNode>(TDeclarationNode destination, IEnumerable<SyntaxNode> members) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode AddParameters<TDeclarationNode>(TDeclarationNode destinationMember, IEnumerable<IParameterSymbol> parameters, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode AddAttributes<TDeclarationNode>(TDeclarationNode destination, IEnumerable<AttributeData> attributes, SyntaxToken? target, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode RemoveAttribute<TDeclarationNode>(TDeclarationNode destination, SyntaxNode attributeToRemove, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode RemoveAttribute<TDeclarationNode>(TDeclarationNode destination, AttributeData attributeToRemove, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode AddStatements<TDeclarationNode>(TDeclarationNode destinationMember, IEnumerable<SyntaxNode> statements, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode UpdateDeclarationModifiers<TDeclarationNode>(TDeclarationNode declaration, IEnumerable<SyntaxToken> newModifiers, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode UpdateDeclarationAccessibility<TDeclarationNode>(TDeclarationNode declaration, Accessibility newAccessibility, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode UpdateDeclarationType<TDeclarationNode>(TDeclarationNode declaration, ITypeSymbol newType, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode UpdateDeclarationMembers<TDeclarationNode>(TDeclarationNode declaration, IList<ISymbol> newMembers, TCodeGenerationContextInfo info, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract CodeGenerationDestination GetDestination(SyntaxNode node);
public abstract SyntaxNode CreateEventDeclaration(IEventSymbol @event, CodeGenerationDestination destination, TCodeGenerationContextInfo info, CancellationToken cancellationToken);
public abstract SyntaxNode CreateFieldDeclaration(IFieldSymbol field, CodeGenerationDestination destination, TCodeGenerationContextInfo info, CancellationToken cancellationToken);
// TODO: Change to not return null (https://github.com/dotnet/roslyn/issues/58243)
public abstract SyntaxNode? CreateMethodDeclaration(IMethodSymbol method, CodeGenerationDestination destination, TCodeGenerationContextInfo info, CancellationToken cancellationToken);
public abstract SyntaxNode CreatePropertyDeclaration(IPropertySymbol property, CodeGenerationDestination destination, TCodeGenerationContextInfo info, CancellationToken cancellationToken);
public abstract SyntaxNode CreateNamedTypeDeclaration(INamedTypeSymbol namedType, CodeGenerationDestination destination, TCodeGenerationContextInfo info, CancellationToken cancellationToken);
public abstract SyntaxNode CreateNamespaceDeclaration(INamespaceSymbol @namespace, CodeGenerationDestination destination, TCodeGenerationContextInfo info, CancellationToken cancellationToken);
protected static T Cast<T>(object value)
=> (T)value;
protected static void CheckDeclarationNode<TDeclarationNode>(SyntaxNode destination) where TDeclarationNode : SyntaxNode
{
if (destination == null)
{
throw new ArgumentNullException(nameof(destination));
}
if (destination is not TDeclarationNode)
{
throw new ArgumentException(
string.Format(WorkspaceExtensionsResources.Destination_type_must_be_a_0_but_given_one_is_1, typeof(TDeclarationNode).Name, destination.GetType().Name),
nameof(destination));
}
}
protected static void CheckDeclarationNode<TDeclarationNode1, TDeclarationNode2>(SyntaxNode destination)
where TDeclarationNode1 : SyntaxNode
where TDeclarationNode2 : SyntaxNode
{
if (destination == null)
{
throw new ArgumentNullException(nameof(destination));
}
if (destination is not TDeclarationNode1 and
not TDeclarationNode2)
{
throw new ArgumentException(
string.Format(WorkspaceExtensionsResources.Destination_type_must_be_a_0_or_a_1_but_given_one_is_2,
typeof(TDeclarationNode1).Name, typeof(TDeclarationNode2).Name, destination.GetType().Name),
nameof(destination));
}
}
protected static void CheckDeclarationNode<TDeclarationNode1, TDeclarationNode2, TDeclarationNode3>(SyntaxNode destination)
where TDeclarationNode1 : SyntaxNode
where TDeclarationNode2 : SyntaxNode
where TDeclarationNode3 : SyntaxNode
{
if (destination == null)
{
throw new ArgumentNullException(nameof(destination));
}
if (destination is not TDeclarationNode1 and
not TDeclarationNode2 and
not TDeclarationNode3)
{
throw new ArgumentException(
string.Format(WorkspaceExtensionsResources.Destination_type_must_be_a_0_1_or_2_but_given_one_is_3,
typeof(TDeclarationNode1).Name, typeof(TDeclarationNode2).Name, typeof(TDeclarationNode3).Name, destination.GetType().Name),
nameof(destination));
}
}
protected static void CheckDeclarationNode<TDeclarationNode1, TDeclarationNode2, TDeclarationNode3, TDeclarationNode4>(SyntaxNode destination)
where TDeclarationNode1 : SyntaxNode
where TDeclarationNode2 : SyntaxNode
where TDeclarationNode3 : SyntaxNode
where TDeclarationNode4 : SyntaxNode
{
if (destination is not TDeclarationNode1 and
not TDeclarationNode2 and
not TDeclarationNode3 and
not TDeclarationNode4)
{
throw new ArgumentException(
string.Format(WorkspaceExtensionsResources.Destination_type_must_be_a_0_1_2_or_3_but_given_one_is_4,
typeof(TDeclarationNode1).Name, typeof(TDeclarationNode2).Name, typeof(TDeclarationNode3).Name, typeof(TDeclarationNode4).Name, destination.GetType().Name),
nameof(destination));
}
}
private async Task<Document> GetEditAsync(
CodeGenerationSolutionContext context,
INamespaceOrTypeSymbol destination,
Func<SyntaxNode, TCodeGenerationContextInfo, IList<bool>?, CancellationToken, SyntaxNode> declarationTransform,
CancellationToken cancellationToken)
{
var (destinationDeclaration, availableIndices) =
await FindMostRelevantDeclarationAsync(context.Solution, destination, context.Context.BestLocation, cancellationToken).ConfigureAwait(false);
if (destinationDeclaration == null)
throw new ArgumentException(WorkspaceExtensionsResources.Could_not_find_location_to_generation_symbol_into);
var destinationTree = destinationDeclaration.SyntaxTree;
var oldDocument = context.Solution.GetRequiredDocument(destinationTree);
var codeGenOptions = await oldDocument.GetCodeGenerationOptionsAsync(cancellationToken).ConfigureAwait(false);
var info = GetInfo(context.Context, codeGenOptions, destinationDeclaration.SyntaxTree.Options);
var transformedDeclaration = declarationTransform(destinationDeclaration, info, availableIndices, cancellationToken);
var root = await destinationTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var currentRoot = root.ReplaceNode(destinationDeclaration, transformedDeclaration);
var newDocument = oldDocument.WithSyntaxRoot(currentRoot);
if (context.Context.AddImports)
{
var addImportsOptions = await newDocument.GetAddImportPlacementOptionsAsync(cancellationToken).ConfigureAwait(false);
var service = newDocument.GetRequiredLanguageService<ImportAdderService>();
newDocument = await service.AddImportsAsync(
newDocument,
[currentRoot.FullSpan],
ImportAdderService.Strategy.AddImportsFromSymbolAnnotations,
addImportsOptions,
cancellationToken).ConfigureAwait(false);
}
return newDocument;
}
protected TDeclarationNode AddMembers<TDeclarationNode>(
TDeclarationNode destination,
IEnumerable<ISymbol> members,
IList<bool>? availableIndices,
TCodeGenerationContextInfo info,
CancellationToken cancellationToken)
where TDeclarationNode : SyntaxNode
{
var membersList = members.ToList();
if (membersList.Count > 1)
{
info = CreateContextInfoForMultipleMembers(info);
}
// Filter out the members that are implicitly declared. They're implicit, hence we do
// not want an explicit declaration. The only exception are fields generated from implicit tuple fields.
var filteredMembers = membersList.Where(m => !m.IsImplicitlyDeclared || m.IsTupleField());
return info.Context.AutoInsertionLocation
? AddMembersToAppropriateLocationInDestination(destination, filteredMembers, availableIndices, info, cancellationToken)
: AddMembersToEndOfDestination(destination, filteredMembers, info, cancellationToken);
}
private TDeclarationSyntax AddMembersToEndOfDestination<TDeclarationSyntax>(
TDeclarationSyntax destination,
IEnumerable<ISymbol> members,
TCodeGenerationContextInfo info,
CancellationToken cancellationToken)
where TDeclarationSyntax : SyntaxNode
{
var newMembers = new List<SyntaxNode>();
var codeGenerationDestination = GetDestination(destination);
foreach (var member in members)
{
cancellationToken.ThrowIfCancellationRequested();
var newMember = GetNewMember(info, codeGenerationDestination, member, cancellationToken);
if (newMember != null)
{
newMembers.Add(newMember);
}
}
// Metadata as source generates complete declarations and doesn't modify
// existing ones. We can take the members to generate, sort them once,
// and then add them in that order to the end of the destination.
if (!GeneratingEnum(members) && info.Context.SortMembers)
{
newMembers.Sort(GetMemberComparer());
}
return this.AddMembers(destination, newMembers);
}
private TDeclarationSyntax AddMembersToAppropriateLocationInDestination<TDeclarationSyntax>(
TDeclarationSyntax destination,
IEnumerable<ISymbol> members,
IList<bool>? availableIndices,
TCodeGenerationContextInfo info,
CancellationToken cancellationToken)
where TDeclarationSyntax : SyntaxNode
{
var currentDestination = destination;
foreach (var member in members)
{
cancellationToken.ThrowIfCancellationRequested();
currentDestination = UpdateDestination(availableIndices, info, currentDestination, member, cancellationToken);
}
return currentDestination;
}
private SyntaxNode? GetNewMember(TCodeGenerationContextInfo info, CodeGenerationDestination codeGenerationDestination, ISymbol member, CancellationToken cancellationToken)
=> member switch
{
IEventSymbol @event => CreateEventDeclaration(@event, codeGenerationDestination, info, cancellationToken),
IFieldSymbol field => CreateFieldDeclaration(field, codeGenerationDestination, info, cancellationToken),
IPropertySymbol property => CreatePropertyDeclaration(property, codeGenerationDestination, info, cancellationToken),
IMethodSymbol method => CreateMethodDeclaration(method, codeGenerationDestination, info, cancellationToken),
INamedTypeSymbol namedType => CreateNamedTypeDeclaration(namedType, codeGenerationDestination, info, cancellationToken),
INamespaceSymbol @namespace => CreateNamespaceDeclaration(@namespace, codeGenerationDestination, info, cancellationToken),
_ => null,
};
private TDeclarationNode UpdateDestination<TDeclarationNode>(
IList<bool>? availableIndices,
TCodeGenerationContextInfo info,
TDeclarationNode currentDestination,
ISymbol member,
CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode
{
return member switch
{
IEventSymbol @event => AddEvent(currentDestination, @event, info, availableIndices, cancellationToken),
IFieldSymbol field => AddField(currentDestination, field, info, availableIndices, cancellationToken),
IPropertySymbol property => AddProperty(currentDestination, property, info, availableIndices, cancellationToken),
IMethodSymbol method => AddMethod(currentDestination, method, info, availableIndices, cancellationToken),
INamedTypeSymbol namedType => AddNamedType(currentDestination, namedType, info, availableIndices, cancellationToken),
INamespaceSymbol @namespace => AddNamespace(currentDestination, @namespace, info, availableIndices, cancellationToken),
_ => currentDestination,
};
}
private static bool GeneratingEnum(IEnumerable<ISymbol> members)
{
var field = members.OfType<IFieldSymbol>().FirstOrDefault();
return field != null && field.ContainingType.IsEnumType();
}
protected abstract IComparer<SyntaxNode> GetMemberComparer();
protected static TCodeGenerationContextInfo CreateContextInfoForMultipleMembers(TCodeGenerationContextInfo info)
{
// For now we ignore the afterThisLocation/beforeThisLocation if we're adding
// multiple members. In the future it would be nice to appropriately handle this.
// The difficulty lies with ensuring that we properly understand the position we're
// inserting into, even as we change the type by adding multiple members. Not
// impossible to figure out, but out of scope right now.
return (TCodeGenerationContextInfo)info.WithContext(info.Context.With(afterThisLocation: null, beforeThisLocation: null));
}
public virtual Task<Document> AddEventAsync(
CodeGenerationSolutionContext context, INamedTypeSymbol destination, IEventSymbol @event, CancellationToken cancellationToken)
{
return GetEditAsync(
context,
destination,
(t, opts, ai, ct) => AddEvent(t, @event, opts, ai, ct),
cancellationToken);
}
public Task<Document> AddFieldAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IFieldSymbol field, CancellationToken cancellationToken)
{
return GetEditAsync(
context,
destination,
(t, opts, ai, ct) => AddField(t, field, opts, ai, ct),
cancellationToken);
}
public Task<Document> AddPropertyAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IPropertySymbol property, CancellationToken cancellationToken)
{
return GetEditAsync(
context,
destination,
(t, opts, ai, ct) => AddProperty(t, property, opts, ai, ct),
cancellationToken);
}
public Task<Document> AddNamedTypeAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, INamedTypeSymbol namedType, CancellationToken cancellationToken)
{
return GetEditAsync(
context,
destination,
(t, opts, ai, ct) => AddNamedType(t, namedType, opts, ai, ct),
cancellationToken);
}
public Task<Document> AddNamedTypeAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamedTypeSymbol namedType, CancellationToken cancellationToken)
{
return GetEditAsync(
context,
destination,
(t, opts, ai, ct) => AddNamedType(t, namedType, opts, ai, ct),
cancellationToken);
}
public Task<Document> AddNamespaceAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamespaceSymbol @namespace, CancellationToken cancellationToken)
{
return GetEditAsync(
context,
destination,
(t, opts, ai, ct) => AddNamespace(t, @namespace, opts, ai, ct),
cancellationToken);
}
public Task<Document> AddMethodAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IMethodSymbol method, CancellationToken cancellationToken)
{
return GetEditAsync(
context,
destination,
(t, opts, ai, ct) => AddMethod(t, method, opts, ai, ct),
cancellationToken);
}
public Task<Document> AddMembersAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IEnumerable<ISymbol> members, CancellationToken cancellationToken)
{
return GetEditAsync(
context,
destination,
(t, opts, ai, ct) => AddMembers(t, members, ai, opts, ct),
cancellationToken);
}
public Task<Document> AddNamespaceOrTypeAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamespaceOrTypeSymbol namespaceOrType, CancellationToken cancellationToken)
{
if (namespaceOrType == null)
{
throw new ArgumentNullException(nameof(namespaceOrType));
}
if (namespaceOrType is INamespaceSymbol namespaceSymbol)
{
return AddNamespaceAsync(context, destination, namespaceSymbol, cancellationToken);
}
else
{
return AddNamedTypeAsync(context, destination, (INamedTypeSymbol)namespaceOrType, cancellationToken);
}
}
protected static void CheckLocation(SyntaxNode destinationMember, [NotNull] Location? location)
{
if (location == null)
{
throw new ArgumentException(WorkspaceExtensionsResources.No_location_provided_to_add_statements_to);
}
if (!location.IsInSource)
{
throw new ArgumentException(WorkspaceExtensionsResources.Destination_location_was_not_in_source);
}
if (location.SourceTree != destinationMember.SyntaxTree)
{
throw new ArgumentException(WorkspaceExtensionsResources.Destination_location_was_from_a_different_tree);
}
}
protected static void ComputePositionAndTriviaForRemoveAttributeList(
SyntaxNode attributeList,
Func<SyntaxTrivia, bool> isEndOfLineTrivia,
out int positionOfRemovedNode,
out IEnumerable<SyntaxTrivia> triviaOfRemovedNode)
{
positionOfRemovedNode = attributeList.FullSpan.Start;
var leading = attributeList.GetLeadingTrivia();
var trailing = attributeList.GetTrailingTrivia();
if (trailing.Count >= 1 && isEndOfLineTrivia(trailing.Last()))
{
// Remove redundant trailing trivia as we are removing the entire attribute list.
triviaOfRemovedNode = leading;
}
else
{
triviaOfRemovedNode = leading.Concat(trailing);
}
}
protected static void ComputePositionAndTriviaForRemoveAttributeFromAttributeList(
SyntaxNode attributeToRemove,
Func<SyntaxToken, bool> isComma,
out int positionOfRemovedNode,
out IEnumerable<SyntaxTrivia> triviaOfRemovedNode)
{
positionOfRemovedNode = attributeToRemove.FullSpan.Start;
var root = attributeToRemove.SyntaxTree.GetRoot();
var previousToken = root.FindToken(attributeToRemove.FullSpan.Start - 1);
var leading = isComma(previousToken) ? previousToken.LeadingTrivia : attributeToRemove.GetLeadingTrivia();
var nextToken = root.FindToken(attributeToRemove.FullSpan.End + 1);
var trailing = isComma(nextToken) ? nextToken.TrailingTrivia : attributeToRemove.GetTrailingTrivia();
triviaOfRemovedNode = leading.Concat(trailing);
}
protected static T AppendTriviaAtPosition<T>(T node, int position, SyntaxTriviaList trivia)
where T : SyntaxNode
{
if (trivia.Any())
{
var tokenToInsertTrivia = node.FindToken(position);
var tokenWithInsertedTrivia = tokenToInsertTrivia.WithLeadingTrivia(trivia.Concat(tokenToInsertTrivia.LeadingTrivia));
return node.ReplaceToken(tokenToInsertTrivia, tokenWithInsertedTrivia);
}
return node;
}
protected static SyntaxTokenList GetUpdatedDeclarationAccessibilityModifiers(
ArrayBuilder<SyntaxToken> newModifierTokens, SyntaxTokenList modifiersList,
Func<SyntaxToken, bool> isAccessibilityModifier)
{
using var _ = ArrayBuilder<SyntaxToken>.GetInstance(out var updatedModifiersList);
var anyAccessModifierSeen = false;
foreach (var modifier in modifiersList)
{
SyntaxToken newModifier;
if (isAccessibilityModifier(modifier))
{
if (newModifierTokens.Count == 0)
continue;
newModifier = newModifierTokens[0]
.WithLeadingTrivia(modifier.LeadingTrivia)
.WithTrailingTrivia(modifier.TrailingTrivia);
newModifierTokens.RemoveAt(0);
anyAccessModifierSeen = true;
}
else
{
if (anyAccessModifierSeen && newModifierTokens.Any())
{
updatedModifiersList.AddRange(newModifierTokens);
newModifierTokens.Clear();
}
newModifier = modifier;
}
updatedModifiersList.Add(newModifier);
}
if (!anyAccessModifierSeen)
{
for (var i = newModifierTokens.Count - 1; i >= 0; i--)
updatedModifiersList.Insert(0, newModifierTokens[i]);
}
else
{
updatedModifiersList.AddRange(newModifierTokens);
}
return [.. updatedModifiersList];
}
}
|