Example #1
0
        private static Document InsertInheritdocComment(Document document, SyntaxNode root, SyntaxNode syntaxNode, CancellationToken cancellationToken)
        {
            // Currently unused
            _ = cancellationToken;

            SyntaxTriviaList leadingTrivia = syntaxNode.GetLeadingTrivia();
            int insertionIndex             = leadingTrivia.Count;

            while (insertionIndex > 0 && !leadingTrivia[insertionIndex - 1].HasBuiltinEndLine())
            {
                insertionIndex--;
            }

            string newLineText          = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);
            var    documentationComment =
                XmlSyntaxFactory.DocumentationComment(
                    newLineText,
                    XmlSyntaxFactory.EmptyElement(XmlCommentHelper.InheritdocXmlTag));
            var trivia = SyntaxFactory.Trivia(documentationComment);

            SyntaxTriviaList newLeadingTrivia = leadingTrivia.Insert(insertionIndex, trivia);
            SyntaxNode       newElement       = syntaxNode.WithLeadingTrivia(newLeadingTrivia);

            return(document.WithSyntaxRoot(root.ReplaceNode(syntaxNode, newElement)));
        }
        public void TestTextAttributeQuotes()
        {
            SyntaxTokenList textTokens = SyntaxFactory.TokenList(XmlSyntaxFactory.TextLiteral("value"));

            Assert.Equal(" name=\"value\"", XmlSyntaxFactory.TextAttribute("name", SyntaxKind.DoubleQuoteToken, textTokens).ToFullString());
            Assert.Equal(" name='value'", XmlSyntaxFactory.TextAttribute("name", SyntaxKind.SingleQuoteToken, textTokens).ToFullString());
        }
        private SyntaxNode RenderBlockElementAsMarkdown(SyntaxNode originalNode, SyntaxNode rewrittenNode, string newLineText, ISymbol documentedSymbol)
        {
            if (!(rewrittenNode is XmlElementSyntax elementSyntax))
            {
                return(rewrittenNode);
            }

            switch (elementSyntax.StartTag?.Name?.ToString())
            {
            case XmlCommentHelper.SummaryXmlTag:
            case XmlCommentHelper.RemarksXmlTag:
            case XmlCommentHelper.ExampleXmlTag:
            case XmlCommentHelper.ReturnsXmlTag:
            case XmlCommentHelper.ValueXmlTag:
                break;

            default:
                return(rewrittenNode);
            }

            string rendered = RenderAsMarkdown(elementSyntax.Content.ToString(), documentedSymbol).Trim();

            return(elementSyntax.WithContent(
                       XmlSyntaxFactory.List(
                           XmlSyntaxFactory.NewLine(newLineText).WithoutTrailingTrivia(),
                           XmlSyntaxFactory.Text(" " + rendered.Replace("\n", "\n "), xmlEscape: false),
                           XmlSyntaxFactory.NewLine(newLineText).WithoutTrailingTrivia(),
                           XmlSyntaxFactory.Text(" "))));
        }
 public void TestTextNewLine()
 {
     Assert.Equal("\r\n/// ", XmlSyntaxFactory.TextNewLine("\r\n", true).ToFullString());
     Assert.Equal("\r\n", XmlSyntaxFactory.TextNewLine("\r\n", false).ToFullString());
     Assert.Equal("\n/// ", XmlSyntaxFactory.TextNewLine("\n", true).ToFullString());
     Assert.Equal("\n", XmlSyntaxFactory.TextNewLine("\n", false).ToFullString());
 }
Example #5
0
 private static SyntaxList <XmlNodeSyntax> BuildStandardText(SyntaxToken identifier, TypeParameterListSyntax typeParameters, string newLineText, string preText, string postText)
 {
     return(XmlSyntaxFactory.List(
                XmlSyntaxFactory.NewLine(newLineText),
                XmlSyntaxFactory.Text(preText),
                BuildSeeElement(identifier, typeParameters),
                XmlSyntaxFactory.Text(postText.EndsWith(".") ? postText : (postText + "."))));
 }
        public void TestSummaryElement()
        {
            string expected =
                "<summary>\r\n"
                + "/// Summary.\r\n"
                + "/// </summary>";

            Assert.Equal(expected, XmlSyntaxFactory.SummaryElement("\r\n", XmlSyntaxFactory.Text("Summary.")).ToFullString());
        }
        public void TestEmptySummaryElement()
        {
            string expected =
                "<summary>\r\n"
                + "///\r\n"
                + "/// </summary>";

            Assert.Equal(expected, XmlSyntaxFactory.SummaryElement("\r\n").ToFullString());
        }
        public void TestValueElement()
        {
            string expected =
                "<value>\r\n"
                + "/// Value.\r\n"
                + "/// </value>";

            Assert.Equal(expected, XmlSyntaxFactory.ValueElement("\r\n", XmlSyntaxFactory.Text("Value.")).ToFullString());
        }
