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)); }
private bool TryRemoveSummaryPrefix(ref SyntaxList <XmlNodeSyntax> summaryContent, string prefix) { XmlNodeSyntax firstContent = summaryContent.FirstOrDefault(IsContentElement); XmlTextSyntax firstText = firstContent as XmlTextSyntax; if (firstText == null) { return(false); } string firstTextContent = string.Concat(firstText.DescendantTokens()); if (!firstTextContent.TrimStart().StartsWith(prefix, StringComparison.Ordinal)) { return(false); } // Find the token containing the prefix, such as "Gets or sets " SyntaxToken prefixToken = default(SyntaxToken); foreach (SyntaxToken textToken in firstText.TextTokens) { if (textToken.IsMissing) { continue; } if (!textToken.Text.TrimStart().StartsWith(prefix, StringComparison.Ordinal)) { continue; } prefixToken = textToken; break; } if (prefixToken.IsMissingOrDefault()) { return(false); } string text = prefixToken.Text; string valueText = prefixToken.ValueText; int index = text.IndexOf(prefix); if (index >= 0) { bool additionalCharacters = index + prefix.Length < text.Length; text = text.Substring(0, index) + (additionalCharacters ? char.ToUpperInvariant(text[index + prefix.Length]).ToString() : string.Empty) + text.Substring(index + (additionalCharacters ? (prefix.Length + 1) : prefix.Length)); } index = valueText.IndexOf(prefix); if (index >= 0) { valueText = valueText.Remove(index, prefix.Length); } SyntaxToken replaced = SyntaxFactory.Token(prefixToken.LeadingTrivia, prefixToken.Kind(), text, valueText, prefixToken.TrailingTrivia); summaryContent = summaryContent.Replace(firstText, firstText.ReplaceToken(prefixToken, replaced)); return(true); }