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))); }
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 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)))); }