Example #9
0
        public void TestReturnsElement()
        {
            string expected =
                "<returns>\r\n"
                + "/// Returns.\r\n"
                + "/// </returns>";

            Assert.Equal(expected, XmlSyntaxFactory.ReturnsElement("\r\n", XmlSyntaxFactory.Text("Returns.")).ToFullString());
        }
Example #10
0
        internal static SyntaxList <XmlNodeSyntax> BuildStandardTextSyntaxList(BaseTypeDeclarationSyntax typeDeclaration, string preText, string postText)
        {
            TypeParameterListSyntax typeParameterList = GetTypeParameterList(typeDeclaration);

            return(XmlSyntaxFactory.List(
                       XmlSyntaxFactory.Text(preText),
                       BuildSeeElement(typeDeclaration.Identifier, typeParameterList),
                       XmlSyntaxFactory.Text(postText.EndsWith(".") ? postText : (postText + "."))));
        }
        public void TestRemarksElement()
        {
            string expected =
                "<remarks>\r\n"
                + "/// <para>Remarks.</para>\r\n"
                + "/// </remarks>";

            Assert.Equal(expected, XmlSyntaxFactory.RemarksElement("\r\n", XmlSyntaxFactory.ParaElement(XmlSyntaxFactory.Text("Remarks."))).ToFullString());
        }
Example #12
0
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var xmlElement = (XmlElementSyntax)root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true);

            var newXmlElement = XmlSyntaxFactory.TypeParamRefElement(xmlElement.Content.ToFullString()).WithTriviaFrom(xmlElement);

            return(document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)));
        }
Example #13
0
 public void GenerateSummarySeeAlsoCommentBenchmark()
 {
     for (var i = 0; i < _testValues.Length * 5; ++i)
     {
         var currentIndex  = i % _testValues.Length;
         var testValue     = _testValues[currentIndex];
         var testValueType = _testValuesTypes[currentIndex];
         var syntax        = XmlSyntaxFactory.GenerateSummarySeeAlsoComment(testValue, testValueType);
     }
 }
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxToken token = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true);

            var xmlElement    = token.Parent.FirstAncestorOrSelf <XmlElementSyntax>();
            var newXmlElement = xmlElement.WithContent(XmlSyntaxFactory.List(XmlSyntaxFactory.Element(XmlCommentHelper.DescriptionXmlTag, xmlElement.Content)));

            return(document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)));
        }
        private static IEnumerable <XmlNodeSyntax> ExpandTextNodes(XmlNodeSyntax node)
        {
            if (!(node is XmlTextSyntax xmlTextSyntax))
            {
                yield return(node);

                yield break;
            }

            foreach (var textToken in xmlTextSyntax.TextTokens)
            {
                yield return(XmlSyntaxFactory.Text(textToken));
            }
        }
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var xmlElement = (XmlElementSyntax)root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true);

            var newXmlElement = XmlSyntaxFactory.EmptyElement(XmlCommentHelper.SeeXmlTag)
                                .AddAttributes(XmlSyntaxFactory.TextAttribute(
                                                   "cref",
                                                   SyntaxFactory.Token(SyntaxTriviaList.Empty, SyntaxKind.XmlTextLiteralToken, xmlElement.Content.ToFullString(), xmlElement.Content.ToFullString(), SyntaxTriviaList.Empty)))
                                .WithTriviaFrom(xmlElement);

            return(document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)));
        }
Example #17
0
        private static XmlEmptyElementSyntax BuildSeeElement(SyntaxToken identifier, TypeParameterListSyntax typeParameters)
        {
            TypeSyntax identifierName;

            // Get a TypeSyntax representing the class name with its type parameters
            if (typeParameters == null || !typeParameters.Parameters.Any())
            {
                identifierName = SyntaxFactory.IdentifierName(identifier.WithoutTrivia());
            }
            else
            {
                identifierName = SyntaxFactory.GenericName(identifier.WithoutTrivia(), ParameterToArgumentListSyntax(typeParameters));
            }

            return(XmlSyntaxFactory.SeeElement(SyntaxFactory.TypeCref(identifierName)));
        }
