|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis.DocumentationComments;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Utilities;
namespace Microsoft.CodeAnalysis.MetadataAsSource
{
internal partial class AbstractMetadataAsSourceService
{
internal class DocCommentFormatter
{
private const int s_indentSize = 2;
private const int s_wrapLength = 80;
private static readonly string s_summaryHeader = FeaturesResources.Summary_colon;
private static readonly string s_paramHeader = FeaturesResources.Parameters_colon;
private const string s_labelFormat = "{0}:";
private static readonly string s_typeParameterHeader = FeaturesResources.Type_parameters_colon;
private static readonly string s_returnsHeader = FeaturesResources.Returns_colon;
private static readonly string s_valueHeader = FeaturesResources.Value_colon;
private static readonly string s_exceptionsHeader = FeaturesResources.Exceptions_colon;
private static readonly string s_remarksHeader = FeaturesResources.Remarks_colon;
internal static ImmutableArray<string> Format(IDocumentationCommentFormattingService docCommentFormattingService, DocumentationComment docComment)
{
var formattedCommentLinesBuilder = ArrayBuilder<string>.GetInstance();
var lineBuilder = new StringBuilder();
var formattedSummaryText = docCommentFormattingService.Format(docComment.SummaryText);
if (!string.IsNullOrWhiteSpace(formattedSummaryText))
{
formattedCommentLinesBuilder.Add(s_summaryHeader);
formattedCommentLinesBuilder.AddRange(CreateWrappedTextFromRawText(formattedSummaryText));
}
var parameterNames = docComment.ParameterNames;
if (parameterNames.Length > 0)
{
formattedCommentLinesBuilder.Add(string.Empty);
formattedCommentLinesBuilder.Add(s_paramHeader);
for (var i = 0; i < parameterNames.Length; i++)
{
if (i != 0)
{
formattedCommentLinesBuilder.Add(string.Empty);
}
lineBuilder.Clear();
lineBuilder.Append(' ', s_indentSize);
lineBuilder.Append(string.Format(s_labelFormat, parameterNames[i]));
formattedCommentLinesBuilder.Add(lineBuilder.ToString());
var rawParameterText = docComment.GetParameterText(parameterNames[i]);
var formattedParameterText = docCommentFormattingService.Format(rawParameterText);
if (!string.IsNullOrWhiteSpace(formattedParameterText))
{
formattedCommentLinesBuilder.AddRange(CreateWrappedTextFromRawText(formattedParameterText));
}
}
}
var typeParameterNames = docComment.TypeParameterNames;
if (typeParameterNames.Length > 0)
{
formattedCommentLinesBuilder.Add(string.Empty);
formattedCommentLinesBuilder.Add(s_typeParameterHeader);
for (var i = 0; i < typeParameterNames.Length; i++)
{
if (i != 0)
{
formattedCommentLinesBuilder.Add(string.Empty);
}
lineBuilder.Clear();
lineBuilder.Append(' ', s_indentSize);
lineBuilder.Append(string.Format(s_labelFormat, typeParameterNames[i]));
formattedCommentLinesBuilder.Add(lineBuilder.ToString());
var rawTypeParameterText = docComment.GetTypeParameterText(typeParameterNames[i]);
var formattedTypeParameterText = docCommentFormattingService.Format(rawTypeParameterText);
if (!string.IsNullOrWhiteSpace(formattedTypeParameterText))
{
formattedCommentLinesBuilder.AddRange(CreateWrappedTextFromRawText(formattedTypeParameterText));
}
}
}
var formattedReturnsText = docCommentFormattingService.Format(docComment.ReturnsText);
if (!string.IsNullOrWhiteSpace(formattedReturnsText))
{
formattedCommentLinesBuilder.Add(string.Empty);
formattedCommentLinesBuilder.Add(s_returnsHeader);
formattedCommentLinesBuilder.AddRange(CreateWrappedTextFromRawText(formattedReturnsText));
}
var formattedValueText = docCommentFormattingService.Format(docComment.ValueText);
if (!string.IsNullOrWhiteSpace(formattedValueText))
{
formattedCommentLinesBuilder.Add(string.Empty);
formattedCommentLinesBuilder.Add(s_valueHeader);
formattedCommentLinesBuilder.AddRange(CreateWrappedTextFromRawText(formattedValueText));
}
var exceptionTypes = docComment.ExceptionTypes;
if (exceptionTypes.Length > 0)
{
formattedCommentLinesBuilder.Add(string.Empty);
formattedCommentLinesBuilder.Add(s_exceptionsHeader);
for (var i = 0; i < exceptionTypes.Length; i++)
{
var rawExceptionTexts = docComment.GetExceptionTexts(exceptionTypes[i]);
for (var j = 0; j < rawExceptionTexts.Length; j++)
{
if (i != 0 || j != 0)
{
formattedCommentLinesBuilder.Add(string.Empty);
}
lineBuilder.Clear();
lineBuilder.Append(' ', s_indentSize);
lineBuilder.Append(string.Format(s_labelFormat, exceptionTypes[i]));
formattedCommentLinesBuilder.Add(lineBuilder.ToString());
var formattedExceptionText = docCommentFormattingService.Format(rawExceptionTexts[j]);
if (!string.IsNullOrWhiteSpace(formattedExceptionText))
{
formattedCommentLinesBuilder.AddRange(CreateWrappedTextFromRawText(formattedExceptionText));
}
}
}
}
var formattedRemarksText = docCommentFormattingService.Format(docComment.RemarksText);
if (!string.IsNullOrWhiteSpace(formattedRemarksText))
{
formattedCommentLinesBuilder.Add(string.Empty);
formattedCommentLinesBuilder.Add(s_remarksHeader);
formattedCommentLinesBuilder.AddRange(CreateWrappedTextFromRawText(formattedRemarksText));
}
// Eliminate any blank lines at the beginning.
while (formattedCommentLinesBuilder is [{ Length: 0 }, ..])
formattedCommentLinesBuilder.RemoveAt(0);
// Eliminate any blank lines at the end.
while (formattedCommentLinesBuilder is [.., { Length: 0 }])
formattedCommentLinesBuilder.RemoveAt(formattedCommentLinesBuilder.Count - 1);
return formattedCommentLinesBuilder.ToImmutableAndFree();
}
private static ImmutableArray<string> CreateWrappedTextFromRawText(string rawText)
{
using var _ = ArrayBuilder<string>.GetInstance(out var lines);
// First split the string into constituent lines.
var split = rawText.Split(new[] { "\r\n" }, System.StringSplitOptions.None);
// Now split each line into multiple lines.
foreach (var item in split)
SplitRawLineIntoFormattedLines(item, lines);
return lines.ToImmutable();
}
private static void SplitRawLineIntoFormattedLines(
string line, ArrayBuilder<string> lines)
{
var indent = new StringBuilder().Append(' ', s_indentSize * 2).ToString();
var words = line.Split(' ');
var firstInLine = true;
var sb = new StringBuilder();
sb.Append(indent);
foreach (var word in words)
{
// We must always append at least one word to ensure progress.
if (firstInLine)
{
firstInLine = false;
}
else
{
sb.Append(' ');
}
sb.Append(word);
if (sb.Length >= s_wrapLength)
{
lines.Add(sb.ToString());
sb.Clear();
sb.Append(indent);
firstInLine = true;
}
}
if (sb.ToString().Trim() != string.Empty)
{
lines.Add(sb.ToString());
}
}
}
}
}
|