How do I add an XML Doc Comment to a ClassDeclarationSyntax in Roslyn?

2.8k Views Asked by At

I have some code that builds a class:

return SyntaxFactory
    .ClassDeclaration(name)
    .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
    .WithMembers(GetProperties());

This all works, and outputs the class that I expect. I'd like to add an XML doc to this:

/// <summary>
/// Some plain text here.
/// </summary>

I don't need anything fancy, just plain text. I'm struggling to find any examples of this, and have been through various overloads of Annotations, Trivia, and loads of methods hanging off SyntaxFactory (XmlComment, XmlElementStartTag, DocumentationCommentExterior, DocumentationCommentTrivia) with no joy (ironically, none of these have XML Comments!). The closest I got was something that compiled, but crashed at runtime with Unexpected false!

I'm targetting .NET 4.5, using VS 2015 RC and 1.0.0-rc2 of Roslyn.

4

There are 4 best solutions below

5
On BEST ANSWER

You can use RoslynQuoter to see how to do this:

        SyntaxFactory.ClassDeclaration(
            @"C")
        .WithKeyword(
            SyntaxFactory.Token(
                SyntaxFactory.TriviaList(
                    SyntaxFactory.Trivia(
                        SyntaxFactory.DocumentationCommentTrivia(
                            SyntaxKind.SingleLineDocumentationCommentTrivia,
                            SyntaxFactory.List<XmlNodeSyntax>(
                                new XmlNodeSyntax[]{
                                    SyntaxFactory.XmlText()
                                    .WithTextTokens(
                                        SyntaxFactory.TokenList(
                                            SyntaxFactory.XmlTextLiteral(
                                                SyntaxFactory.TriviaList(
                                                    SyntaxFactory.DocumentationCommentExterior(
                                                        @"///")),
                                                @" ",
                                                @" ",
                                                SyntaxFactory.TriviaList()))),
                                    SyntaxFactory.XmlElement(
                                        SyntaxFactory.XmlElementStartTag(
                                            SyntaxFactory.XmlName(
                                                SyntaxFactory.Identifier(
                                                    @"summary"))),
                                        SyntaxFactory.XmlElementEndTag(
                                            SyntaxFactory.XmlName(
                                                SyntaxFactory.Identifier(
                                                    @"summary"))))
                                    .WithContent(
                                        SyntaxFactory.SingletonList<XmlNodeSyntax>(
                                            SyntaxFactory.XmlText()
                                            .WithTextTokens(
                                                SyntaxFactory.TokenList(
                                                    new []{
                                                        SyntaxFactory.XmlTextNewLine(
                                                            SyntaxFactory.TriviaList(),
                                                            @"
",
                                                            @"
",
                                                            SyntaxFactory.TriviaList()),
                                                        SyntaxFactory.XmlTextLiteral(
                                                            SyntaxFactory.TriviaList(
                                                                SyntaxFactory.DocumentationCommentExterior(
                                                                    @"///")),
                                                            @" Some plain text here.",
                                                            @" Some plain text here.",
                                                            SyntaxFactory.TriviaList()),
                                                        SyntaxFactory.XmlTextNewLine(
                                                            SyntaxFactory.TriviaList(),
                                                            @"
",
                                                            @"
",
                                                            SyntaxFactory.TriviaList()),
                                                        SyntaxFactory.XmlTextLiteral(
                                                            SyntaxFactory.TriviaList(
                                                                SyntaxFactory.DocumentationCommentExterior(
                                                                    @"///")),
                                                            @" ",
                                                            @" ",
                                                            SyntaxFactory.TriviaList())})))),
                                    SyntaxFactory.XmlText()
                                    .WithTextTokens(
                                        SyntaxFactory.TokenList(
                                            SyntaxFactory.XmlTextNewLine(
                                                SyntaxFactory.TriviaList(),
                                                @"
",
                                                @"
",
                                                SyntaxFactory.TriviaList())))})))),
                SyntaxKind.ClassKeyword,
                SyntaxFactory.TriviaList()))))
1
On

try this one:

var doc = @"
/// <summary>
/// Some plain text here.
/// </summary>
";
return SyntaxFactory.Comment(doc);
0
On

As already noted parsing works and you can build yourself with the necessary XmlText newline.

Here is an extension method class that you can base on. Note that there are classes for the different documentation xml elements. For instance XmlValueElement and XmlSummaryElement below.

public static class XmlComments
{
    private static TMember AddSimple<TMember>(this TMember member,XmlElementSyntax xmlElement) where TMember:MemberDeclarationSyntax
    {
        return member.WithLeadingTrivia(
                TriviaList(
                    Trivia(
                        DocumentationComment(
                            xmlElement,
                            XmlText().WithTextTokens(
                                TokenList(
                                    Token(
                                        TriviaList(),
                                        SyntaxKind.XmlTextLiteralNewLineToken,
                                        Environment.NewLine,
                                        Environment.NewLine,
                                        TriviaList()
                                    )
                                )
                            )
                        )
                    )
                )
            );
    }
    public static PropertyDeclarationSyntax AddValue(this PropertyDeclarationSyntax property,string value)
    {
        return property.AddSimple(
            XmlValueElement(
                XmlText(value)
            ));
    }
    public static TMember AddSummary<TMember>(this TMember member, string value) where TMember: MemberDeclarationSyntax
    {
        return member.AddSimple(
            XmlSummaryElement(
                XmlText(value)
            ));
    }
}
0
On

Here's what I ended up doing:

var tokens = docText.Split('\n')
    .Select(line => XmlTextLiteral(line))
    .ToList();
for(int i = 1; i < tokens.Count; i += 2)
    tokens.Insert(i, XmlTextNewLine("\n"));

var summary = XmlElement("summary",
    SingletonList<XmlNodeSyntax>(XmlText(TokenList(tokens))));
SyntaxTriviaList doc = TriviaList(
    Trivia(DocumentationComment(summary, XmlText("\n"))));
return member.WithLeadingTrivia(doc);

As you noticed, one needs to manually insert XmlTextNewLine for every line separator in summary. The final XmlText("\n") is there to ensure member definition actually starts on a new line after /// </summary>

I have not actually tested that in Visual Studio (e.g. haven't checked tooltips out). You might need to generate <p>, <para>, <br/>, or something like that around every newline