Example #18
0
        internal static SyntaxList <XmlNodeSyntax> BuildStandardTextSyntaxList(BaseTypeDeclarationSyntax typeDeclaration, string newLineText, string preText, string postText)
        {
            TypeParameterListSyntax typeParameterList;

            if (typeDeclaration is ClassDeclarationSyntax classDeclaration)
            {
                typeParameterList = classDeclaration.TypeParameterList;
            }
            else
            {
                typeParameterList = (typeDeclaration as StructDeclarationSyntax)?.TypeParameterList;
            }

            return(XmlSyntaxFactory.List(
                       XmlSyntaxFactory.NewLine(newLineText),
                       XmlSyntaxFactory.Text(preText),
                       BuildSeeElement(typeDeclaration.Identifier, typeParameterList),
                       XmlSyntaxFactory.Text(postText.EndsWith(".") ? postText : (postText + "."))));
        }
        private static SyntaxList <XmlNodeSyntax> BuildStandardText(SyntaxToken identifier, TypeParameterListSyntax typeParameters, string newLineText, string preText, string postText)
        {
            TypeSyntax identifierName;

            // Get a TypeSyntax representing the class name with its type parameters
            if (typeParameters == null || !typeParameters.Parameters.Any())
            {
                identifierName = SyntaxFactory.IdentifierName(identifier.WithoutTrivia());
            }
            else
            {
                identifierName = SyntaxFactory.GenericName(identifier.WithoutTrivia(), ParameterToArgumentListSyntax(typeParameters));
            }

            return(XmlSyntaxFactory.List(
                       XmlSyntaxFactory.NewLine(newLineText),
                       XmlSyntaxFactory.Text(preText),
                       XmlSyntaxFactory.SeeElement(SyntaxFactory.TypeCref(identifierName)),
                       XmlSyntaxFactory.Text(postText.EndsWith(".") ? postText : (postText + "."))));
        }
        private static Task <Document> GetTransformedDocumentAsync(Document document, SyntaxNode root, Diagnostic diagnostic)
        {
            var         token = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true);
            SyntaxToken updatedToken;

            switch (token.Kind())
            {
            case SyntaxKind.XmlTextLiteralToken:
                updatedToken = XmlSyntaxFactory.TextLiteral(" " + token.Text.TrimStart(' ')).WithTriviaFrom(token);
                break;

            default:
                updatedToken = token.WithLeadingTrivia(token.LeadingTrivia.Add(SyntaxFactory.Space));
                break;
            }

            Document updatedDocument = document.WithSyntaxRoot(root.ReplaceToken(token, updatedToken));

            return(Task.FromResult(updatedDocument));
        }
Example #21
0
        private static SyntaxList <XmlNodeSyntax> RemoveTrailingEmptyLines(SyntaxList <XmlNodeSyntax> content)
        {
            if (!(content[content.Count - 1] is XmlTextSyntax xmlText))
            {
                return(content);
            }

            // skip the last token, as it contains the documentation comment for the closing tag, which needs to remain.
            var firstEmptyToken = -1;

            for (var j = xmlText.TextTokens.Count - 2; j >= 0; j--)
            {
                var textToken = xmlText.TextTokens[j];

                if (textToken.IsXmlWhitespace())
                {
                    firstEmptyToken = j;
                }
                else if (textToken.IsXmlNewLine() && textToken.LeadingTrivia.Any(SyntaxKind.DocumentationCommentExteriorTrivia))
                {
                    // Skip completely blank lines
                    firstEmptyToken = j;
                }
                else if (textToken.IsKind(SyntaxKind.XmlTextLiteralToken) && !string.IsNullOrWhiteSpace(textToken.Text))
                {
                    break;
                }
            }

            if (firstEmptyToken > -1)
            {
                var newContent = content.RemoveAt(content.Count - 1);
                newContent = newContent
                             .Add(XmlSyntaxFactory.Text(xmlText.TextTokens.Take(firstEmptyToken).ToArray()))
                             .Add(XmlSyntaxFactory.Text(xmlText.TextTokens.Last()));
                return(newContent);
            }

            return(content);
        }
