public static async Task<Document> TransformAsync(Document inputDocument, IProgressAndErrors progress, bool simplify = false) { Requires.NotNull(inputDocument, "inputDocument"); var workspace = inputDocument.Project.Solution.Workspace; var inputSemanticModel = await inputDocument.GetSemanticModelAsync(); var inputSyntaxTree = inputSemanticModel.SyntaxTree; var inputFileLevelUsingDirectives = inputSyntaxTree.GetRoot().ChildNodes().OfType<UsingDirectiveSyntax>(); var memberNodes = from syntax in inputSyntaxTree.GetRoot().DescendantNodes(n => n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax || n is TypeDeclarationSyntax).OfType<MemberDeclarationSyntax>() select syntax; var emittedMembers = new List<MemberDeclarationSyntax>(); foreach (var memberNode in memberNodes) { var namespaceNode = memberNode.Parent as NamespaceDeclarationSyntax; var generators = FindCodeGenerators(inputSemanticModel, memberNode); foreach (var generator in generators) { var generatedTypes = await generator.GenerateAsync(memberNode, inputDocument, progress, CancellationToken.None); if (namespaceNode != null) { emittedMembers.Add(SyntaxFactory.NamespaceDeclaration(namespaceNode.Name) .WithUsings(SyntaxFactory.List(namespaceNode.ChildNodes().OfType<UsingDirectiveSyntax>())) .WithMembers(SyntaxFactory.List(generatedTypes))); } else { emittedMembers.AddRange(generatedTypes); } } } var emittedTree = SyntaxFactory.CompilationUnit() .WithUsings(SyntaxFactory.List(inputFileLevelUsingDirectives).Add( SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("ImmutableObjectGraph")))) .WithMembers(SyntaxFactory.List(emittedMembers)) .WithLeadingTrivia(SyntaxFactory.Comment(GeneratedByAToolPreamble)) .WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed); // Format the tree to get reasonably good whitespace. var formattedTree = Formatter.Format(emittedTree, workspace, workspace.Options); // Reduce the document to get rid of unnecessary fully-qualified type names that just hurt readability. var formattedText = formattedTree.GetText(); var document = inputDocument.Project.AddDocument("generated.cs", formattedText); if (simplify) { var annotatedDocument = document .WithSyntaxRoot((await document.GetSyntaxRootAsync()) .WithAdditionalAnnotations(Simplifier.Annotation)); // allow simplification of the entire document document = await Simplifier.ReduceAsync(annotatedDocument); } return document; }
public static async Task <Document> TransformAsync(Document inputDocument, IProgressAndErrors progress, bool simplify = false) { Requires.NotNull(inputDocument, "inputDocument"); var workspace = inputDocument.Project.Solution.Workspace; var inputSemanticModel = await inputDocument.GetSemanticModelAsync(); var inputSyntaxTree = inputSemanticModel.SyntaxTree; var inputFileLevelUsingDirectives = inputSyntaxTree.GetRoot().ChildNodes().OfType <UsingDirectiveSyntax>(); var memberNodes = from syntax in inputSyntaxTree.GetRoot().DescendantNodes(n => n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax || n is TypeDeclarationSyntax).OfType <MemberDeclarationSyntax>() select syntax; var emittedMembers = new List <MemberDeclarationSyntax>(); foreach (var memberNode in memberNodes) { var namespaceNode = memberNode.Ancestors().OfType <NamespaceDeclarationSyntax>().FirstOrDefault(); var generators = FindCodeGenerators(inputSemanticModel, memberNode); foreach (var generator in generators) { var generatedTypes = await generator.GenerateAsync(memberNode, inputDocument, progress, CancellationToken.None); if (namespaceNode != null) { emittedMembers.Add(SyntaxFactory.NamespaceDeclaration(namespaceNode.Name) .WithUsings(SyntaxFactory.List(namespaceNode.ChildNodes().OfType <UsingDirectiveSyntax>())) .WithMembers(SyntaxFactory.List(generatedTypes))); } else { emittedMembers.AddRange(generatedTypes); } } } // By default, retain all the using directives that came from the input file. var resultFileLevelUsingDirectives = SyntaxFactory.List(inputFileLevelUsingDirectives); // Add a using directive for the ImmutableObjectGraph if there isn't one. var immutableObjectGraphName = nameof(ImmutableObjectGraph); if (!resultFileLevelUsingDirectives.Any(u => u.Name.ToString() == immutableObjectGraphName)) { resultFileLevelUsingDirectives = resultFileLevelUsingDirectives.Add( SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(immutableObjectGraphName))); } var emittedTree = SyntaxFactory.CompilationUnit() .WithUsings(resultFileLevelUsingDirectives) .WithMembers(SyntaxFactory.List(emittedMembers)) .WithLeadingTrivia(SyntaxFactory.Comment(GeneratedByAToolPreamble)) .WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed) .NormalizeWhitespace(); // Format the tree to get reasonably good whitespace. var formattedTree = Formatter.Format(emittedTree, workspace, workspace.Options); // Reduce the document to get rid of unnecessary fully-qualified type names that just hurt readability. var formattedText = formattedTree.GetText(); var document = inputDocument.Project.AddDocument("generated.cs", formattedText); if (simplify) { var annotatedDocument = document .WithSyntaxRoot((await document.GetSyntaxRootAsync()) .WithAdditionalAnnotations(Simplifier.Annotation)); // allow simplification of the entire document document = await Simplifier.ReduceAsync(annotatedDocument); } return(document); }
/// <summary> /// Create the syntax tree representing the expansion of some member to which this attribute is applied. /// </summary> /// <param name="applyTo">The syntax node this attribute is found on.</param> /// <param name="document">The document with the semantic model in which this attribute was found.</param> /// <param name="progress">A way to report progress, errors and warnings.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <returns>The generated member syntax to be added to the project.</returns> public abstract Task <IReadOnlyList <MemberDeclarationSyntax> > GenerateAsync(MemberDeclarationSyntax applyTo, Document document, IProgressAndErrors progress, CancellationToken cancellationToken);
public override Task <IReadOnlyList <MemberDeclarationSyntax> > GenerateAsync(MemberDeclarationSyntax applyTo, Document document, IProgressAndErrors progress, CancellationToken cancellationToken) { var options = new CodeGen.Options { GenerateBuilder = this.GenerateBuilder, Delta = this.Delta, DefineInterface = this.DefineInterface, DefineRootedStruct = this.DefineRootedStruct, DefineWithMethodsPerProperty = this.DefineWithMethodsPerProperty, }; return(CodeGen.GenerateAsync((ClassDeclarationSyntax)applyTo, document, progress, options, cancellationToken)); }
public Task <IReadOnlyList <MemberDeclarationSyntax> > GenerateAsync(MemberDeclarationSyntax applyTo, Document document, IProgressAndErrors progress, CancellationToken cancellationToken) { Requires.NotNull(applyTo, nameof(applyTo)); var options = new CodeGen.Options { GenerateBuilder = this.GetBoolData(nameof(GenerateImmutableAttribute.GenerateBuilder)), Delta = this.GetBoolData(nameof(GenerateImmutableAttribute.Delta)), DefineInterface = this.GetBoolData(nameof(GenerateImmutableAttribute.DefineInterface)), DefineRootedStruct = this.GetBoolData(nameof(GenerateImmutableAttribute.DefineRootedStruct)), DefineWithMethodsPerProperty = this.GetBoolData(nameof(GenerateImmutableAttribute.DefineWithMethodsPerProperty)), }; return(CodeGen.GenerateAsync((ClassDeclarationSyntax)applyTo, document, progress, options, cancellationToken)); }