/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, Location[] diagnosticLocations) { if (syntax == null) { return; } if (completeDocumentation != null) { XElement summaryNode = completeDocumentation.Nodes().OfType<XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.SummaryXmlTag); if (summaryNode == null) { // Handled by SA1604 return; } if (!XmlCommentHelper.IsConsideredEmpty(summaryNode)) { return; } } else { if (!XmlCommentHelper.IsConsideredEmpty(syntax)) { return; } } foreach (var location in diagnosticLocations) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } }
/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, Location[] diagnosticLocations) { if (completeDocumentation != null) { // We are working with an <include> element if (completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.SummaryXmlTag)) { return; } if (completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag)) { // Ignore nodes with an <inheritdoc/> tag in the included XML. return; } } else { if (syntax != null) { return; } if (documentation?.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag) != null) { // Ignore nodes with an <inheritdoc/> tag. return; } } foreach (var location in diagnosticLocations) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } }
private void ClassifyDocumentationComment(DocumentationCommentTriviaSyntax documentationComment) { if (!_textSpan.OverlapsWith(documentationComment.Span)) { return; } foreach (var xmlNode in documentationComment.Content) { var childFullSpan = xmlNode.FullSpan; if (childFullSpan.Start > _textSpan.End) { return; } else if (childFullSpan.End < _textSpan.Start) { continue; } ClassifyXmlNode(xmlNode); } // NOTE: the "EndOfComment" token is a special, zero width token. However, if it's a multi-line xml doc comment // the final '*/" will be leading exterior trivia on it. ClassifyXmlTrivia(documentationComment.EndOfComment.LeadingTrivia); }
private static IEnumerable<SyntaxNode> GetAllNodesToRemove(IEnumerable<Tuple<ParameterSyntax, Tuple<XmlElementSyntax, XmlNameAttributeSyntax>>> paramterWithDocParameter, DocumentationCommentTriviaSyntax documentationNode) { var nodesToRemove = paramterWithDocParameter.Where(p => p.Item1 == null).Select(x => x.Item2.Item1).ToList(); var xmlTextNodesToRemove = documentationNode.Content.OfType<XmlTextSyntax>() .Join(nodesToRemove, textNode => textNode.FullSpan.End, tagNode => tagNode.FullSpan.Start, (textNode, tagNode) => textNode); var allNodesToRemove = nodesToRemove.Cast<SyntaxNode>().Union(xmlTextNodesToRemove); return allNodesToRemove; }
private static MethodDeclarationSyntax GetMethodFromXmlDocumentation(DocumentationCommentTriviaSyntax doc) { var tokenParent = doc.ParentTrivia.Token.Parent; var method = tokenParent as MethodDeclarationSyntax; if (method == null) { var attributeList = tokenParent as AttributeListSyntax; if (attributeList == null) return null; method = attributeList.Parent as MethodDeclarationSyntax; } return method; }
protected static IEnumerable<Tuple<ParameterSyntax, Tuple<XmlElementSyntax, XmlNameAttributeSyntax>>> GetMethodParametersWithDocParameters(MethodDeclarationSyntax method, DocumentationCommentTriviaSyntax documentationNode) { var methodParameters = method.ParameterList.Parameters; var xElementsWitAttrs = documentationNode.Content.OfType<XmlElementSyntax>() .Where(xEle => xEle.StartTag.Name.LocalName.ValueText == "param") .SelectMany(xEle => xEle.StartTag.Attributes, (xEle, attr) => new Tuple<XmlElementSyntax, XmlNameAttributeSyntax>(xEle, (XmlNameAttributeSyntax)attr)); var keys = methodParameters.Select(parameter => parameter.Identifier.ValueText) .Union(xElementsWitAttrs.Select(x => x.Item2.Identifier.Identifier.ValueText)) .ToImmutableHashSet(); return (from key in keys let Parameter = methodParameters.FirstOrDefault(p => p.Identifier.ValueText == key) let DocParameter = xElementsWitAttrs.FirstOrDefault(p => p.Item2.Identifier.Identifier.ValueText == key) select new Tuple<ParameterSyntax, Tuple<XmlElementSyntax, XmlNameAttributeSyntax>>(Parameter, DocParameter)); }
/// <summary> /// Given a DocumentationCommentTriviaSyntax, return the full text, but with /// documentation comment IDs substituted into crefs. /// </summary> /// <remarks> /// Still has all of the comment punctuation (///, /**, etc). /// </remarks> public static string GetSubstitutedText( CSharpCompilation compilation, DiagnosticBag diagnostics, Symbol symbol, DocumentationCommentTriviaSyntax trivia, ArrayBuilder<CSharpSyntaxNode> includeElementNodes, ref HashSet<ParameterSymbol> documentedParameters, ref HashSet<TypeParameterSymbol> documentedTypeParameters) { PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); using (StringWriter writer = new StringWriter(pooled.Builder, CultureInfo.InvariantCulture)) { DocumentationCommentWalker walker = new DocumentationCommentWalker(compilation, diagnostics, symbol, writer, includeElementNodes, documentedParameters, documentedTypeParameters); walker.Visit(trivia); // Copy back out in case they have been initialized. documentedParameters = walker._documentedParameters; documentedTypeParameters = walker._documentedTypeParameters; } return pooled.ToStringAndFree(); }
private void RemoveExistingTags(DocumentationCommentTriviaSyntax parentTrivia, ISet<string> names, Func<XmlElementSyntax, string> selector) { if (parentTrivia != null) { foreach (var node in parentTrivia.Content) { var element = node as XmlElementSyntax; if (element != null) { names.Remove(selector(element)); } } } }
private IEnumerable<CompletionData> GetTagsForSymbol(CompletionEngine engine, ISymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token) { if (symbol is IMethodSymbol) { return GetTagsForMethod(engine, (IMethodSymbol)symbol, filterSpan, trivia, token); } if (symbol is IPropertySymbol) { return GetTagsForProperty(engine, (IPropertySymbol)symbol, filterSpan, trivia); } if (symbol is INamedTypeSymbol) { return GetTagsForType(engine, (INamedTypeSymbol)symbol, filterSpan, trivia); } return SpecializedCollections.EmptyEnumerable<CompletionData>(); }
private IEnumerable<CompletionItem> GetTagsForMethod(IMethodSymbol symbol, DocumentationCommentTriviaSyntax trivia) { var items = new List<CompletionItem>(); var parameters = symbol.GetParameters().Select(p => p.Name).ToSet(); var typeParameters = symbol.TypeParameters.Select(t => t.Name).ToSet(); RemoveExistingTags(trivia, parameters, x => AttributeSelector(x, ParamTagName)); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, TypeParamTagName)); items.AddRange(parameters.Select(p => CreateCompletionItem(FormatParameter(ParamTagName, p)))); items.AddRange(typeParameters.Select(t => CreateCompletionItem(FormatParameter(TypeParamTagName, t)))); // Provide a return completion item in case the function returns something var returns = true; foreach (var node in trivia.Content) { var element = node as XmlElementSyntax; if (element != null && !element.StartTag.IsMissing && !element.EndTag.IsMissing) { var startTag = element.StartTag; if (startTag.Name.LocalName.ValueText == ReturnsTagName) { returns = false; break; } } } if (returns && !symbol.ReturnsVoid) { items.Add(CreateCompletionItem(ReturnsTagName)); } return items; }
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)); }
private IEnumerable <CompletionData> GetTagsForType(CompletionEngine engine, INamedTypeSymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia) { var items = new List <CompletionData>(); var typeParameters = symbol.TypeParameters.Select(p => p.Name).ToSet(); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam")); items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData( this, FormatParameter("typeparam", t)))); return(items); }
//public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node) //{ // base.VisitDelegateDeclaration(node); //} //public override void VisitDestructorDeclaration(DestructorDeclarationSyntax node) //{ // base.VisitDestructorDeclaration(node); //} //public override void VisitDiscardDesignation(DiscardDesignationSyntax node) //{ // base.VisitDiscardDesignation(node); //} public override void VisitDocumentationCommentTrivia(DocumentationCommentTriviaSyntax node) { Debug.Fail(node.ToString()); base.VisitDocumentationCommentTrivia(node); }
public ParsedType(NamespaceDeclarationSyntax node, DocumentationCommentTriviaSyntax documentation) : base(documentation) { _namespaceType = node; }
/// <summary> /// Add documentation for the property. /// </summary> /// <param name="context">the code fix context.</param> /// <param name="root">the root syntax node.</param> /// <param name="methodDeclaration">the property declaration containing invalid documentation.</param> /// <param name="documentComment">the existing comment.</param> /// <returns>the correct code.</returns> private Task<Document> AddDocumentationAsync( CodeFixContext context, SyntaxNode root, MethodDeclarationSyntax methodDeclaration, DocumentationCommentTriviaSyntax documentComment) { var summary = this._commentNodeFactory.GetExistingSummaryCommentText(documentComment) ?? this._commentNodeFactory.CreateCommentSummaryText(methodDeclaration); var @class = methodDeclaration.Parent as ClassDeclarationSyntax; var first = @class?.DescendantNodes().FirstOrDefault() == methodDeclaration; var parameters = this._commentNodeFactory.CreateParameters(methodDeclaration, documentComment); var typeParameters = this._commentNodeFactory.CreateTypeParameters(methodDeclaration, documentComment); var @return = this._commentNodeFactory.CreateReturnValueDocumentation(methodDeclaration, documentComment); var returns = @return == null ? new XmlElementSyntax[] { } : new[] { @return }; var summaryPlusParameters = new XmlNodeSyntax[] { summary } .Concat(parameters) .Concat(returns) .Concat(typeParameters) .ToArray(); var comment = this._commentNodeFactory .CreateDocumentComment(summaryPlusParameters) .AddLeadingEndOfLineTriviaFrom(methodDeclaration.GetLeadingTrivia()); var trivia = SyntaxFactory.Trivia(comment); var methodTrivia = first ? methodDeclaration.WithLeadingTrivia(trivia) : methodDeclaration.WithLeadingTrivia(SyntaxFactory.CarriageReturnLineFeed, trivia); var result = documentComment != null ? root.ReplaceNode(documentComment, comment.AdjustDocumentationCommentNewLineTrivia()) : root.ReplaceNode(methodDeclaration, methodTrivia); var newDocument = context.Document.WithSyntaxRoot(result); return Task.FromResult(newDocument); }
/// <summary> /// Analyzes the top-level <c><summary></c> element of a documentation comment. /// </summary> /// <param name="context">The current analysis context.</param> /// <param name="documentation">The documentation syntax associated with the element.</param> /// <param name="syntax">The <see cref="XmlElementSyntax"/> or <see cref="XmlEmptyElementSyntax"/> of the node /// to examine.</param> /// <param name="completeDocumentation">The complete documentation for the declared symbol, with any /// <c><include></c> elements expanded. If the XML documentation comment included a <c><summary></c> /// element, this value will be <see langword="null"/>, even if the XML documentation comment also included an /// <c><include></c> element.</param> /// <param name="diagnosticLocations">The location(s) where diagnostics, if any, should be reported.</param> protected abstract void HandleXmlElement(SyntaxNodeAnalysisContext context, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, params Location[] diagnosticLocations);
private IEnumerable<CompletionData> GetTagsForProperty(CompletionEngine engine, IPropertySymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia) { var items = new List<CompletionData>(); var typeParameters = symbol.GetTypeArguments().Select(p => p.Name).ToSet(); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam")); items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData(this, "typeparam", null, "name$" + t))); items.Add(engine.Factory.CreateXmlDocCompletionData(this, "value")); return items; }
private IEnumerable<CompletionData> GetTagsForType(CompletionEngine engine, INamedTypeSymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia) { var items = new List<CompletionData>(); var typeParameters = symbol.TypeParameters.Select(p => p.Name).ToSet(); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam")); items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData ( this, FormatParameter("typeparam", t)))); return items; }
private IEnumerable<CompletionItem> GetTagsForType(INamedTypeSymbol symbol, TextSpan itemSpan, DocumentationCommentTriviaSyntax trivia) { var items = new List<CompletionItem>(); var typeParameters = symbol.TypeParameters.Select(p => p.Name).ToSet(); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, TypeParamTagName)); items.AddRange(typeParameters.Select(t => CreateCompletionItem(itemSpan, FormatParameter(TypeParamTagName, t)))); return items; }
private IEnumerable<SyntaxTrivia> ConvertDocCommentToRegularComment(DocumentationCommentTriviaSyntax structuredTrivia) { var xmlFragment = DocumentationCommentUtilities.ExtractXMLFragment(structuredTrivia.ToFullString()); var docComment = DocumentationComment.FromXmlFragment(xmlFragment); var commentLines = AbstractMetadataAsSourceService.DocCommentFormatter.Format(_formattingService, docComment); foreach (var line in commentLines) { if (!string.IsNullOrWhiteSpace(line)) { yield return SyntaxFactory.Comment("// " + line); } else { yield return SyntaxFactory.Comment("//"); } yield return SyntaxFactory.ElasticCarriageReturnLineFeed; } }
private IEnumerable<CompletionItem> GetTagsForMethod(IMethodSymbol symbol, TextSpan itemSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token) { var items = new List<CompletionItem>(); var parameters = symbol.GetParameters().Select(p => p.Name).ToSet(); var typeParameters = symbol.TypeParameters.Select(t => t.Name).ToSet(); // User is trying to write a name, try to suggest only names. if (token.Parent.IsKind(SyntaxKind.XmlNameAttribute) || (token.Parent.IsKind(SyntaxKind.IdentifierName) && token.Parent.IsParentKind(SyntaxKind.XmlNameAttribute))) { string parentElementName = null; var emptyElement = token.GetAncestor<XmlEmptyElementSyntax>(); if (emptyElement != null) { parentElementName = emptyElement.Name.LocalName.Text; } // We're writing the name of a paramref or typeparamref if (parentElementName == ParamRefTagName) { items.AddRange(parameters.Select(p => CreateCompletionItem(itemSpan, p))); } else if (parentElementName == TypeParamRefTagName) { items.AddRange(typeParameters.Select(t => CreateCompletionItem(itemSpan, t))); } return items; } RemoveExistingTags(trivia, parameters, x => AttributeSelector(x, ParamTagName)); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, TypeParamTagName)); items.AddRange(parameters.Select(p => CreateCompletionItem(itemSpan, FormatParameter(ParamTagName, p)))); items.AddRange(typeParameters.Select(t => CreateCompletionItem(itemSpan, FormatParameter(TypeParamTagName, t)))); // Provide a return completion item in case the function returns something var returns = true; foreach (var node in trivia.Content) { var element = node as XmlElementSyntax; if (element != null && !element.StartTag.IsMissing && !element.EndTag.IsMissing) { var startTag = element.StartTag; if (startTag.Name.LocalName.ValueText == ReturnsTagName) { returns = false; break; } } } if (returns && !symbol.ReturnsVoid) { items.Add(CreateCompletionItem(itemSpan, ReturnsTagName)); } return items; }
private IEnumerable<CompletionItem> GetTagsForType(INamedTypeSymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia) { var items = new List<CompletionItem>(); var typeParameters = symbol.TypeParameters.Select(p => p.Name).ToSet(); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam")); items.AddRange(typeParameters.Select(t => new XmlDocCommentCompletionItem(this, filterSpan, FormatParameter("typeparam", t), GetCompletionItemRules()))); return items; }
internal static TNode RemoveSingleLineDocumentationComment <TNode>(TNode node, DocumentationCommentTriviaSyntax documentationComment) where TNode : SyntaxNode { if (node == null) { throw new ArgumentNullException(nameof(node)); } if (!documentationComment.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)) { throw new ArgumentException($"Documentation comment's kind must be '{nameof(SyntaxKind.SingleLineDocumentationCommentTrivia)}'.", nameof(documentationComment)); } SyntaxTrivia trivia = documentationComment.ParentTrivia; SyntaxToken token = trivia.Token; SyntaxTriviaList leadingTrivia = token.LeadingTrivia; int index = leadingTrivia.IndexOf(trivia); if (index >= 0 && index < leadingTrivia.Count - 1 && leadingTrivia[index + 1].IsWhitespaceTrivia()) { SyntaxTriviaList newLeadingTrivia = leadingTrivia.RemoveRange(index, 2); SyntaxToken newToken = token.WithLeadingTrivia(newLeadingTrivia); return(node.ReplaceToken(token, newToken)); } return(node.RemoveNode(documentationComment, SyntaxRemoveOptions.KeepNoTrivia)); }
private IEnumerable<CompletionItem> GetTagsForMethod(IMethodSymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token) { var items = new List<CompletionItem>(); var parameters = symbol.GetParameters().Select(p => p.Name).ToSet(); var typeParameters = symbol.TypeParameters.Select(t => t.Name).ToSet(); // User is trying to write a name, try to suggest only names. if (token.Parent.IsKind(SyntaxKind.XmlNameAttribute) || (token.Parent.IsKind(SyntaxKind.IdentifierName) && token.Parent.IsParentKind(SyntaxKind.XmlNameAttribute))) { string parentElementName = null; var emptyElement = token.GetAncestor<XmlEmptyElementSyntax>(); if (emptyElement != null) { parentElementName = emptyElement.Name.LocalName.Text; } // We're writing the name of a paramref or typeparamref if (parentElementName == "paramref") { items.AddRange(parameters.Select(p => new XmlDocCommentCompletionItem(this, filterSpan, p, GetCompletionItemRules()))); } else if (parentElementName == "typeparamref") { items.AddRange(typeParameters.Select(t => new XmlDocCommentCompletionItem(this, filterSpan, t, GetCompletionItemRules()))); } return items; } var returns = true; RemoveExistingTags(trivia, parameters, x => AttributeSelector(x, "param")); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam")); foreach (var node in trivia.Content) { var element = node as XmlElementSyntax; if (element != null && !element.StartTag.IsMissing && !element.EndTag.IsMissing) { var startTag = element.StartTag; if (startTag.Name.LocalName.ValueText == "returns") { returns = false; break; } } } items.AddRange(parameters.Select(p => new XmlDocCommentCompletionItem(this, filterSpan, FormatParameter("param", p), GetCompletionItemRules()))); items.AddRange(typeParameters.Select(t => new XmlDocCommentCompletionItem(this, filterSpan, FormatParameter("typeparam", t), GetCompletionItemRules()))); if (returns && !symbol.ReturnsVoid) { items.Add(new XmlDocCommentCompletionItem(this, filterSpan, "returns", GetCompletionItemRules())); } return items; }
// // public override bool SendEnterThroughToEditor(ICompletionData ICompletionData, string textTypedSoFar) // { // return false; // } private IEnumerable <CompletionData> GetTopLevelSingleUseNames(CompletionEngine engine, DocumentationCommentTriviaSyntax parentTrivia, TextSpan span) { var names = new HashSet <string>(new[] { "summary", "remarks", "example", "completionlist" }); RemoveExistingTags(parentTrivia, names, (x) => x.StartTag.Name.LocalName.ValueText); return(names.Select(n => GetItem(engine, n, span))); }
private IEnumerable<CompletionItem> GetTagsForProperty(IPropertySymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia) { var items = new List<CompletionItem>(); var typeParameters = symbol.GetTypeArguments().Select(p => p.Name).ToSet(); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam")); items.AddRange(typeParameters.Select(t => new XmlItem(this, filterSpan, "typeparam", "name", t))); items.Add(new XmlItem(this, filterSpan, "value")); return items; }
private IEnumerable <CompletionData> GetTagsForMethod(CompletionEngine engine, IMethodSymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token) { var items = new List <CompletionData>(); var parameters = symbol.GetParameters().Select(p => p.Name).ToSet(); var typeParameters = symbol.TypeParameters.Select(t => t.Name).ToSet(); // User is trying to write a name, try to suggest only names. if (token.Parent.IsKind(SyntaxKind.XmlNameAttribute) || (token.Parent.IsKind(SyntaxKind.IdentifierName) && token.Parent.IsParentKind(SyntaxKind.XmlNameAttribute))) { string parentElementName = null; var emptyElement = token.GetAncestor <XmlEmptyElementSyntax>(); if (emptyElement != null) { parentElementName = emptyElement.Name.LocalName.Text; } // We're writing the name of a paramref or typeparamref if (parentElementName == "paramref") { items.AddRange(parameters.Select(p => engine.Factory.CreateXmlDocCompletionData(this, p))); } else if (parentElementName == "typeparamref") { items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData(this, t))); } return(items); } var returns = true; RemoveExistingTags(trivia, parameters, x => AttributeSelector(x, "param")); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam")); foreach (var node in trivia.Content) { var element = node as XmlElementSyntax; if (element != null && !element.StartTag.IsMissing && !element.EndTag.IsMissing) { var startTag = element.StartTag; if (startTag.Name.LocalName.ValueText == "returns") { returns = false; break; } } } items.AddRange(parameters.Select(p => engine.Factory.CreateXmlDocCompletionData(this, FormatParameter("param", p)))); items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData(this, FormatParameter("typeparam", t)))); if (returns && !symbol.ReturnsVoid) { items.Add(engine.Factory.CreateXmlDocCompletionData(this, "returns")); } return(items); }
private async Task <Document> CreateChangedDocument(CodeFixContext context, PropertyDeclarationSyntax propertyDeclarationSyntax, CancellationToken cancellationToken) { DocumentationCommentTriviaSyntax documentationComment = propertyDeclarationSyntax.GetDocumentationCommentTriviaSyntax(); if (documentationComment == null) { return(context.Document); } XmlElementSyntax summaryElement = (XmlElementSyntax)documentationComment.Content.GetFirstXmlElement("summary"); if (summaryElement == null) { return(context.Document); } SyntaxList <XmlNodeSyntax> summaryContent = summaryElement.Content; XmlNodeSyntax firstContent = summaryContent.FirstOrDefault(IsContentElement); XmlTextSyntax firstText = firstContent as XmlTextSyntax; if (firstText != null) { string firstTextContent = string.Concat(firstText.DescendantTokens()); if (firstTextContent.TrimStart().StartsWith("Gets ", StringComparison.Ordinal)) { // Find the token containing "Gets " SyntaxToken getsToken = default(SyntaxToken); foreach (SyntaxToken textToken in firstText.TextTokens) { if (textToken.IsMissing) { continue; } if (!textToken.Text.TrimStart().StartsWith("Gets ", StringComparison.Ordinal)) { continue; } getsToken = textToken; break; } if (!getsToken.IsMissing) { string text = getsToken.Text; string valueText = getsToken.ValueText; int index = text.IndexOf("Gets "); if (index >= 0) { bool additionalCharacters = index + 5 < text.Length; text = text.Substring(0, index) + (additionalCharacters ? char.ToUpperInvariant(text[index + 5]).ToString() : string.Empty) + text.Substring(index + (additionalCharacters ? (5 + 1) : 5)); } index = valueText.IndexOf("Gets "); if (index >= 0) { valueText = valueText.Remove(index, 5); } SyntaxToken replaced = SyntaxFactory.Token(getsToken.LeadingTrivia, getsToken.Kind(), text, valueText, getsToken.TrailingTrivia); summaryContent = summaryContent.Replace(firstText, firstText.ReplaceToken(getsToken, replaced)); } } } string defaultValueToken = "NullIfNotIncluded"; SemanticModel semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); IPropertySymbol propertySymbol = semanticModel.GetDeclaredSymbol(propertyDeclarationSyntax); if (propertySymbol != null) { ITypeSymbol propertyType = propertySymbol.Type; if (propertyType.IsImmutableArray()) { defaultValueToken = "DefaultArrayIfNotIncluded"; } } XmlElementSyntax valueElement = XmlSyntaxFactory.MultiLineElement( "value", XmlSyntaxFactory.List( XmlSyntaxFactory.ParaElement(XmlSyntaxFactory.PlaceholderElement(summaryContent.WithoutFirstAndLastNewlines())), XmlSyntaxFactory.NewLine(), XmlSyntaxFactory.TokenElement(defaultValueToken))); XmlNodeSyntax leadingNewLine = XmlSyntaxFactory.NewLine(); // 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); } DocumentationCommentTriviaSyntax newDocumentationComment = documentationComment.WithContent( documentationComment.Content.InsertRange(documentationComment.Content.Count - 1, XmlSyntaxFactory.List( leadingNewLine, valueElement))); SyntaxNode root = await context.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); SyntaxNode newRoot = root.ReplaceNode(documentationComment, newDocumentationComment); return(context.Document.WithSyntaxRoot(newRoot)); }
/// <summary> /// Add documentation for the property. /// </summary> /// <param name="context">the code fix context.</param> /// <param name="root">the root syntax node.</param> /// <param name="fieldDeclaration">the property declaration containing invalid documentation.</param> /// <param name="documentComment">the existing comment.</param> /// <returns>the correct code.</returns> private Task<Document> AddDocumentationAsync( CodeFixContext context, SyntaxNode root, FieldDeclarationSyntax fieldDeclaration, DocumentationCommentTriviaSyntax documentComment) { var @class = fieldDeclaration.Parent as ClassDeclarationSyntax; var first = @class?.DescendantNodes().FirstOrDefault() == fieldDeclaration; var summary = this._commentNodeFactory.CreateCommentSummaryText(fieldDeclaration); var comment = this._commentNodeFactory .CreateDocumentComment(summary) .WithAdditionalAnnotations(Formatter.Annotation); var trivia = SyntaxFactory.Trivia(comment); var pd = first ? fieldDeclaration.WithLeadingTrivia(trivia) : fieldDeclaration.WithLeadingTrivia(SyntaxFactory.CarriageReturnLineFeed, trivia); var result = documentComment != null ? root.ReplaceNode(documentComment, comment.AdjustDocumentationCommentNewLineTrivia()) : root.ReplaceNode(fieldDeclaration, pd); var newDocument = context.Document.WithSyntaxRoot(result); return Task.FromResult(newDocument); }
private static void HandleMemberDeclaration(SyntaxNodeAnalysisContext context) { MemberDeclarationSyntax memberSyntax = (MemberDeclarationSyntax)context.Node; var modifiers = memberSyntax.GetModifiers(); if (modifiers.Any(SyntaxKind.OverrideKeyword)) { return; } DocumentationCommentTriviaSyntax documentation = memberSyntax.GetDocumentationCommentTriviaSyntax(); if (documentation == null) { return; } Location location; ISymbol declaredSymbol = context.SemanticModel.GetDeclaredSymbol(memberSyntax, context.CancellationToken); if (declaredSymbol == null && memberSyntax.IsKind(SyntaxKind.EventFieldDeclaration)) { var eventFieldDeclarationSyntax = (EventFieldDeclarationSyntax)memberSyntax; VariableDeclaratorSyntax firstVariable = eventFieldDeclarationSyntax.Declaration?.Variables.FirstOrDefault(); if (firstVariable != null) { declaredSymbol = context.SemanticModel.GetDeclaredSymbol(firstVariable, context.CancellationToken); } } if (documentation.Content.GetFirstXmlElement(XmlCommentHelper.IncludeXmlTag) is XmlEmptyElementSyntax includeElement) { if (declaredSymbol == null) { return; } var rawDocumentation = declaredSymbol.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken); var completeDocumentation = XElement.Parse(rawDocumentation, LoadOptions.None); var inheritDocElement = completeDocumentation.Nodes().OfType<XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.InheritdocXmlTag); if (inheritDocElement == null) { return; } if (HasXmlCrefAttribute(inheritDocElement)) { return; } location = includeElement.GetLocation(); } else { XmlNodeSyntax inheritDocElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag); if (inheritDocElement == null) { return; } if (HasXmlCrefAttribute(inheritDocElement)) { return; } location = inheritDocElement.GetLocation(); } // If we don't have a declared symbol we have some kind of field declaration. A field can not override or // implement anything so we want to report a diagnostic. if (declaredSymbol == null || !NamedTypeHelpers.IsImplementingAnInterfaceMember(declaredSymbol)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } }
/// <summary> /// add documentation for the constructor. /// </summary> /// <param name="context">the code fix context.</param> /// <param name="root">the syntax root.</param> /// <param name="constructorDeclaration">the constructor declaration syntax.</param> /// <param name="documentComment">the document content.</param> /// <returns>the resulting document.</returns> private Task<Document> AddDocumentationAsync(CodeFixContext context, SyntaxNode root, ConstructorDeclarationSyntax constructorDeclaration, DocumentationCommentTriviaSyntax documentComment) { var lines = documentComment.GetExistingSummaryCommentDocumentation() ?? new string[] { }; var standardCommentText = this._commentNodeFactory.PrependStandardCommentText(constructorDeclaration, lines); var parameters = this._commentNodeFactory.CreateParameters(constructorDeclaration, documentComment); var summaryPlusParameters = new XmlNodeSyntax[] { standardCommentText } .Concat(parameters) .ToArray(); var comment = this._commentNodeFactory .CreateDocumentComment(summaryPlusParameters) .AddLeadingEndOfLineTriviaFrom(constructorDeclaration.GetLeadingTrivia()); var trivia = SyntaxFactory.Trivia(comment); var result = documentComment != null ? root.ReplaceNode(documentComment, comment.AdjustDocumentationCommentNewLineTrivia()) : root.ReplaceNode(constructorDeclaration, constructorDeclaration.WithLeadingTrivia(trivia)); var newDocument = context.Document.WithSyntaxRoot(result); return Task.FromResult(newDocument); }
// // public override bool SendEnterThroughToEditor(ICompletionData ICompletionData, string textTypedSoFar) // { // return false; // } private IEnumerable<CompletionData> GetTopLevelSingleUseNames(CompletionEngine engine, DocumentationCommentTriviaSyntax parentTrivia, TextSpan span) { var names = new HashSet<string>(new[] { "summary", "remarks", "example", "completionlist" }); RemoveExistingTags(parentTrivia, names, (x) => x.StartTag.Name.LocalName.ValueText); return names.Select(n => GetItem(engine, n, span)); }
private static void HandleBaseTypeLikeDeclaration(SyntaxNodeAnalysisContext context) { BaseTypeDeclarationSyntax baseType = context.Node as BaseTypeDeclarationSyntax; // baseType can be null here if we are looking at a delegate declaration if (baseType != null && baseType.BaseList != null && baseType.BaseList.Types.Any()) { return; } DocumentationCommentTriviaSyntax documentation = context.Node.GetDocumentationCommentTriviaSyntax(); if (documentation == null) { return; } Location location; var includeElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.IncludeXmlTag) as XmlEmptyElementSyntax; if (includeElement != null) { var declaration = context.SemanticModel.GetDeclaredSymbol(baseType, context.CancellationToken); if (declaration == null) { return; } var rawDocumentation = declaration.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken); var completeDocumentation = XElement.Parse(rawDocumentation, LoadOptions.None); var inheritDocElement = completeDocumentation.Nodes().OfType <XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.InheritdocXmlTag); if (inheritDocElement == null) { return; } if (HasXmlCrefAttribute(inheritDocElement)) { return; } location = includeElement.GetLocation(); } else { XmlNodeSyntax inheritDocElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag); if (inheritDocElement == null) { return; } if (HasXmlCrefAttribute(inheritDocElement)) { return; } location = inheritDocElement.GetLocation(); } context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); }
/// <summary> /// Add documentation for the property. /// </summary> /// <param name="context">the code fix context.</param> /// <param name="root">the root syntax node.</param> /// <param name="typeDeclaration">the property declaration containing invalid documentation.</param> /// <param name="documentComment">the existing comment.</param> /// <returns>the correct code.</returns> private Task<Document> AddDocumentationAsync( CodeFixContext context, SyntaxNode root, TypeDeclarationSyntax typeDeclaration, DocumentationCommentTriviaSyntax documentComment) { var summary = this._commentNodeFactory.CreateCommentSummaryText(typeDeclaration); var typeParameters = this._commentNodeFactory .CreateTypeParameters(typeDeclaration, documentComment) .ToArray(); var all = typeParameters != null && typeParameters.Any() ? new XmlNodeSyntax[] { summary }.Concat(typeParameters).ToArray() : new XmlNodeSyntax[] { summary }; var comment = this._commentNodeFactory .CreateDocumentComment(all) .WithAdditionalAnnotations(Formatter.Annotation); var trivia = SyntaxFactory.Trivia(comment); var replacement = typeDeclaration.WithLeadingTrivia(trivia); var result = root.ReplaceNode(typeDeclaration, replacement); var newDocument = context.Document.WithSyntaxRoot(result); return Task.FromResult(newDocument); }
public override void VisitDocumentationCommentTrivia(DocumentationCommentTriviaSyntax node) { return; //base.VisitDocumentationCommentTrivia(node); }
public override void VisitDocumentationCommentTrivia(DocumentationCommentTriviaSyntax node) { storedXmlComment.Add(node); }
private IEnumerable<CompletionItem> GetTopLevelSingleUseNames(DocumentationCommentTriviaSyntax parentTrivia, TextSpan span) { var names = new HashSet<string>(new[] { SummaryTagName, RemarksTagName, ExampleTagName, CompletionListTagName }); RemoveExistingTags(parentTrivia, names, (x) => x.StartTag.Name.LocalName.ValueText); return names.Select(n => GetItem(n, span)); }
private static bool ContainsXmlParseDiagnostic(DocumentationCommentTriviaSyntax node) { if (!node.ContainsDiagnostics) { return false; } foreach (Diagnostic diag in node.GetDiagnostics()) { if ((ErrorCode)diag.Code == ErrorCode.WRN_XMLParseError) { return true; } } return false; }
private IEnumerable<CompletionItem> GetTagsForSymbol(ISymbol symbol, TextSpan itemSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token) { if (symbol is IMethodSymbol) { return GetTagsForMethod((IMethodSymbol)symbol, itemSpan, trivia, token); } if (symbol is IPropertySymbol) { return GetTagsForProperty((IPropertySymbol)symbol, itemSpan, trivia, token); } if (symbol is INamedTypeSymbol) { return GetTagsForType((INamedTypeSymbol)symbol, itemSpan, trivia); } return SpecializedCollections.EmptyEnumerable<CompletionItem>(); }
private IEnumerable <CompletionItem> GetTagsForMethod( IMethodSymbol symbol, DocumentationCommentTriviaSyntax trivia, SyntaxToken token) { var items = new List <CompletionItem>(); var parameters = symbol.GetParameters().Select(p => p.Name).ToSet(); var typeParameters = symbol.TypeParameters.Select(t => t.Name).ToSet(); // User is trying to write a name, try to suggest only names. if (token.Parent.IsKind(SyntaxKind.XmlNameAttribute) || (token.Parent.IsKind(SyntaxKind.IdentifierName) && token.Parent.IsParentKind(SyntaxKind.XmlNameAttribute))) { string parentElementName = null; var emptyElement = token.GetAncestor <XmlEmptyElementSyntax>(); if (emptyElement != null) { parentElementName = emptyElement.Name.LocalName.Text; } // We're writing the name of a paramref or typeparamref if (parentElementName == ParamRefTagName) { items.AddRange(parameters.Select(CreateCompletionItem)); } else if (parentElementName == TypeParamRefTagName) { items.AddRange(typeParameters.Select(CreateCompletionItem)); } return(items); } RemoveExistingTags(trivia, parameters, x => AttributeSelector(x, ParamTagName)); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, TypeParamTagName)); items.AddRange(parameters.Select(p => CreateCompletionItem(FormatParameter(ParamTagName, p)))); items.AddRange(typeParameters.Select(t => CreateCompletionItem(FormatParameter(TypeParamTagName, t)))); // Provide a return completion item in case the function returns something var returns = true; foreach (var node in trivia.Content) { var element = node as XmlElementSyntax; if (element != null && !element.StartTag.IsMissing && !element.EndTag.IsMissing) { var startTag = element.StartTag; if (startTag.Name.LocalName.ValueText == ReturnsTagName) { returns = false; break; } } } if (returns && !symbol.ReturnsVoid) { items.Add(CreateCompletionItem(ReturnsTagName)); } return(items); }
private IEnumerable<CompletionItem> GetTagsForProperty(IPropertySymbol symbol, TextSpan itemSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token) { var items = new List<CompletionItem>(); if (symbol.IsIndexer) { var parameters = symbol.GetParameters().Select(p => p.Name).ToSet(); // User is trying to write a name, try to suggest only names. if (token.Parent.IsKind(SyntaxKind.XmlNameAttribute) || (token.Parent.IsKind(SyntaxKind.IdentifierName) && token.Parent.IsParentKind(SyntaxKind.XmlNameAttribute))) { string parentElementName = null; var emptyElement = token.GetAncestor<XmlEmptyElementSyntax>(); if (emptyElement != null) { parentElementName = emptyElement.Name.LocalName.Text; } // We're writing the name of a paramref if (parentElementName == ParamRefTagName) { items.AddRange(parameters.Select(p => CreateCompletionItem(itemSpan, p))); } return items; } RemoveExistingTags(trivia, parameters, x => AttributeSelector(x, ParamTagName)); items.AddRange(parameters.Select(p => CreateCompletionItem(itemSpan, FormatParameter(ParamTagName, p)))); } var typeParameters = symbol.GetTypeArguments().Select(p => p.Name).ToSet(); items.AddRange(typeParameters.Select(t => CreateCompletionItem(itemSpan, TypeParamTagName, NameAttributeName, t))); items.Add(CreateCompletionItem(itemSpan, "value")); return items; }
private MethodDeclarationSyntax AbstractRequestMethod(bool sync, bool callSettings, IEnumerable <ParameterInfo> parameters, DocumentationCommentTriviaSyntax returnsXmlDoc = null) { var returnTyp = sync ? MethodDetails.SyncReturnTyp : MethodDetails.AsyncReturnTyp; var methodName = sync ? MethodDetails.SyncMethodName : MethodDetails.AsyncMethodName; var finalParam = callSettings ? _def.CallSettingsParam : _def.CancellationTokenParam; var finalParamXmlDoc = callSettings ? _def.CallSettingsXmlDoc : _def.CancellationTokenXmlDoc; returnsXmlDoc = returnsXmlDoc ?? (sync ? MethodDetails is MethodDetails.Paginated ? _def.ReturnsSyncPaginatedXmlDoc : _def.ReturnsSyncXmlDoc : MethodDetails is MethodDetails.Paginated ? _def.ReturnsAsyncPaginatedXmlDoc : _def.ReturnsAsyncXmlDoc); if (callSettings) { return(Method(Public | Virtual, Ctx.Type(returnTyp), methodName)(parameters.Select(x => x.Parameter).Append(finalParam).ToArray()) .WithBody(This.Call(methodName)(New(Ctx.Type(MethodDetails.RequestTyp))() .WithInitializer(NestInit(parameters, 0).ToArray()), finalParam)) .WithXmlDoc(parameters.Select(x => x.XmlDoc).Prepend(_def.SummaryXmlDoc).Append(finalParamXmlDoc).Append(returnsXmlDoc).ToArray())); IEnumerable <ObjectInitExpr> NestInit(IEnumerable <ParameterInfo> ps, int ofs) { var byField = ps.GroupBy(x => (x.FieldDescs ?? Enumerable.Empty <FieldDescriptor>()).Skip(ofs).SkipLast(1).FirstOrDefault()) .OrderBy(x => x.Key?.Index); foreach (var f in byField) { if (f.Key == null) { // No more nesting, these are the actual fields that need filling. foreach (var param in f.OrderBy(x => x.FieldDescs?.Last().Index ?? int.MaxValue)) { yield return((param.InitExpr as ObjectInitExpr) ?? new ObjectInitExpr(param.ResourcePropertyName ?? param.FieldDescs.Last().CSharpPropertyName(), param.InitExpr, isDeprecated: param.FieldDescs.Last().IsDeprecated())); } } else { // Nested field. var code = New(Ctx.Type(ProtoTyp.Of(f.Key)))().WithInitializer(NestInit(f, ofs + 1).ToArray()); yield return(new ObjectInitExpr(f.Key.CSharpPropertyName(), code)); } } } } else { return(Method(Public | Virtual, Ctx.Type(returnTyp), methodName)(parameters.Select(x => x.Parameter).Append(finalParam).ToArray()) .WithBody(This.Call(methodName)(parameters.Select(x => (object)x.Parameter).Append( Ctx.Type <CallSettings>().Call(nameof(CallSettings.FromCancellationToken))(finalParam)))) .WithXmlDoc(parameters.Select(x => x.XmlDoc).Prepend(_def.SummaryXmlDoc).Append(finalParamXmlDoc).Append(returnsXmlDoc).ToArray())); } }
public ParsedType(BaseTypeDeclarationSyntax type, DocumentationCommentTriviaSyntax documentation) : base(documentation) { _declarationType = type; }
public ParameterInfo(IReadOnlyList <FieldDescriptor> fieldDescs, ParameterSyntax parameter, object initExpr, string resourcePropertyName, DocumentationCommentTriviaSyntax xmlDoc) => (FieldDescs, Parameter, InitExpr, ResourcePropertyName, XmlDoc) = (fieldDescs, parameter, initExpr, resourcePropertyName, xmlDoc);
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)); }
public static XmlElementSyntax GetXmlCommentParam(this DocumentationCommentTriviaSyntax documentationSyntax, string xmlParamName) => documentationSyntax?.ChildNodes() .OfType <XmlElementSyntax>() .FirstOrDefault(xmlElementSyntax => xmlElementSyntax.IsXmlCommentParam(xmlParamName));
/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, Location[] diagnosticLocations) { if (!needsComment) { // A missing summary is allowed for this element. return; } if (completeDocumentation != null) { // We are working with an <include> element if (completeDocumentation.Nodes().OfType <XElement>().Any(element => element.Name == XmlCommentHelper.SummaryXmlTag)) { return; } if (completeDocumentation.Nodes().OfType <XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag)) { // Ignore nodes with an <inheritdoc/> tag in the included XML. return; } } else { if (syntax != null) { return; } if (documentation?.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag) != null) { // Ignore nodes with an <inheritdoc/> tag. return; } } foreach (var location in diagnosticLocations) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } }
public static ImmutableArray <string> GetAttributeValues(DocumentationCommentTriviaSyntax comment, string elementName, string attributeName) { HashSet <string> values = null; bool containsInclude = false; bool isFirst = true; foreach (XmlNodeSyntax node in comment.Content) { SyntaxKind kind = node.Kind(); if (kind == SyntaxKind.XmlElement) { var element = (XmlElementSyntax)node; string name = element.StartTag?.Name?.LocalName.ValueText; if (name != null) { if (isFirst) { if (NameEquals(name, "include")) { containsInclude = true; } isFirst = false; } else { containsInclude = false; } if (NameEquals(name, "inheritdoc")) { return(default(ImmutableArray <string>)); } else if (NameEquals(name, elementName)) { string value = GetAttributeValue(element, attributeName); if (value != null) { (values ?? (values = new HashSet <string>())).Add(value); } } } } if (kind == SyntaxKind.XmlEmptyElement) { var element = (XmlEmptyElementSyntax)node; string name = element?.Name?.LocalName.ValueText; if (name != null) { if (isFirst) { if (NameEquals(name, "include")) { containsInclude = true; } isFirst = false; } else { containsInclude = false; } if (NameEquals(name, "inheritdoc")) { return(default(ImmutableArray <string>)); } } } } if (!containsInclude) { return(values?.ToImmutableArray() ?? ImmutableArray <string> .Empty); } return(default(ImmutableArray <string>)); }
private XmlEmptyElementSyntax GetXmlExcludeTag(DocumentationCommentTriviaSyntax xmlComment) => xmlComment .ChildNodes() .OfType <XmlEmptyElementSyntax>() .FirstOrDefault(s => XmlCommentExcludeTag.Equals(s.Name?.ToString(), StringComparison.Ordinal));
private IEnumerable <CompletionData> GetTagsForSymbol(CompletionEngine engine, ISymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token) { if (symbol is IMethodSymbol) { return(GetTagsForMethod(engine, (IMethodSymbol)symbol, filterSpan, trivia, token)); } if (symbol is IPropertySymbol) { return(GetTagsForProperty(engine, (IPropertySymbol)symbol, filterSpan, trivia)); } if (symbol is INamedTypeSymbol) { return(GetTagsForType(engine, (INamedTypeSymbol)symbol, filterSpan, trivia)); } return(SpecializedCollections.EmptyEnumerable <CompletionData>()); }
private XmlElementSyntax GetSummaryTag(DocumentationCommentTriviaSyntax xmlComment) => xmlComment .ChildNodes() .OfType <XmlElementSyntax>() .FirstOrDefault(n => XmlCommentSummaryTag.Equals(n.StartTag?.Name?.ToString(), StringComparison.Ordinal));
private IEnumerable <CompletionData> GetTagsForProperty(CompletionEngine engine, IPropertySymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia) { var items = new List <CompletionData>(); var typeParameters = symbol.GetTypeArguments().Select(p => p.Name).ToSet(); RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam")); items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData(this, "typeparam", null, "name$" + t))); items.Add(engine.Factory.CreateXmlDocCompletionData(this, "value")); return(items); }
public override Evaluation VisitDocumentationCommentTrivia(DocumentationCommentTriviaSyntax node) { return(base.VisitDocumentationCommentTrivia(node)); }
private static bool CanAddExceptionToComment( DocumentationCommentTriviaSyntax comment, INamedTypeSymbol exceptionSymbol, SemanticModel semanticModel, CancellationToken cancellationToken) { var containsException = false; var containsIncludeOrExclude = false; var isFirst = true; foreach (XmlNodeSyntax node in comment.Content) { XmlElementInfo info = SyntaxInfo.XmlElementInfo(node); if (info.Success) { switch (info.GetTag()) { case XmlTag.Include: case XmlTag.Exclude: { if (isFirst) { containsIncludeOrExclude = true; } break; } case XmlTag.InheritDoc: { return(false); } case XmlTag.Exception: { if (!containsException) { if (info.IsEmptyElement) { containsException = ContainsException((XmlEmptyElementSyntax)info.Element, exceptionSymbol, semanticModel, cancellationToken); } else { containsException = ContainsException((XmlElementSyntax)info.Element, exceptionSymbol, semanticModel, cancellationToken); } } break; } } if (isFirst) { isFirst = false; } else { containsIncludeOrExclude = false; } } } return(!containsIncludeOrExclude && !containsException); }
private void HandleNamedType(SymbolAnalysisContext context) { INamedTypeSymbol symbol = (INamedTypeSymbol)context.Symbol; if (symbol.TypeKind != TypeKind.Class) { return; } if (!symbol.IsExtensibleJsonObject()) { return; } foreach (var propertySymbol in symbol.GetMembers().OfType <IPropertySymbol>()) { if (propertySymbol.SetMethod != null) { continue; } var locations = propertySymbol.Locations; if (locations.IsDefaultOrEmpty) { continue; } var tree = locations[0].SourceTree; if (tree == null) { continue; } var root = tree.GetRoot(context.CancellationToken); var node = root.FindNode(locations[0].SourceSpan, getInnermostNodeForTie: true); var propertySyntax = node.FirstAncestorOrSelf <PropertyDeclarationSyntax>(); var getter = propertySyntax.AccessorList?.Accessors.FirstOrDefault(i => i.Keyword.IsKind(SyntaxKind.GetKeyword)); if (!(getter?.Body?.Statements.Count == 1)) { continue; } ReturnStatementSyntax returnStatement = getter?.Body?.Statements.FirstOrDefault() as ReturnStatementSyntax; ExpressionSyntax returnExpression = returnStatement.Expression; if (returnExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression)) { MemberAccessExpressionSyntax memberAccess = (MemberAccessExpressionSyntax)returnExpression; if (!memberAccess.Expression.IsKind(SyntaxKind.ThisExpression)) { continue; } } SemanticModel semanticModel = context.Compilation.GetSemanticModel(tree); SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(returnExpression, context.CancellationToken); IFieldSymbol fieldSymbol = symbolInfo.Symbol as IFieldSymbol; if (fieldSymbol.ContainingType != propertySymbol.ContainingType) { continue; } if (!fieldSymbol.GetAttributes().Any(i => string.Equals("global::Newtonsoft.Json.JsonPropertyAttribute", i.AttributeClass.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), StringComparison.Ordinal))) { continue; } DocumentationCommentTriviaSyntax documentationCommentSyntax = propertySyntax.GetDocumentationCommentTriviaSyntax(); if (documentationCommentSyntax == null) { continue; } XmlNodeSyntax valueNode = documentationCommentSyntax.Content.GetFirstXmlElement("value"); if (valueNode == null) { continue; } string defaultValueToken = "NullIfNotIncluded"; ITypeSymbol propertyType = propertySymbol.Type; if (propertyType.IsImmutableArray()) { defaultValueToken = "DefaultArrayIfNotIncluded"; } XmlElementSyntax valueElementSyntax = valueNode as XmlElementSyntax; if (valueElementSyntax != null) { bool foundToken = false; foreach (XmlElementSyntax valueElementChild in valueElementSyntax.Content.OfType <XmlElementSyntax>()) { if (!string.Equals("token", valueElementChild.StartTag?.Name?.ToString(), StringComparison.Ordinal)) { continue; } if (!string.Equals(defaultValueToken, valueElementChild.Content.ToFullString())) { continue; } foundToken = true; break; } if (foundToken) { continue; } } string propertyTypeName = propertyType.ToMinimalDisplayString(semanticModel, returnExpression.SpanStart, SymbolDisplayFormat.CSharpErrorMessageFormat); context.ReportDiagnostic(Diagnostic.Create(Descriptor, valueNode.GetLocation(), propertyTypeName, defaultValueToken)); } }
static string DocCommentToPythonDoc(DocumentationCommentTriviaSyntax doccomment, int indentLevel) { StringBuilder summary = new StringBuilder(); StringBuilder args = new StringBuilder(); StringBuilder returns = new StringBuilder(); string comment = doccomment.ToString(); comment = comment.Replace("///", ""); comment = comment.Replace("\t", " "); var doc = new System.Xml.XmlDocument(); doc.LoadXml("<doc>" + comment + "</doc>"); var nodes = doc.FirstChild.ChildNodes; foreach (var node in nodes) { var element = node as System.Xml.XmlElement; string elementText = element.InnerText.Trim(); if (string.IsNullOrWhiteSpace(elementText)) { continue; } string[] lines = elementText.Split(new char[] { '\n' }); if (element.Name.Equals("summary", StringComparison.OrdinalIgnoreCase)) { foreach (var line in lines) { summary.AppendLine(_T(indentLevel) + line.Trim()); } } else if (element.Name.Equals("returns", StringComparison.OrdinalIgnoreCase)) { returns.AppendLine(); returns.AppendLine(_T(indentLevel) + "Returns:"); foreach (var line in lines) { returns.AppendLine(_T(indentLevel + 1) + line.Trim()); } } else if (element.Name.Equals("param", StringComparison.OrdinalIgnoreCase)) { if (args.Length == 0) { args.AppendLine(); args.AppendLine(_T(indentLevel) + "Args:"); } string parameterName = element.GetAttribute("name"); bool added = false; foreach (var line in lines) { if (!added) { args.AppendLine(_T(indentLevel + 1) + parameterName + " : " + line.Trim()); continue; } added = true; args.AppendLine(_T(indentLevel + 1) + line.Trim()); } } } StringBuilder rc = new StringBuilder(); rc.AppendLine(_T(indentLevel) + "\"\"\""); rc.Append(summary.ToString()); rc.Append(args.ToString()); rc.Append(returns.ToString()); rc.AppendLine(_T(indentLevel) + "\"\"\""); return(rc.ToString()); }
public static void Analyze(SyntaxNodeAnalysisContext context, DocumentationCommentTriviaSyntax documentationComment) { bool containsInheritDoc = false; bool containsInclude = false; bool containsSummaryElement = false; bool isFirst = true; foreach (XmlNodeSyntax node in documentationComment.Content) { SyntaxKind kind = node.Kind(); if (kind == SyntaxKind.XmlElement) { var element = (XmlElementSyntax)node; string name = element.StartTag?.Name?.LocalName.ValueText; if (name != null) { if (isFirst) { if (IsInclude(name)) { containsInclude = true; } isFirst = false; } else { containsInclude = false; } if (IsInheritDoc(name)) { containsInheritDoc = true; } else if (IsSummary(name)) { if (IsSummaryMissing(element)) { context.ReportDiagnostic( DiagnosticDescriptors.AddSummaryToDocumentationComment, element); } containsSummaryElement = true; } if (containsInheritDoc && containsSummaryElement) { break; } } } else if (kind == SyntaxKind.XmlEmptyElement) { var element = (XmlEmptyElementSyntax)node; string name = element.Name?.LocalName.ValueText; if (name != null) { if (isFirst) { if (IsInclude(name)) { containsInclude = true; } isFirst = false; } else { containsInclude = false; } if (IsInheritDoc(name)) { containsInheritDoc = true; } else if (IsSummary(name)) { context.ReportDiagnostic( DiagnosticDescriptors.AddSummaryToDocumentationComment, element); containsSummaryElement = true; } if (containsInheritDoc && containsSummaryElement) { break; } } } } if (!containsSummaryElement && !containsInheritDoc && !containsInclude) { context.ReportDiagnostic( DiagnosticDescriptors.AddSummaryElementToDocumentationComment, documentationComment); } }
public static string DocCommentToPythonDoc( DocumentationCommentTriviaSyntax doccomment, MethodDeclarationSyntax method, int indentLevel, out StringBuilder summary, out List <ParameterInfo> parameters, out ReturnInfo returnInfo) { // See https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html // for docstring examples summary = new StringBuilder(); StringBuilder args = new StringBuilder(); StringBuilder returns = new StringBuilder(); StringBuilder outArgs = new StringBuilder(); parameters = new List <ParameterInfo>(); returnInfo = new ReturnInfo() { Type = method.ReturnType.ToString() }; if (doccomment == null) { return(""); } var doc = DocCommentToXml(doccomment); var nodes = doc.FirstChild.ChildNodes; foreach (var node in nodes) { var element = node as System.Xml.XmlElement; string elementText = element.InnerText.Trim(); if (string.IsNullOrWhiteSpace(elementText)) { continue; } string[] lines = elementText.Split(new char[] { '\n' }); if (element.Name.Equals("summary", StringComparison.OrdinalIgnoreCase)) { foreach (var line in lines) { summary.AppendLine(_T(indentLevel) + line.Trim()); } } else if (element.Name.Equals("returns", StringComparison.OrdinalIgnoreCase)) { returns.AppendLine(); returns.AppendLine(_T(indentLevel) + "Returns:"); returns.Append(_T(indentLevel + 1) + $"{method.ReturnType}: "); bool firstLine = true; foreach (var line in lines) { returnInfo.Description.Add(line.Trim()); if (!firstLine) { returns.Append(_T(indentLevel + 1)); } firstLine = false; returns.AppendLine(line.Trim()); } } else if (element.Name.Equals("param", StringComparison.OrdinalIgnoreCase)) { string parameterName = element.GetAttribute("name"); ParameterInfo pinfo = new ParameterInfo { Name = parameterName }; string paramType = ""; bool isOutParam = false; foreach (var param in method.ParameterList.Parameters) { if (param.Identifier.ToString().Equals(parameterName, StringComparison.Ordinal)) { isOutParam = IsOutParameter(param); paramType = $" ({param.Type})"; pinfo.Type = param.Type.ToString(); } } if (args.Length == 0 && !isOutParam) { args.AppendLine(); args.AppendLine(_T(indentLevel) + "Args:"); } bool added = false; StringBuilder sb = isOutParam ? outArgs : args; foreach (var line in lines) { pinfo.Description.Add(line.Trim()); if (!added) { added = true; sb.AppendLine(_T(indentLevel + 1) + parameterName + paramType + ": " + line.Trim()); continue; } sb.AppendLine(_T(indentLevel + 2) + line.Trim()); } if (!isOutParam) { parameters.Add(pinfo); } } } StringBuilder rc = new StringBuilder(); rc.AppendLine(_T(indentLevel) + "\"\"\""); rc.Append(summary.ToString()); rc.Append(args.ToString()); rc.Append(returns.ToString()); rc.Append(outArgs.ToString()); rc.AppendLine(_T(indentLevel) + "\"\"\""); return(rc.ToString()); }
private IEnumerable<CompletionItem> GetTagsForProperty(IPropertySymbol symbol, DocumentationCommentTriviaSyntax trivia) { var items = new List<CompletionItem>(); if (symbol.IsIndexer) { var parameters = symbol.GetParameters().Select(p => p.Name).ToSet(); RemoveExistingTags(trivia, parameters, x => AttributeSelector(x, ParamTagName)); items.AddRange(parameters.Select(p => CreateCompletionItem(FormatParameter(ParamTagName, p)))); } var typeParameters = symbol.GetTypeArguments().Select(p => p.Name).ToSet(); items.AddRange(typeParameters.Select(t => CreateCompletionItem(TypeParamTagName, NameAttributeName, t))); items.Add(CreateCompletionItem("value")); return items; }