Example #22
0
        private static Task <Document> GetConstructorOrDestructorDocumentationTransformedDocumentAsync(Document document, SyntaxNode root, BaseMethodDeclarationSyntax declaration, CancellationToken cancellationToken)
        {
            SyntaxTriviaList leadingTrivia = declaration.GetLeadingTrivia();
            int insertionIndex             = GetInsertionIndex(ref leadingTrivia);

            string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);

            var documentationNodes = new List <XmlNodeSyntax>();

            var typeDeclaration        = declaration.FirstAncestorOrSelf <BaseTypeDeclarationSyntax>();
            var standardText           = SA1642SA1643CodeFixProvider.GenerateStandardText(document, declaration, typeDeclaration, cancellationToken);
            var standardTextSyntaxList = SA1642SA1643CodeFixProvider.BuildStandardTextSyntaxList(typeDeclaration, newLineText, standardText[0], standardText[1]);

            // Remove the empty line generated by build standard text, as this is not needed with constructing a new summary element.
            standardTextSyntaxList = standardTextSyntaxList.RemoveAt(0);

            documentationNodes.Add(XmlSyntaxFactory.SummaryElement(newLineText, standardTextSyntaxList));

            if (declaration.ParameterList != null)
            {
                foreach (var parameter in declaration.ParameterList.Parameters)
                {
                    documentationNodes.Add(XmlSyntaxFactory.NewLine(newLineText));
                    documentationNodes.Add(XmlSyntaxFactory.ParamElement(parameter.Identifier.ValueText));
                }
            }

            var documentationComment =
                XmlSyntaxFactory.DocumentationComment(
                    newLineText,
                    documentationNodes.ToArray());
            var trivia = SyntaxFactory.Trivia(documentationComment);

            SyntaxTriviaList newLeadingTrivia = leadingTrivia.Insert(insertionIndex, trivia);
            SyntaxNode       newElement       = declaration.WithLeadingTrivia(newLeadingTrivia);

            return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(declaration, newElement))));
        }
        /// <summary>
        /// Generates an observable declaration that wraps a event.
        /// </summary>
        /// <param name="eventDetails">The details of the event to wrap.</param>
        /// <param name="dataObjectName">The name of the item where the event is stored.</param>
        /// <param name="prefix">A prefix to append to the name.</param>
        /// <returns>The property declaration.</returns>
        protected static PropertyDeclarationSyntax?GenerateEventWrapperObservable(IEventSymbol eventDetails, string dataObjectName, string?prefix)
        {
            prefix ??= string.Empty;

            // Create "Observable.FromEvent" for our method.
            var(expressionBody, observableEventArgType) = GenerateFromEventExpression(eventDetails, dataObjectName);

            if (observableEventArgType == null || expressionBody == null)
            {
                return(null);
            }

            var modifiers = eventDetails.IsStatic
                ? new[] { SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword }
                : new[] { SyntaxKind.PublicKeyword };

            var attributes = RoslynHelpers.GenerateObsoleteAttributeList(eventDetails);

            // Produces for static: public static global::System.IObservable<(argType1, argType2)> EventName => (contents of expression body)
            // Produces for instance: public global::System.IObservable<(argType1, argType2)> EventName => (contents of expression body)
            return(PropertyDeclaration(observableEventArgType, prefix + eventDetails.Name, attributes, modifiers, expressionBody, 2)
                   .WithLeadingTrivia(XmlSyntaxFactory.GenerateSummarySeeAlsoComment("Gets an observable which signals when the {0} event triggers.", eventDetails.ConvertToDocument()))
                   .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
        }
        private static XmlElementSyntax TrimWhitespaceContent(XmlElementSyntax paragraph, out SyntaxList <XmlNodeSyntax> leadingWhitespaceContent, out SyntaxList <XmlNodeSyntax> trailingWhitespaceContent)
        {
            SyntaxList <XmlNodeSyntax> completeContent = XmlSyntaxFactory.List(paragraph.Content.SelectMany(ExpandTextNodes).ToArray());

            leadingWhitespaceContent  = XmlSyntaxFactory.List(completeContent.TakeWhile(x => XmlCommentHelper.IsConsideredEmpty(x)).ToArray());
            trailingWhitespaceContent = XmlSyntaxFactory.List(completeContent.Skip(leadingWhitespaceContent.Count).Reverse().TakeWhile(x => XmlCommentHelper.IsConsideredEmpty(x)).Reverse().ToArray());

            SyntaxList <XmlNodeSyntax> trimmedContent = XmlSyntaxFactory.List(completeContent.Skip(leadingWhitespaceContent.Count).Take(completeContent.Count - leadingWhitespaceContent.Count - trailingWhitespaceContent.Count).ToArray());
            SyntaxTriviaList           leadingTrivia  = SyntaxFactory.TriviaList();
            SyntaxTriviaList           trailingTrivia = SyntaxFactory.TriviaList();

            if (trimmedContent.Any())
            {
                leadingTrivia  = trimmedContent[0].GetLeadingTrivia();
                trailingTrivia = trimmedContent.Last().GetTrailingTrivia();
                trimmedContent = trimmedContent.Replace(trimmedContent[0], trimmedContent[0].WithoutLeadingTrivia());
                trimmedContent = trimmedContent.Replace(trimmedContent.Last(), trimmedContent.Last().WithoutTrailingTrivia());
            }
            else
            {
                leadingTrivia  = SyntaxFactory.TriviaList();
                trailingTrivia = SyntaxFactory.TriviaList();
            }

            XmlElementSyntax result = paragraph;

            if (leadingWhitespaceContent.Any())
            {
                var first    = leadingWhitespaceContent[0];
                var newFirst = first.WithLeadingTrivia(first.GetLeadingTrivia().InsertRange(0, paragraph.GetLeadingTrivia()));
                leadingWhitespaceContent = leadingWhitespaceContent.Replace(first, newFirst);
            }
            else
            {
                leadingTrivia = leadingTrivia.InsertRange(0, result.GetLeadingTrivia());
            }

            if (trailingWhitespaceContent.Any())
            {
                var last    = trailingWhitespaceContent.Last();
                var newLast = last.WithLeadingTrivia(last.GetLeadingTrivia().AddRange(paragraph.GetTrailingTrivia()));
                trailingWhitespaceContent = trailingWhitespaceContent.Replace(last, newLast);
            }
            else
            {
                trailingTrivia = trailingTrivia.AddRange(result.GetTrailingTrivia());
            }

            if (trimmedContent.FirstOrDefault() is XmlTextSyntax firstTextNode &&
                firstTextNode.TextTokens.Any())
            {
                SyntaxToken firstTextToken    = firstTextNode.TextTokens[0];
                string      leadingWhitespace = new(firstTextToken.Text.Cast <char>().TakeWhile(char.IsWhiteSpace).ToArray());
                if (leadingWhitespace.Length > 0)
                {
                    SyntaxToken   newFirstTextToken = XmlSyntaxFactory.TextLiteral(firstTextToken.Text.Substring(leadingWhitespace.Length)).WithTriviaFrom(firstTextToken);
                    XmlTextSyntax newFirstTextNode  = firstTextNode.WithTextTokens(firstTextNode.TextTokens.Replace(firstTextToken, newFirstTextToken));
                    trimmedContent = trimmedContent.Replace(firstTextNode, newFirstTextNode);
                    leadingTrivia  = leadingTrivia.Add(SyntaxFactory.Whitespace(leadingWhitespace));
                }
            }

            if (trimmedContent.LastOrDefault() is XmlTextSyntax lastTextNode &&
                lastTextNode.TextTokens.Any())
            {
                SyntaxToken lastTextToken      = lastTextNode.TextTokens.Last();
                string      trailingWhitespace = new(lastTextToken.Text.Cast <char>().Reverse().TakeWhile(char.IsWhiteSpace).Reverse().ToArray());
                if (trailingWhitespace.Length > 0)
                {
                    SyntaxToken   newLastTextToken = XmlSyntaxFactory.TextLiteral(lastTextToken.Text.Substring(0, lastTextToken.Text.Length - trailingWhitespace.Length)).WithTriviaFrom(lastTextToken);
                    XmlTextSyntax newLastTextNode  = lastTextNode.WithTextTokens(lastTextNode.TextTokens.Replace(lastTextToken, newLastTextToken));
                    trimmedContent = trimmedContent.Replace(lastTextNode, newLastTextNode);
                    trailingTrivia = trailingTrivia.Insert(0, SyntaxFactory.Whitespace(trailingWhitespace));
                }
            }

            return(result.WithContent(trimmedContent)
                   .WithLeadingTrivia(leadingTrivia)
                   .WithTrailingTrivia(trailingTrivia));
        }
Example #25
0
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var firstComment = root.FindTrivia(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true);
            var parentToken  = firstComment.Token;

            var lines = new List <string>();

            for (int i = 0; i < parentToken.LeadingTrivia.Count; i++)
            {
                if (!parentToken.LeadingTrivia[i].IsKind(SyntaxKind.SingleLineCommentTrivia) &&
                    !parentToken.LeadingTrivia[i].IsKind(SyntaxKind.MultiLineCommentTrivia))
                {
                    continue;
                }

                if (!diagnostic.Location.SourceSpan.Contains(parentToken.LeadingTrivia[i].Span))
                {
                    continue;
                }

                if (parentToken.LeadingTrivia[i].IsKind(SyntaxKind.SingleLineCommentTrivia))
                {
                    lines.Add(parentToken.LeadingTrivia[i].ToString().Substring(2));
                }
                else
                {
                    var commentText    = parentToken.LeadingTrivia[i].ToString();
                    var normalizedText = commentText.Substring(1, commentText.Length - 3)
                                         .Replace("\r\n", "\n").Replace('\r', '\n');
                    foreach (var line in normalizedText.Split('\n'))
                    {
                        if (Regex.IsMatch(line, "^\\s*\\*"))
                        {
                            lines.Add(line.Substring(line.IndexOf('*') + 1));
                        }
                        else
                        {
                            lines.Add(line);
                        }
                    }

                    lines[lines.Count - 1] = lines[lines.Count - 1].TrimEnd();
                }
            }

            int firstContentLine = lines.FindIndex(line => !string.IsNullOrWhiteSpace(line));

            if (firstContentLine >= 0)
            {
                lines.RemoveRange(0, firstContentLine);
                int lastContentLine = lines.FindLastIndex(line => !string.IsNullOrWhiteSpace(line));
                lines.RemoveRange(lastContentLine + 1, lines.Count - lastContentLine - 1);
            }

            if (lines.All(line => line.Length == 0 || line.StartsWith(" ")))
            {
                for (int i = 0; i < lines.Count; i++)
                {
                    if (lines[i].Length == 0)
                    {
                        continue;
                    }

                    lines[i] = lines[i].Substring(1);
                }
            }

            var nodes = new List <XmlNodeSyntax>(lines.Select(line => XmlSyntaxFactory.Text(line)));

            for (int i = nodes.Count - 1; i > 0; i--)
            {
                nodes.Insert(i, XmlSyntaxFactory.NewLine(Environment.NewLine));
            }

            var summary = XmlSyntaxFactory.SummaryElement(Environment.NewLine, nodes.ToArray());

            var leadingTrivia  = SyntaxFactory.TriviaList(parentToken.LeadingTrivia.TakeWhile(trivia => !trivia.Equals(firstComment)));
            var newParentToken = parentToken.WithLeadingTrivia(leadingTrivia.Add(SyntaxFactory.Trivia(XmlSyntaxFactory.DocumentationComment(Environment.NewLine, summary))));

            var newRoot = root.ReplaceToken(parentToken, newParentToken);

            return(document.WithSyntaxRoot(root.ReplaceToken(parentToken, newParentToken)));
        }
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode syntax = documentRoot.FindNode(diagnostic.Location.SourceSpan);

            if (syntax == null)
            {
                return(document);
            }

            MethodDeclarationSyntax   methodDeclarationSyntax   = syntax.FirstAncestorOrSelf <MethodDeclarationSyntax>();
            DelegateDeclarationSyntax delegateDeclarationSyntax = syntax.FirstAncestorOrSelf <DelegateDeclarationSyntax>();

            if (methodDeclarationSyntax == null && delegateDeclarationSyntax == null)
            {
                return(document);
            }

            DocumentationCommentTriviaSyntax documentationComment =
                methodDeclarationSyntax?.GetDocumentationCommentTriviaSyntax()
                ?? delegateDeclarationSyntax?.GetDocumentationCommentTriviaSyntax();

            if (documentationComment == null)
            {
                return(document);
            }

            SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            bool isTask;
            bool isAsynchronousTestMethod;

            if (methodDeclarationSyntax != null)
            {
                isTask = IsTaskReturningMethod(semanticModel, methodDeclarationSyntax, cancellationToken);
                isAsynchronousTestMethod = isTask && IsAsynchronousTestMethod(semanticModel, methodDeclarationSyntax, cancellationToken);
            }
            else
            {
                isTask = IsTaskReturningMethod(semanticModel, delegateDeclarationSyntax, cancellationToken);
                isAsynchronousTestMethod = false;
            }

            XmlNodeSyntax returnsElement = documentationComment.Content.GetFirstXmlElement(XmlCommentHelper.ReturnsXmlTag) as XmlNodeSyntax;

            if (returnsElement != null && !isTask)
            {
                // This code fix doesn't know how to do anything more than document Task-returning methods.
                return(document);
            }

            SyntaxList <XmlNodeSyntax> content = XmlSyntaxFactory.List();

            if (isTask)
            {
                content = content.Add(XmlSyntaxFactory.Text("A "));
                content = content.Add(XmlSyntaxFactory.SeeElement(SyntaxFactory.TypeCref(SyntaxFactory.ParseTypeName("global::System.Threading.Tasks.Task"))).WithAdditionalAnnotations(Simplifier.Annotation));
                string operationKind = isAsynchronousTestMethod ? "unit test" : "operation";
                content = content.Add(XmlSyntaxFactory.Text($" representing the asynchronous {operationKind}."));

                // wrap the generated content in a <placeholder> element for review.
                content = XmlSyntaxFactory.List(XmlSyntaxFactory.PlaceholderElement(content));
            }

            // Try to replace an existing <returns> element if the comment contains one. Otherwise, add it as a new element.
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode newRoot;

            if (returnsElement != null)
            {
                XmlEmptyElementSyntax emptyElement = returnsElement as XmlEmptyElementSyntax;
                if (emptyElement != null)
                {
                    XmlElementSyntax updatedReturns = XmlSyntaxFactory.Element(XmlCommentHelper.ReturnsXmlTag, content)
                                                      .WithLeadingTrivia(returnsElement.GetLeadingTrivia())
                                                      .WithTrailingTrivia(returnsElement.GetTrailingTrivia());
                    newRoot = root.ReplaceNode(returnsElement, updatedReturns);
                }
                else
                {
                    XmlElementSyntax updatedReturns = ((XmlElementSyntax)returnsElement).WithContent(content);
                    newRoot = root.ReplaceNode(returnsElement, updatedReturns);
                }
            }
            else
            {
                string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);

                returnsElement = XmlSyntaxFactory.Element(XmlCommentHelper.ReturnsXmlTag, content);

                XmlNodeSyntax leadingNewLine = XmlSyntaxFactory.NewLine(newLineText);

                // HACK: The formatter isn't working when contents are added to an existing documentation comment, so we
                // manually apply the indentation from the last line of the existing comment to each new line of the
                // generated content.
                SyntaxTrivia exteriorTrivia = GetLastDocumentationCommentExteriorTrivia(documentationComment);
                if (!exteriorTrivia.Token.IsMissing)
                {
                    leadingNewLine = leadingNewLine.ReplaceExteriorTrivia(exteriorTrivia);
                    returnsElement = returnsElement.ReplaceExteriorTrivia(exteriorTrivia);
                }

                DocumentationCommentTriviaSyntax newDocumentationComment = documentationComment.WithContent(
                    documentationComment.Content.InsertRange(
                        documentationComment.Content.Count - 1,
                        XmlSyntaxFactory.List(leadingNewLine, returnsElement)));

                newRoot = root.ReplaceNode(documentationComment, newDocumentationComment);
            }

            return(document.WithSyntaxRoot(newRoot));
        }
