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()); }
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()); }
public void TestReturnsElement() { string expected = "<returns>\r\n" + "/// Returns.\r\n" + "/// </returns>"; Assert.Equal(expected, XmlSyntaxFactory.ReturnsElement("\r\n", XmlSyntaxFactory.Text("Returns.")).ToFullString()); }
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()); }
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))); }
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))); }
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))); }
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)); }
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); }
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)); }
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)); }
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)))); }
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()); }