Skip to content

Commit

Permalink
- Improve newline/whitespace stripping
Browse files Browse the repository at this point in the history
- Add example parsing
  • Loading branch information
eNeRGy164 committed Sep 19, 2019
1 parent 6004492 commit e5943de
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public static DocumentationCommentsDescription Parse(string documentationComment
documentation.Example = documentation.ParseSection(element.Element(Section.Example));
documentation.Remarks = documentation.ParseSection(element.Element(Section.Remarks));
documentation.Returns = documentation.ParseSection(element.Element(Section.Returns));
documentation.Summary = documentation.ParseSection(element.Element(Section.Summary));
documentation.Summary = documentation.ParseSection(element.Element(Section.Summary), true);
documentation.Value = documentation.ParseSection(element.Element(Section.Value));

documentation.ParseSection(element.Elements(Section.Exception));
Expand Down Expand Up @@ -91,7 +91,7 @@ private void ParseSection(IEnumerable<XElement> sections)
break;

case Section.SeeAlso when !string.IsNullOrWhiteSpace(section.Attribute(Attribute.CRef).Value):
this.ProcessSeeAlsoTag(section);
this.ProcessSeeAlsoTag(section, false);
break;

case Section.TypeParam when !string.IsNullOrWhiteSpace(section.Attribute(Attribute.Name).Value):
Expand All @@ -101,7 +101,7 @@ private void ParseSection(IEnumerable<XElement> sections)
}
}

private string ParseSection(XElement section)
private string ParseSection(XElement section, bool removeNewLines = false)
{
if (section == null || section.IsEmpty)
{
Expand All @@ -115,35 +115,35 @@ private string ParseSection(XElement section)
switch (node)
{
case XText text:
ProcessInlineContent(contents, text.Value);
ProcessInlineContent(contents, text.Value, removeNewLines);
break;

case XElement element when element.Name == Block.Code || element.Name == Block.Para:
ProcessBlockContent(contents, element.Value);
this.ProcessBlockContent(contents, element);
break;

case XElement element when element.Name == Block.List:
this.ProcessListContent(contents, element);
break;

case XElement element when element.Name == Inline.C:
ProcessInlineContent(contents, element.Value);
ProcessInlineContent(contents, element.Value, removeNewLines);
break;

case XElement element when (element.Name == Inline.ParamRef || element.Name == Inline.TypeParamRef) && element.Attribute(Attribute.Name) != null:
ProcessInlineContent(contents, StripIDPrefix(element.Attribute(Attribute.Name).Value));
ProcessInlineContent(contents, StripIDPrefix(element.Attribute(Attribute.Name).Value), removeNewLines);
break;

case XElement element when element.Name == Inline.See:
ProcessInlineContent(contents, element.IsEmpty ? StripIDPrefix(element.Attribute(Attribute.CRef)?.Value) : element.Value);
ProcessInlineContent(contents, element.IsEmpty ? StripIDPrefix(element.Attribute(Attribute.CRef)?.Value) : element.Value, removeNewLines);
break;

case XElement element when element.Name == Section.SeeAlso:
this.ProcessSeeAlsoTag(element);
this.ProcessSeeAlsoTag(element, removeNewLines);
break;

case XElement element when !element.IsEmpty:
ProcessInlineContent(contents, element.Value);
ProcessInlineContent(contents, element.Value, removeNewLines);
break;
}
}
Expand All @@ -159,8 +159,8 @@ private void ProcessListContent(StringBuilder contents, XElement element)
case ListType.Bullet:
foreach (var item in element.Elements(List.Item))
{
var term = item.Element(List.Term)?.Value.Trim() ?? string.Empty;
var description = item.Element(List.Description)?.Value.Trim() ?? string.Empty;
var term = this.ParseSection(item.Element(List.Term));
var description = this.ParseSection(item.Element(List.Description));

contents.Append("* ");

Expand All @@ -186,15 +186,15 @@ private void ProcessListContent(StringBuilder contents, XElement element)
break;

case ListType.Number:
if (!int.TryParse(element.Attribute(Attribute.Start)?.Value ?? "1", out var startIndex))
if (!int.TryParse(element.Attribute(Attribute.Start)?.Value.Trim() ?? "1", out var startIndex))
{
startIndex = 1;
}

foreach (var item in element.Elements(List.Item))
{
var term = item.Element(List.Term)?.Value.Trim() ?? string.Empty;
var description = item.Element(List.Description)?.Value.Trim() ?? string.Empty;
var term = this.ParseSection(item.Element(List.Term));
var description = this.ParseSection(item.Element(List.Description));

contents.Append(startIndex);
contents.Append(". ");
Expand Down Expand Up @@ -224,8 +224,8 @@ private void ProcessListContent(StringBuilder contents, XElement element)
case ListType.Definition:
foreach (var item in element.Elements(List.Item))
{
var term = item.Element(List.Term)?.Value.Trim() ?? string.Empty;
var description = item.Element(List.Description)?.Value.Trim() ?? string.Empty;
var term = this.ParseSection(item.Element(List.Term));
var description = this.ParseSection(item.Element(List.Description));

contents.Append(term);
contents.Append('\n');
Expand All @@ -241,34 +241,41 @@ private void ProcessListContent(StringBuilder contents, XElement element)
}
}

private static void ProcessInlineContent(StringBuilder stringBuilder, string text)
private static void ProcessInlineContent(StringBuilder stringBuilder, string text, bool removeNewLines)
{
if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != ' ' && stringBuilder[stringBuilder.Length - 1] != '\n')
{
stringBuilder.Append(' ');
}

stringBuilder.Append(InlineWhitespace.Replace(text, " ").Trim());
if (removeNewLines)
{
stringBuilder.Append(InlineWhitespace.Replace(text, " ").Trim());
}
else
{
stringBuilder.Append(string.Join("\n", text.Split('\n').Select(t => t.Trim())));
}
}

private static void ProcessBlockContent(StringBuilder stringBuilder, string text)
private void ProcessBlockContent(StringBuilder stringBuilder, XElement element)
{
if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '\n')
{
stringBuilder.Append('\n');
}

stringBuilder.Append(InlineWhitespace.Replace(text, " "));
stringBuilder.Append(this.ParseSection(element));
stringBuilder.Append('\n');
}