Example #27
0
        private static Task <Document> GetMethodDocumentationTransformedDocumentAsync(Document document, SyntaxNode root, SemanticModel semanticModel, MethodDeclarationSyntax methodDeclaration, CancellationToken cancellationToken)
        {
            SyntaxTriviaList leadingTrivia = methodDeclaration.GetLeadingTrivia();
            int insertionIndex             = GetInsertionIndex(ref leadingTrivia);

            string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);

            var documentationNodes = new List <XmlNodeSyntax>();

            documentationNodes.Add(XmlSyntaxFactory.SummaryElement(newLineText));

            if (methodDeclaration.TypeParameterList != null)
            {
                foreach (var typeParameter in methodDeclaration.TypeParameterList.Parameters)
                {
                    documentationNodes.Add(XmlSyntaxFactory.NewLine(newLineText));
                    documentationNodes.Add(XmlSyntaxFactory.TypeParamElement(typeParameter.Identifier.ValueText));
                }
            }

            if (methodDeclaration.ParameterList != null)
            {
                foreach (var parameter in methodDeclaration.ParameterList.Parameters)
                {
                    documentationNodes.Add(XmlSyntaxFactory.NewLine(newLineText));
                    documentationNodes.Add(XmlSyntaxFactory.ParamElement(parameter.Identifier.ValueText));
                }
            }

            TypeSyntax typeName;

            var typeSymbol = semanticModel.GetSymbolInfo(methodDeclaration.ReturnType, cancellationToken).Symbol as INamedTypeSymbol;

            if (typeSymbol.IsGenericType)
            {
                typeName = SyntaxFactory.ParseTypeName("global::System.Threading.Tasks.Task<TResult>");
            }
            else
            {
                typeName = SyntaxFactory.ParseTypeName("global::System.Threading.Tasks.Task");
            }

            XmlNodeSyntax[] returnContent =
            {
                XmlSyntaxFactory.Text(DocumentationResources.TaskReturnElementFirstPart),
                XmlSyntaxFactory.SeeElement(SyntaxFactory.TypeCref(typeName)).WithAdditionalAnnotations(Simplifier.Annotation),
                XmlSyntaxFactory.Text(DocumentationResources.TaskReturnElementSecondPart),
            };

            documentationNodes.Add(XmlSyntaxFactory.NewLine(newLineText));
            documentationNodes.Add(XmlSyntaxFactory.ReturnsElement(returnContent));

            var documentationComment =
                XmlSyntaxFactory.DocumentationComment(
                    newLineText,
                    documentationNodes.ToArray());
            var trivia = SyntaxFactory.Trivia(documentationComment);

            SyntaxTriviaList newLeadingTrivia = leadingTrivia.Insert(insertionIndex, trivia);
            SyntaxNode       newElement       = methodDeclaration.WithLeadingTrivia(newLeadingTrivia);

            return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(methodDeclaration, newElement))));
        }
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode enclosingNode = root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true);

            if (!(enclosingNode is XmlNodeSyntax xmlNodeSyntax))
            {
                return(document);
            }

            XmlElementSyntax xmlElementSyntax = xmlNodeSyntax.FirstAncestorOrSelf <XmlElementSyntax>();

            if (xmlElementSyntax == null)
            {
                return(document);
            }

            SyntaxToken startToken = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true);

            if (!(startToken.Parent is XmlNodeSyntax startNode) || startNode == xmlElementSyntax)
            {
                return(document);
            }

            while (startNode.Parent != xmlElementSyntax)
            {
                startNode = startNode.Parent as XmlNodeSyntax;
                if (startNode == null)
                {
                    return(document);
                }
            }

            SyntaxToken stopToken = root.FindToken(diagnostic.Location.SourceSpan.End - 1, findInsideTrivia: true);

            if (!(stopToken.Parent is XmlNodeSyntax stopNode) || stopNode == xmlElementSyntax)
            {
                return(document);
            }

            while (stopNode.Parent != xmlElementSyntax)
            {
                stopNode = stopNode.Parent as XmlNodeSyntax;
                if (stopNode == null)
                {
                    return(document);
                }
            }

            int startIndex = xmlElementSyntax.Content.IndexOf(startNode);
            int stopIndex  = xmlElementSyntax.Content.IndexOf(stopNode);

            if (startIndex < 0 || stopIndex < 0)
            {
                return(document);
            }

            XmlElementSyntax           paragraph = XmlSyntaxFactory.ParaElement(xmlElementSyntax.Content.Skip(startIndex).Take(stopIndex - startIndex + 1).ToArray());
            SyntaxList <XmlNodeSyntax> leadingWhitespaceContent;
            SyntaxList <XmlNodeSyntax> trailingWhitespaceContent;

            paragraph = TrimWhitespaceContent(paragraph, out leadingWhitespaceContent, out trailingWhitespaceContent);

            SyntaxList <XmlNodeSyntax> newContent = XmlSyntaxFactory.List();

            newContent = newContent.AddRange(xmlElementSyntax.Content.Take(startIndex));
            newContent = newContent.AddRange(leadingWhitespaceContent);
            newContent = newContent.Add(paragraph);
            newContent = newContent.AddRange(trailingWhitespaceContent);
            newContent = newContent.AddRange(xmlElementSyntax.Content.Skip(stopIndex + 1));
            return(document.WithSyntaxRoot(root.ReplaceNode(xmlElementSyntax, xmlElementSyntax.WithContent(newContent))));
        }
