private static async Task <Document> FormatSummaryOnMultipleLinesAsync( Document document, DocumentationCommentTriviaSyntax documentationComment, CancellationToken cancellationToken) { SourceText sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); XmlElementSyntax summaryElement = documentationComment.SummaryElement(); string indentation = ""; SyntaxTrivia parentTrivia = documentationComment.ParentTrivia; SyntaxToken token = parentTrivia.Token; SyntaxTriviaList leadingTrivia = token.LeadingTrivia; int index = leadingTrivia.IndexOf(parentTrivia); if (index > 0) { SyntaxTrivia previousTrivia = token.LeadingTrivia[index - 1]; if (previousTrivia.IsWhitespaceTrivia()) { indentation = previousTrivia.ToString(); } } string endOfLine = documentationComment.DescendantTokens().FirstOrDefault(f => f.IsKind(SyntaxKind.XmlTextLiteralNewLineToken)).ToString(); if (endOfLine.Length == 0) { endOfLine = Environment.NewLine; } string startOfLine = endOfLine + indentation + "/// "; SourceText newSourceText = sourceText.WithChanges( new TextChange(new TextSpan(summaryElement.StartTag.Span.End, 0), startOfLine), new TextChange(new TextSpan(summaryElement.EndTag.SpanStart, 0), startOfLine)); return(document.WithText(newSourceText)); }
private static string GetInteriorLinePrefix(DocumentationCommentTriviaSyntax node) { switch (node.Kind()) { case SyntaxKind.SingleLineDocumentationCommentTrivia: { var firstToken = node.GetFirstToken(); char firstChar; if (!TryGetFirstChar(firstToken, out firstChar)) { return(string.Empty); } if (!IsWhitespace(firstChar)) { return(string.Empty); } // Check for a matching whitespace character on every line. foreach (var token in node.DescendantTokens()) { if (token.Kind() == SyntaxKind.XmlTextLiteralNewLineToken) { var nextToken = token.GetNextToken(); // If the next token is beyond the end of the comment then the new // line was the end of the comment. if (!node.Span.Contains(nextToken.Span)) { break; } if (!TryGetFirstChar(nextToken, out char c) || c != firstChar) { return(string.Empty); } } } return(firstChar.ToString(System.Globalization.CultureInfo.InvariantCulture)); } case SyntaxKind.MultiLineDocumentationCommentTrivia: { string whitespace = null; // Check for a matching whitespace prefix on every line after the first. foreach (var token in node.DescendantTokens()) { if (token.Kind() == SyntaxKind.XmlTextLiteralNewLineToken) { var nextToken = token.GetNextToken(); // If the next token is beyond the end of the comment then the new // line was the end of the comment. if (!node.Span.Contains(nextToken.Span)) { break; } string text; if (!TryGetLiteralText(nextToken, out text)) { return(string.Empty); } if (whitespace == null) { // The whitespace prefix is the leading whitespace of the text. for (int i = 0; i < text.Length; ++i) { if (!IsWhitespace(text[i])) { text = text.Substring(0, i - 1); } } whitespace = text; } else if (!text.StartsWith(whitespace, StringComparison.Ordinal)) { // The new whitespace prefix is the common prefix of the text // and the old whitespace prefix. for (int i = 0; i < text.Length; ++i) { if (text[i] != whitespace[i]) { text = text.Substring(0, i - 1); } } whitespace = text; } } } return(whitespace ?? string.Empty); } default: throw new InvalidOperationException("Unreachable."); } bool TryGetFirstChar(SyntaxToken token, out char c) { string text; if (!TryGetLiteralText(token, out text) || text.Length < 1) { c = default; return(false); } c = text[0]; return(true); } bool TryGetLiteralText(SyntaxToken token, out string text) { if (token.Kind() != SyntaxKind.XmlTextLiteralToken) { text = default; return(false); } text = token.ToString(); return(true); } }