private DocumentTransformationResult TransformDocument(IDocumentAnalyzationResult documentResult) { var rootNode = documentResult.Node; var endOfLineTrivia = rootNode.DescendantTrivia().First(o => o.IsKind(SyntaxKind.EndOfLineTrivia)); var result = new DocumentTransformationResult(documentResult) { EndOfLineTrivia = endOfLineTrivia }; // Annotate so that the annotation exposed is valid rootNode = rootNode.WithAdditionalAnnotations(new SyntaxAnnotation(result.Annotation)); var globalNsTransformResult = new RootNamespaceTransformationResult(documentResult.GlobalNamespace); foreach (var typeResult in documentResult.GlobalNamespace.Types.Where(o => o.Conversion != TypeConversion.Ignore)) { var typeSpanStart = typeResult.Node.SpanStart; var typeSpanLength = typeResult.Node.Span.Length; var typeNode = rootNode.DescendantNodesAndSelf() .OfType <TypeDeclarationSyntax>() .First(o => o.SpanStart == typeSpanStart && o.Span.Length == typeSpanLength); var transformResult = TransformType(typeResult, globalNsTransformResult); result.TransformedTypes.Add(transformResult); rootNode = rootNode.ReplaceNode(typeNode, typeNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformResult.Annotation))); } foreach (var namespaceResult in documentResult.GlobalNamespace.NestedNamespaces.OrderBy(o => o.Node.SpanStart)) { var namespaceSpanStart = namespaceResult.Node.SpanStart; var namespaceSpanLength = namespaceResult.Node.Span.Length; var namespaceNode = rootNode.DescendantNodesAndSelf() .OfType <NamespaceDeclarationSyntax>() .First(o => o.SpanStart == namespaceSpanStart && o.Span.Length == namespaceSpanLength); var transformResult = TransformNamespace(namespaceResult); result.TransformedNamespaces.Add(transformResult); rootNode = rootNode.ReplaceNode(namespaceNode, namespaceNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformResult.Annotation))); } // Save the orignal node that was only annotated var originalAnnotatedNode = rootNode; // TODO: use the same pattern as in namespace and type transformation var transformResults = result.TransformedNamespaces .Cast <ITransformationResult>() .Union(result.TransformedTypes) .ToList(); var newMembers = transformResults .OrderBy(o => o.OriginalStartSpan) .SelectMany(o => o.GetTransformedNodes()) .ToList(); if (!newMembers.Any()) { return(result); // the document will not be created } rootNode = rootNode .WithMembers(List(newMembers)); // Update the original document if required foreach (var rewrittenNode in transformResults.Where(o => o.OriginalModified != null).OrderByDescending(o => o.OriginalStartSpan)) { if (result.OriginalModified == null) { result.OriginalModified = originalAnnotatedNode; } result.OriginalModified = result.OriginalModified .ReplaceNode(result.OriginalModified .GetAnnotatedNodes(rewrittenNode.Annotation).First(), rewrittenNode.OriginalModified); } // Add auto-generated comment rootNode = rootNode.WithLeadingTrivia( SyntaxNodeExtensions.AddAutoGeneratedTrivia(endOfLineTrivia).AddRange(rootNode.GetLeadingTrivia())); result.Transformed = rootNode; return(result); }
private RootNamespaceTransformationResult TransformNamespace(INamespaceAnalyzationResult rootResult) { var rootNode = rootResult.Node; var startRootSpan = rootNode.SpanStart; var rootTransformResult = new RootNamespaceTransformationResult(rootResult); // We do this here because we want that the root node has span start equal to 0 rootNode = rootNode.WithAdditionalAnnotations(new SyntaxAnnotation(rootTransformResult.Annotation)); startRootSpan -= rootNode.SpanStart; // Before any modification we need to annotate nodes that will be transformed in order to find them later on. foreach (var result in rootResult.GetSelfAndDescendantsNamespaces()) { var spanStart = result.Node.SpanStart - startRootSpan; var spanLength = result.Node.Span.Length; var node = rootNode.DescendantNodesAndSelf().OfType <NamespaceDeclarationSyntax>() .First(o => o.SpanStart == spanStart && o.Span.Length == spanLength); var leadingWhitespace = node.GetLeadingWhitespace(); NamespaceTransformationResult transformResult; if (node == rootNode) { transformResult = rootTransformResult; // To get the indent of the root namespace we need to check the first memeber as the parent is the document transformResult.IndentTrivia = node.Members.FirstOrDefault()?.GetLeadingWhitespace() ?? default(SyntaxTrivia); } else { transformResult = new NamespaceTransformationResult(result) { IndentTrivia = node.GetIndent(leadingWhitespace) }; rootNode = rootNode.ReplaceNode(node, node.WithAdditionalAnnotations(new SyntaxAnnotation(transformResult.Annotation))); rootTransformResult.DescendantTransformedNamespaces.Add(transformResult); } transformResult.LeadingWhitespaceTrivia = leadingWhitespace; transformResult.EndOfLineTrivia = node.GetEndOfLine(); transformResult.TaskConflict = rootResult.ContainsType(nameof(Task)); transformResult.UsingSystem = result.Node.HasUsing("System"); foreach (var typeResult in result.Types /*.Where(o => o.Conversion != TypeConversion.Ignore)*/) { var typeSpanStart = typeResult.Node.SpanStart - startRootSpan; var typeSpanLength = typeResult.Node.Span.Length; var typeNode = rootNode.DescendantNodesAndSelf() .OfType <TypeDeclarationSyntax>() .First(o => o.SpanStart == typeSpanStart && o.Span.Length == typeSpanLength); var transformTypeResult = TransformType(typeResult, transformResult); transformResult.TransformedTypes.Add(transformTypeResult); rootNode = rootNode.ReplaceNode(typeNode, typeNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformTypeResult.Annotation))); transformResult.ThreadingUsingRequired |= typeResult.GetSelfAndDescendantsTypes().SelectMany(o => o.MethodsAndAccessors).Any(o => o.CancellationTokenRequired) || typeResult.GetSelfAndDescendantsTypes() .SelectMany(o => o.MethodsAndAccessors) .SelectMany(o => o.GetSelfAndDescendantsFunctions()) .Any(o => o.BodyFunctionReferences.Any(r => r.PassCancellationToken)); } } // Save the orignal node that was only annotated var originalAnnotatedNode = rootNode; // Now we can start transforming the namespace. Start from the bottom in order to preserve replaced nested namespaces foreach (var transformResult in rootTransformResult.GetSelfAndDescendantTransformedNamespaces().OrderByDescending(o => o.OriginalNode.SpanStart)) { if (transformResult.AnalyzationResult.Conversion == NamespaceConversion.Ignore) { rootNode = rootNode.RemoveNodeKeepDirectives(transformResult.Annotation, transformResult.LeadingWhitespaceTrivia, transformResult.EndOfLineTrivia); continue; } var node = rootNode.GetAnnotatedNodes(transformResult.Annotation).OfType <NamespaceDeclarationSyntax>().First(); var newNode = node; var memberWhitespace = Whitespace(transformResult.LeadingWhitespaceTrivia.ToFullString() + transformResult.IndentTrivia.ToFullString()); foreach (var transformType in transformResult.TransformedTypes.OrderByDescending(o => o.OriginalStartSpan)) { if (transformType.AnalyzationResult.Conversion == TypeConversion.Ignore) { // We need to add a whitespace trivia to kept directives as they will not have any leading whitespace newNode = newNode.RemoveNodeKeepDirectives(transformType.Annotation, memberWhitespace, transformResult.EndOfLineTrivia); continue; } var typeNode = newNode.GetAnnotatedNodes(transformType.Annotation) .OfType <MemberDeclarationSyntax>() .First(); newNode = newNode.ReplaceWithMembers(typeNode, transformType.GetTransformedNodes() .OfType <MemberDeclarationSyntax>() .ToImmutableList()); } // We need to remove all other members that are not namespaces or types newNode = newNode.RemoveMembersKeepDirectives(o => !(o is NamespaceDeclarationSyntax || o is TypeDeclarationSyntax), memberWhitespace, transformResult.EndOfLineTrivia); // TODO: adding namespaces can introduce name conflicts, we should avoid it if (!transformResult.TaskConflict && !rootResult.Node.HasUsing("System.Threading.Tasks")) { transformResult.AddUsing("System.Threading.Tasks"); } if (transformResult.ThreadingUsingRequired && !rootResult.Node.HasUsing("System.Threading")) { transformResult.AddUsing("System.Threading"); } // TODO: add locking namespaces foreach (var additionalUsing in transformResult.AdditionalUsings) { if (!rootResult.Node.HasUsing(additionalUsing) && !node.HasUsing(additionalUsing)) { newNode = newNode.AddUsing(additionalUsing, TriviaList(memberWhitespace), transformResult.EndOfLineTrivia); } } transformResult.Transformed = newNode; rootNode = rootNode.ReplaceNode(node, newNode); // We need to update the original types if they were modified foreach (var transformTypeResult in transformResult.TransformedTypes.Where(o => o.OriginalModified != null).OrderByDescending(o => o.OriginalStartSpan)) { if (rootTransformResult.OriginalModified == null) { rootTransformResult.OriginalModified = originalAnnotatedNode; } rootTransformResult.OriginalModified = rootTransformResult.OriginalModified .ReplaceNode(rootTransformResult.OriginalModified .GetAnnotatedNodes(transformTypeResult.Annotation).First(), transformTypeResult.OriginalModified); } } rootTransformResult.Transformed = rootNode; return(rootTransformResult); }