Example #29
0
        private async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode syntax = documentRoot.FindNode(diagnostic.Location.SourceSpan);

            if (syntax == null)
            {
                return(document);
            }

            PropertyDeclarationSyntax propertyDeclarationSyntax = syntax.FirstAncestorOrSelf <PropertyDeclarationSyntax>();

            if (propertyDeclarationSyntax == null)
            {
                return(document);
            }

            DocumentationCommentTriviaSyntax documentationComment = propertyDeclarationSyntax.GetDocumentationCommentTriviaSyntax();

            if (documentationComment == null)
            {
                return(document);
            }

            if (!(documentationComment.Content.GetFirstXmlElement(XmlCommentHelper.SummaryXmlTag) is XmlElementSyntax summaryElement))
            {
                return(document);
            }

            SyntaxList <XmlNodeSyntax> summaryContent = summaryElement.Content;

            if (!this.TryRemoveSummaryPrefix(ref summaryContent, "Gets or sets "))
            {
                if (!this.TryRemoveSummaryPrefix(ref summaryContent, "Gets "))
                {
                    this.TryRemoveSummaryPrefix(ref summaryContent, "Sets ");
                }
            }

            SyntaxList <XmlNodeSyntax> content = summaryContent.WithoutFirstAndLastNewlines();

            if (!string.IsNullOrWhiteSpace(content.ToFullString()))
            {
                // wrap the content in a <placeholder> element for review
                content = XmlSyntaxFactory.List(XmlSyntaxFactory.PlaceholderElement(content));
            }

            string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);

            XmlElementSyntax valueElement = XmlSyntaxFactory.MultiLineElement(XmlCommentHelper.ValueXmlTag, newLineText, content);

            XmlNodeSyntax leadingNewLine = XmlSyntaxFactory.NewLine(newLineText);

            // HACK: The formatter isn't working when contents are added to an existing documentation comment, so we
            // manually apply the indentation from the last line of the existing comment to each new line of the
            // generated content.
            SyntaxTrivia exteriorTrivia = GetLastDocumentationCommentExteriorTrivia(documentationComment);

            if (!exteriorTrivia.Token.IsMissing)
            {
                leadingNewLine = leadingNewLine.ReplaceExteriorTrivia(exteriorTrivia);
                valueElement   = valueElement.ReplaceExteriorTrivia(exteriorTrivia);
            }

            // Try to replace an existing <value> element if the comment contains one. Otherwise, add it as a new element.
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode    newRoot;
            XmlNodeSyntax existingValue = documentationComment.Content.GetFirstXmlElement(XmlCommentHelper.ValueXmlTag);

            if (existingValue != null)
            {
                newRoot = root.ReplaceNode(existingValue, valueElement);
            }
            else
            {
                DocumentationCommentTriviaSyntax newDocumentationComment = documentationComment.WithContent(
                    documentationComment.Content.InsertRange(
                        documentationComment.Content.Count - 1,
                        XmlSyntaxFactory.List(leadingNewLine, valueElement)));

                newRoot = root.ReplaceNode(documentationComment, newDocumentationComment);
            }

            return(document.WithSyntaxRoot(newRoot));
        }
 public void TestNameAttribute()
 {
     Assert.Equal(" name=\"value\"", XmlSyntaxFactory.NameAttribute("value").ToFullString());
 }