private async Task <Document> CreateChangedDocumentAsync(CodeFixContext context, DocumentationCommentTriviaSyntax documentationCommentTriviaSyntax, CancellationToken cancellationToken) { StringBuilder leadingTriviaBuilder = new StringBuilder(); SyntaxToken parentToken = documentationCommentTriviaSyntax.ParentTrivia.Token; int documentationCommentIndex = parentToken.LeadingTrivia.IndexOf(documentationCommentTriviaSyntax.ParentTrivia); for (int i = 0; i < documentationCommentIndex; i++) { SyntaxTrivia trivia = parentToken.LeadingTrivia[i]; switch (trivia.Kind()) { case SyntaxKind.EndOfLineTrivia: leadingTriviaBuilder.Clear(); break; case SyntaxKind.WhitespaceTrivia: leadingTriviaBuilder.Append(trivia.ToFullString()); break; default: break; } } leadingTriviaBuilder.Append(documentationCommentTriviaSyntax.GetLeadingTrivia().ToFullString()); // this is the trivia that should appear at the beginning of each line of the comment. SyntaxTrivia leadingTrivia = SyntaxFactory.DocumentationCommentExterior(leadingTriviaBuilder.ToString()); string newLineText = context.Document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp); var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); var documentedSymbol = semanticModel.GetDeclaredSymbol(parentToken.Parent.FirstAncestorOrSelf <SyntaxNode>(SyntaxNodeExtensionsEx.IsSymbolDeclaration), context.CancellationToken); DocumentationCommentTriviaSyntax contentsOnly = RemoveExteriorTrivia(documentationCommentTriviaSyntax); contentsOnly = contentsOnly.ReplaceNodes(contentsOnly.ChildNodes(), (originalNode, rewrittenNode) => RenderBlockElementAsMarkdown(originalNode, rewrittenNode, newLineText, documentedSymbol)); string renderedContent = contentsOnly.Content.ToFullString(); string[] lines = renderedContent.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); SyntaxList <XmlNodeSyntax> newContent = XmlSyntaxFactory.List(); for (int i = 0; i < lines.Length; i++) { string line = lines[i]; if (string.IsNullOrWhiteSpace(line)) { if (i == lines.Length - 1) { break; } line = string.Empty; } if (newContent.Count > 0) { newContent = newContent.Add(XmlSyntaxFactory.NewLine(newLineText).WithTrailingTrivia(SyntaxFactory.DocumentationCommentExterior("///"))); } newContent = newContent.Add(XmlSyntaxFactory.Text(line.TrimEnd(), xmlEscape: false)); } contentsOnly = contentsOnly.WithContent(newContent); contentsOnly = contentsOnly .ReplaceExteriorTrivia(leadingTrivia) .WithLeadingTrivia(SyntaxFactory.DocumentationCommentExterior("///")) .WithTrailingTrivia(SyntaxFactory.EndOfLine(Environment.NewLine)); string fullContent = contentsOnly.ToFullString(); SyntaxTriviaList parsedTrivia = SyntaxFactory.ParseLeadingTrivia(fullContent); SyntaxTrivia documentationTrivia = parsedTrivia.FirstOrDefault(i => i.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)); contentsOnly = documentationTrivia.GetStructure() as DocumentationCommentTriviaSyntax; if (contentsOnly == null) { return(context.Document); } // Remove unnecessary nested paragraph elements contentsOnly = contentsOnly.ReplaceNodes(contentsOnly.DescendantNodes().OfType <XmlElementSyntax>(), MarkUnnecessaryParagraphs); contentsOnly = contentsOnly.ReplaceNodes(contentsOnly.DescendantNodes().OfType <XmlElementSyntax>(), RemoveUnnecessaryParagraphs); SyntaxNode root = await context.Document.GetSyntaxRootAsync(cancellationToken); SyntaxNode newRoot = root.ReplaceNode(documentationCommentTriviaSyntax, contentsOnly); if (documentationCommentTriviaSyntax.IsEquivalentTo(contentsOnly)) { return(context.Document); } if (documentationCommentTriviaSyntax.ToFullString().Equals(contentsOnly.ToFullString(), StringComparison.Ordinal)) { return(context.Document); } return(context.Document.WithSyntaxRoot(newRoot)); }