diff --git a/Src/Compilers/Core/Portable/Syntax/SeparatedSyntaxList.cs b/Src/Compilers/Core/Portable/Syntax/SeparatedSyntaxList.cs index 845480bd2a75b160c7b97bbe2d45c72976654cdb..e5a4187c5e6d4f65b2feaf7828597dd0d840c36a 100644 --- a/Src/Compilers/Core/Portable/Syntax/SeparatedSyntaxList.cs +++ b/Src/Compilers/Core/Portable/Syntax/SeparatedSyntaxList.cs @@ -367,19 +367,18 @@ public SeparatedSyntaxList InsertRange(int index, IEnumerable node } } + var nodesToInsertWithSeparators = new List(); foreach (var item in nodes) { if (item != null) { // if item before insertion point is a node, add a separator - if (insertionIndex > 0 && nodesWithSeps[insertionIndex - 1].IsNode) + if (nodesToInsertWithSeparators.Count > 0 || (insertionIndex > 0 && nodesWithSeps[insertionIndex - 1].IsNode)) { - nodesWithSeps = nodesWithSeps.Insert(insertionIndex, item.Green.CreateSeparator(item)); // separator - insertionIndex++; + nodesToInsertWithSeparators.Add(item.Green.CreateSeparator(item)); } - nodesWithSeps = nodesWithSeps.Insert(insertionIndex, item); - insertionIndex++; + nodesToInsertWithSeparators.Add(item); } } @@ -387,10 +386,10 @@ public SeparatedSyntaxList InsertRange(int index, IEnumerable node if (insertionIndex < nodesWithSeps.Count && nodesWithSeps[insertionIndex].IsNode) { var node = nodesWithSeps[insertionIndex].AsNode(); - nodesWithSeps = nodesWithSeps.Insert(insertionIndex, node.Green.CreateSeparator(node)); // separator + nodesToInsertWithSeparators.Add(node.Green.CreateSeparator(node)); // separator } - return new SeparatedSyntaxList(nodesWithSeps); + return new SeparatedSyntaxList(nodesWithSeps.InsertRange(insertionIndex, nodesToInsertWithSeparators)); } private static bool KeepSeparatorWithPreviousNode(SyntaxToken separator) diff --git a/Src/Compilers/Core/Portable/Syntax/SyntaxNodeOrTokenList.cs b/Src/Compilers/Core/Portable/Syntax/SyntaxNodeOrTokenList.cs index a2e318d915c48e3d7f3102ab77a4e30726f02931..08c4c5161700ad9dd1046d12ccdad10afac8eba8 100644 --- a/Src/Compilers/Core/Portable/Syntax/SyntaxNodeOrTokenList.cs +++ b/Src/Compilers/Core/Portable/Syntax/SyntaxNodeOrTokenList.cs @@ -295,7 +295,7 @@ public SyntaxNodeOrTokenList Insert(int index, SyntaxNodeOrToken nodeOrToken) throw new ArgumentException("nodeOrToken"); } - return InsertRange(index, new[] { nodeOrToken }); + return InsertRange(index, SpecializedCollections.SingletonEnumerable(nodeOrToken)); } /// @@ -315,22 +315,14 @@ public SyntaxNodeOrTokenList InsertRange(int index, IEnumerable items) diff --git a/Src/Workspaces/CSharp/CodeGeneration/CSharpCodeGenerationHelpers.cs b/Src/Workspaces/CSharp/CodeGeneration/CSharpCodeGenerationHelpers.cs index e38bc76e6f23b03a54ad7fba558057207eda146a..c6101f4528f8a6077f84b923814870048029b8a3 100644 --- a/Src/Workspaces/CSharp/CodeGeneration/CSharpCodeGenerationHelpers.cs +++ b/Src/Workspaces/CSharp/CodeGeneration/CSharpCodeGenerationHelpers.cs @@ -329,7 +329,7 @@ public static MemberDeclarationSyntax LastOperator(SyntaxList(TDeclarationN return destination; } + protected override TDeclarationNode AddMembers(TDeclarationNode destination, IEnumerable members) + { + CheckDeclarationNode(destination); + + if (destination is EnumDeclarationSyntax) + { + return Cast(Cast(destination) + .AddMembers(members.Cast().ToArray())); + } + else if (destination is TypeDeclarationSyntax) + { + return Cast(Cast(destination) + .AddMembers(members.Cast().ToArray())); + } + else if (destination is NamespaceDeclarationSyntax) + { + return Cast(Cast(destination) + .AddMembers(members.Cast().ToArray())); + } + else + { + return Cast(Cast(destination) + .AddMembers(members.Cast().ToArray())); + } + } + public override TDeclarationNode RemoveAttribute( TDeclarationNode destination, AttributeData attributeToRemove, diff --git a/Src/Workspaces/Core/CodeGeneration/AbstractCodeGenerationService.cs b/Src/Workspaces/Core/CodeGeneration/AbstractCodeGenerationService.cs index e3863b218b18c0b64fa62c51f280c72c5b0f5976..94dde65079f408bd75a8397df228bab3d8d22db8 100644 --- a/Src/Workspaces/Core/CodeGeneration/AbstractCodeGenerationService.cs +++ b/Src/Workspaces/Core/CodeGeneration/AbstractCodeGenerationService.cs @@ -66,6 +66,7 @@ public TDeclarationNode AddMembers(TDeclarationNode destinatio protected abstract TDeclarationNode AddProperty(TDeclarationNode destination, IPropertySymbol property, CodeGenerationOptions options, IList availableIndices) where TDeclarationNode : SyntaxNode; protected abstract TDeclarationNode AddNamedType(TDeclarationNode destination, INamedTypeSymbol namedType, CodeGenerationOptions options, IList availableIndices) where TDeclarationNode : SyntaxNode; protected abstract TDeclarationNode AddNamespace(TDeclarationNode destination, INamespaceSymbol @namespace, CodeGenerationOptions options, IList availableIndices) where TDeclarationNode : SyntaxNode; + protected abstract TDeclarationNode AddMembers(TDeclarationNode destination, IEnumerable members) where TDeclarationNode : SyntaxNode; public abstract TDeclarationNode AddParameters(TDeclarationNode destinationMember, IEnumerable parameters, CodeGenerationOptions options, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode; public abstract TDeclarationNode AddAttributes(TDeclarationNode destination, IEnumerable attributes, SyntaxToken? target, CodeGenerationOptions options, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode; @@ -78,6 +79,7 @@ public TDeclarationNode AddMembers(TDeclarationNode destinatio public abstract TDeclarationNode UpdateDeclarationType(TDeclarationNode declaration, ITypeSymbol newType, CodeGenerationOptions options, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode; public abstract TDeclarationNode UpdateDeclarationMembers(TDeclarationNode declaration, IList newMembers, CodeGenerationOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) where TDeclarationNode : SyntaxNode; + public abstract CodeGenerationDestination GetDestination(SyntaxNode node); public abstract SyntaxNode CreateEventDeclaration(IEventSymbol @event, CodeGenerationDestination destination, CodeGenerationOptions options); public abstract SyntaxNode CreateFieldDeclaration(IFieldSymbol field, CodeGenerationDestination destination, CodeGenerationOptions options); public abstract SyntaxNode CreateMethodDeclaration(IMethodSymbol method, CodeGenerationDestination destination, CodeGenerationOptions options); @@ -132,6 +134,24 @@ protected static T Cast(object value) } } + protected static void CheckDeclarationNode(SyntaxNode destination) + where TDeclarationNode1 : SyntaxNode + where TDeclarationNode2 : SyntaxNode + where TDeclarationNode3 : SyntaxNode + where TDeclarationNode4 : SyntaxNode + { + if (!(destination is TDeclarationNode1) && + !(destination is TDeclarationNode2) && + !(destination is TDeclarationNode3) && + !(destination is TDeclarationNode4)) + { + throw new ArgumentException( + string.Format(WorkspacesResources.InvalidDestinationNode3, + typeof(TDeclarationNode1).Name, typeof(TDeclarationNode2).Name, typeof(TDeclarationNode3).Name, typeof(TDeclarationNode4).Name), + "destination"); + } + } + private async Task GetEditAsync( Solution solution, INamespaceOrTypeSymbol destination, @@ -189,16 +209,42 @@ protected static T Cast(object value) // not want an explicit declaration. var filteredMembers = membersList.Where(m => !m.IsImplicitlyDeclared); - foreach (var member in filteredMembers) + if (options.AutoInsertionLocation) + { + foreach (var member in filteredMembers) + { + currentDestination = member.TypeSwitch( + (IEventSymbol @event) => this.AddEvent(currentDestination, @event, options, availableIndices), + (IFieldSymbol field) => this.AddField(currentDestination, field, options, availableIndices), + (IPropertySymbol property) => this.AddProperty(currentDestination, property, options, availableIndices), + (IMethodSymbol method) => this.AddMethod(currentDestination, method, options, availableIndices), + (INamedTypeSymbol namedType) => this.AddNamedType(currentDestination, namedType, options, availableIndices), + (INamespaceSymbol @namespace) => this.AddNamespace(currentDestination, @namespace, options, availableIndices), + _ => currentDestination); + } + } + else { - currentDestination = member.TypeSwitch( - (IEventSymbol @event) => this.AddEvent(currentDestination, @event, options, availableIndices), - (IFieldSymbol field) => this.AddField(currentDestination, field, options, availableIndices), - (IPropertySymbol property) => this.AddProperty(currentDestination, property, options, availableIndices), - (IMethodSymbol method) => this.AddMethod(currentDestination, method, options, availableIndices), - (INamedTypeSymbol namedType) => this.AddNamedType(currentDestination, namedType, options, availableIndices), - (INamespaceSymbol @namespace) => this.AddNamespace(currentDestination, @namespace, options, availableIndices), - _ => currentDestination); + var newMembers = new List(); + var codeGenerationDestination = GetDestination(destination); + foreach (var member in filteredMembers) + { + var newMember = member.TypeSwitch( + (IEventSymbol @event) => this.CreateEventDeclaration(@event, codeGenerationDestination, options), + (IFieldSymbol field) => this.CreateFieldDeclaration(field, codeGenerationDestination, options), + (IPropertySymbol property) => this.CreatePropertyDeclaration(property, codeGenerationDestination, options), + (IMethodSymbol method) => this.CreateMethodDeclaration(method, codeGenerationDestination, options), + (INamedTypeSymbol namedType) => this.CreateNamedTypeDeclaration(namedType, codeGenerationDestination, options), + (INamespaceSymbol @namespace) => this.CreateNamespaceDeclaration(@namespace, codeGenerationDestination, options), + _ => null); + + if (newMember != null) + { + newMembers.Add(newMember); + } + } + + currentDestination = this.AddMembers(currentDestination, newMembers); } return currentDestination; diff --git a/Src/Workspaces/VisualBasic/CodeGeneration/EnumMemberGenerator.vb b/Src/Workspaces/VisualBasic/CodeGeneration/EnumMemberGenerator.vb index 3e9f6ed1e546b9fe2bc09509421ec184cfa8dc70..237a55f7e07ea3d9409e64133681ffa710d93813 100644 --- a/Src/Workspaces/VisualBasic/CodeGeneration/EnumMemberGenerator.vb +++ b/Src/Workspaces/VisualBasic/CodeGeneration/EnumMemberGenerator.vb @@ -13,15 +13,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration enumMember As IFieldSymbol, options As CodeGenerationOptions) As EnumBlockSyntax - ' We never generate the special enum backing field. - If enumMember.Name = WellKnownMemberNames.EnumBackingFieldName Then + Dim member = GenerateEnumMemberDeclaration(enumMember, destination, options) + If member Is Nothing Then Return destination End If Dim members = New List(Of StatementSyntax)() members.AddRange(destination.Members) - Dim member = GenerateEnumMemberDeclaration(enumMember, destination, options) - members.Add(member) Dim leadingTrivia = destination.EndEnumStatement.GetLeadingTrivia() @@ -32,6 +30,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Public Function GenerateEnumMemberDeclaration(enumMember As IFieldSymbol, enumDeclarationOpt As EnumBlockSyntax, options As CodeGenerationOptions) As EnumMemberDeclarationSyntax + ' We never generate the special enum backing field. + If enumMember.Name = WellKnownMemberNames.EnumBackingFieldName Then + Return Nothing + End If + Dim reusableSyntax = GetReuseableSyntaxNodeForSymbol(Of EnumMemberDeclarationSyntax)(enumMember, options) If reusableSyntax IsNot Nothing Then Return reusableSyntax diff --git a/Src/Workspaces/VisualBasic/CodeGeneration/VisualBasicCodeGenerationHelpers.vb b/Src/Workspaces/VisualBasic/CodeGeneration/VisualBasicCodeGenerationHelpers.vb index 3012ac9f41c0a127955f89804709dd17b0914180..f6aa092fbe854c730c72542c03a71c35220d0e49 100644 --- a/Src/Workspaces/VisualBasic/CodeGeneration/VisualBasicCodeGenerationHelpers.vb +++ b/Src/Workspaces/VisualBasic/CodeGeneration/VisualBasicCodeGenerationHelpers.vb @@ -236,17 +236,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return declarationList.Count End Function - Public Function GetDestination(destination As TypeBlockSyntax) As CodeGenerationDestination + Public Function GetDestination(destination As SyntaxNode) As CodeGenerationDestination If destination IsNot Nothing Then Select Case destination.VisualBasicKind Case SyntaxKind.ClassBlock Return CodeGenerationDestination.ClassType + Case SyntaxKind.CompilationUnit + Return CodeGenerationDestination.CompilationUnit + Case SyntaxKind.EnumBlock + Return CodeGenerationDestination.EnumType Case SyntaxKind.InterfaceBlock Return CodeGenerationDestination.InterfaceType Case SyntaxKind.ModuleBlock Return CodeGenerationDestination.ModuleType + Case SyntaxKind.NamespaceBlock + Return CodeGenerationDestination.Namespace Case SyntaxKind.StructureBlock Return CodeGenerationDestination.StructType + Case Else + Return CodeGenerationDestination.Unspecified End Select End If diff --git a/Src/Workspaces/VisualBasic/CodeGeneration/VisualBasicCodeGenerationService.vb b/Src/Workspaces/VisualBasic/CodeGeneration/VisualBasicCodeGenerationService.vb index 6be48b5221ce18386ef9b5484a3b2b167f37ca35..6d40a9d211cd1a55644c5a4d6c76371bd6e933f1 100644 --- a/Src/Workspaces/VisualBasic/CodeGeneration/VisualBasicCodeGenerationService.vb +++ b/Src/Workspaces/VisualBasic/CodeGeneration/VisualBasicCodeGenerationService.vb @@ -18,6 +18,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration MyBase.New(provider.GetService(Of ISymbolDeclarationService)()) End Sub + Public Overloads Overrides Function GetDestination(containerNode As SyntaxNode) As CodeGenerationDestination + Return VisualBasicCodeGenerationHelpers.GetDestination(containerNode) + End Function + Protected Overrides Function CreateImportsAdder(document As Document) As AbstractImportsAdder Return New ImportsStatementsAdder(document) End Function @@ -204,6 +208,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return destinationMember End Function + Protected Overrides Function AddMembers(Of TDeclarationNode As SyntaxNode)(destination As TDeclarationNode, members As IEnumerable(Of SyntaxNode)) As TDeclarationNode + CheckDeclarationNode(Of EnumBlockSyntax, TypeBlockSyntax, NamespaceBlockSyntax, CompilationUnitSyntax)(destination) + If TypeOf destination Is EnumBlockSyntax Then + Return Cast(Of TDeclarationNode)(Cast(Of EnumBlockSyntax)(destination).AddMembers(members.Cast(Of EnumMemberDeclarationSyntax).ToArray())) + ElseIf TypeOf destination Is TypeBlockSyntax Then + Return Cast(Of TDeclarationNode)(Cast(Of TypeBlockSyntax)(destination).AddMembers(members.Cast(Of StatementSyntax).ToArray())) + ElseIf TypeOf destination Is NamespaceBlockSyntax Then + Return Cast(Of TDeclarationNode)(Cast(Of NamespaceBlockSyntax)(destination).AddMembers(members.Cast(Of StatementSyntax).ToArray())) + Else + Return Cast(Of TDeclarationNode)(Cast(Of CompilationUnitSyntax)(destination).AddMembers(members.Cast(Of StatementSyntax).ToArray())) + End If + End Function + Private Overloads Shared Function AddParametersToMethod(Of TDeclarationNode As SyntaxNode)(methodStatement As MethodBaseSyntax, methodBlock As MethodBlockBaseSyntax, parameters As IEnumerable(Of IParameterSymbol), diff --git a/Src/Workspaces/VisualBasic/Extensions/TypeBlockSyntaxExtensions.vb b/Src/Workspaces/VisualBasic/Extensions/TypeBlockSyntaxExtensions.vb index 21ba49982fa75bcad3780668a140e96df4d98bd4..234c056646989c5a89679993a3047a7af38e8d77 100644 --- a/Src/Workspaces/VisualBasic/Extensions/TypeBlockSyntaxExtensions.vb +++ b/Src/Workspaces/VisualBasic/Extensions/TypeBlockSyntaxExtensions.vb @@ -39,6 +39,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Throw Contract.Unreachable End Function + + Public Function AddMembers(node As TypeBlockSyntax, ParamArray members As StatementSyntax()) As TypeBlockSyntax + Select Case node.VisualBasicKind + Case SyntaxKind.ModuleBlock + Return DirectCast(node, ModuleBlockSyntax).AddMembers(members) + Case SyntaxKind.InterfaceBlock + Return DirectCast(node, InterfaceBlockSyntax).AddMembers(members) + Case SyntaxKind.StructureBlock + Return DirectCast(node, StructureBlockSyntax).AddMembers(members) + Case SyntaxKind.ClassBlock + Return DirectCast(node, ClassBlockSyntax).AddMembers(members) + End Select + + Throw Contract.Unreachable + End Function + Public Function WithMembers(node As TypeBlockSyntax, members As SyntaxList(Of StatementSyntax)) As TypeBlockSyntax Select Case node.VisualBasicKind