private void ProcessSeeAlsoTag(XElement element)
private void ProcessSeeAlsoTag(XElement element, bool removeNewLines)
{
var key = StripIDPrefix(element.Attribute(Attribute.CRef).Value);

var contents = new StringBuilder();

ProcessInlineContent(contents, element.Value);
ProcessInlineContent(contents, element.Value, removeNewLines);

var value = contents.ToString().Trim();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,37 @@ class Test
types[0].DocumentationComments.Remarks.Should().Be("A remark.");
}

[TestMethod]
public void Example_Should_BeRead()
{
// Assign
var source = @"
class Test
{
/// <example>
/// The following example demonstrates the use of this method.
///
/// <code>
/// // Get a new random number
/// SampleClass sc = new SampleClass(10);
///
/// int random = sc.GetRandomNumber();
///
/// Console.WriteLine(""Random value: {0}"", random);
/// </code>
/// </example>
void Method() {}
}
";

// Act
var types = TestHelper.VisitSyntaxTree(source);

// Assert
types[0].Methods[0].DocumentationComments.Example.Should().Be("The following example demonstrates the use of this method.\n\n// Get a new random number\nSampleClass sc = new SampleClass(10);\n\nint random = sc.GetRandomNumber();\n\nConsole.WriteLine(\"Random value: {0}\", random);");
}


[TestMethod]
public void Exception_Should_BeRead()
{
Expand Down Expand Up @@ -309,6 +340,27 @@ class Test
types[0].DocumentationComments.Summary.Should().Be("A");
}

[TestMethod]
public void NonSummaryWithWhitespace_Should_OnlyNonNewLinesBeTrimmed()
{
// Assign
var source = @"
/// <remarks>
/// A
/// B
/// </remarks>
class Test
{
}
";

// Act
var types = TestHelper.VisitSyntaxTree(source);

// Assert
types[0].DocumentationComments.Remarks.Should().Be("A\nB");
}

[TestMethod]
public void TagWithSeeAlso_Should_IgnoreSeeAlso()
{
Expand Down Expand Up @@ -748,5 +800,42 @@ class Test
// Assert
types[0].DocumentationComments.Summary.Should().Be("Term 1\n First item\nTerm 2\n Second item");
}

[TestMethod]
public void TagWithNestedContent_Should_RenderCorrectly()
{
// Assign
var source = @"
/// <summary>
/// This is a summary with mixed content.
/// <para>A <see cref=""System.Object"">paragraph</see></para>
/// <para>Another <paramref name=""paragraph""/></para>
/// <list type=""definition"">
/// <item>
/// <term>Term 1</term>
/// <description>First <typeparamref name=""item""/></description>
/// </item>
/// <item>
/// <term>Term <c>2</c></term>
/// <description><para>Second item</para></description>
/// </item>
/// </list>
/// <code>
/// class ACodeSample { }
/// </code>
/// More text <c>null</c> and more text
/// <seealso cref=""System.Object""/>
/// </summary>
class Test
{
}
";

// Act
var types = TestHelper.VisitSyntaxTree(source);

// Assert
types[0].DocumentationComments.Summary.Should().Be("This is a summary with mixed content.\nA paragraph\nAnother paragraph\nTerm 1\n First item\nTerm 2\n Second item\nclass ACodeSample { }\nMore text null and more text");
}
}
}

0 comments on commit e5943de

Please sign in to comment.