Skip to content

Commit

Permalink
Fix inconsistency that forced full-sentence links to have a glowing w…
Browse files Browse the repository at this point in the history
…hite period
  • Loading branch information
jnm2 committed Jul 19, 2021
1 parent 06903c0 commit 0fe9179
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace StyleCop.Analyzers.DocumentationRules
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -125,10 +126,7 @@ private static void HandleSectionOrBlockXmlElement(SyntaxNodeAnalysisContext con

if (!string.IsNullOrEmpty(textWithoutTrailingWhitespace))
{
if (!textWithoutTrailingWhitespace.EndsWith(".", StringComparison.Ordinal)
&& !textWithoutTrailingWhitespace.EndsWith(".)", StringComparison.Ordinal)
&& (startingWithFinalParagraph || !textWithoutTrailingWhitespace.EndsWith(":", StringComparison.Ordinal))
&& !textWithoutTrailingWhitespace.EndsWith("-or-", StringComparison.Ordinal))
if (IsMissingRequiredPeriod(textWithoutTrailingWhitespace, startingWithFinalParagraph))
{
int spanStart = textToken.SpanStart + textWithoutTrailingWhitespace.Length;
ImmutableDictionary<string, string> properties = null;
Expand Down Expand Up @@ -162,10 +160,15 @@ void SetReplaceChar()
}
else if (xmlElement.Content[i].IsInlineElement() && !currentParagraphDone)
{
// Treat empty XML elements as a "word not ending with a period"
var location = Location.Create(xmlElement.SyntaxTree, new TextSpan(xmlElement.Content[i].Span.End, 1));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
currentParagraphDone = true;
var lastTextElement = XmlCommentHelper.TryGetLastTextElementWithContent(xmlElement.Content[i]);

if (lastTextElement is null // Treat empty XML elements as a "word not ending with a period"
|| IsMissingRequiredPeriod(lastTextElement.TextTokens.Last().Text.TrimEnd(' ', '\r', '\n'), startingWithFinalParagraph))
{
var location = Location.Create(xmlElement.SyntaxTree, new TextSpan(xmlElement.Content[i].Span.End, 1));
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
currentParagraphDone = true;
}
}
else if (xmlElement.Content[i] is XmlElementSyntax childXmlElement)
{
Expand Down Expand Up @@ -198,5 +201,13 @@ void SetReplaceChar()
}
}
}

private static bool IsMissingRequiredPeriod(string textWithoutTrailingWhitespace, bool startingWithFinalParagraph)
{
return !textWithoutTrailingWhitespace.EndsWith(".", StringComparison.Ordinal)
&& !textWithoutTrailingWhitespace.EndsWith(".)", StringComparison.Ordinal)
&& (startingWithFinalParagraph || !textWithoutTrailingWhitespace.EndsWith(":", StringComparison.Ordinal))
&& !textWithoutTrailingWhitespace.EndsWith("-or-", StringComparison.Ordinal);
}
}
}
31 changes: 31 additions & 0 deletions StyleCop.Analyzers/StyleCop.Analyzers/Helpers/XmlCommentHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,37 @@ internal static XmlTextSyntax TryGetFirstTextElementWithContent(XmlNodeSyntax no
return null;
}

/// <summary>
/// Returns the last <see cref="XmlTextSyntax"/> which is not simply empty or whitespace.
/// </summary>
/// <param name="node">The XML content to search.</param>
/// <returns>The last <see cref="XmlTextSyntax"/> which is not simply empty or whitespace, or
/// <see langword="null"/> if no such element exists.</returns>
internal static XmlTextSyntax TryGetLastTextElementWithContent(XmlNodeSyntax node)
{
if (node is XmlEmptyElementSyntax)
{
return null;
}
else if (node is XmlTextSyntax xmlText)
{
return !IsConsideredEmpty(node) ? xmlText : null;
}
else if (node is XmlElementSyntax xmlElement)
{
for (var i = xmlElement.Content.Count - 1; i >= 0; i--)
{
var nestedContent = TryGetFirstTextElementWithContent(xmlElement.Content[i]);
if (nestedContent != null)
{
return nestedContent;
}
}
}

return null;
}

/// <summary>
/// Checks if a <see cref="SyntaxTrivia"/> contains a <see cref="DocumentationCommentTriviaSyntax"/> and returns
/// <see langword="true"/> if it is considered empty.
Expand Down

0 comments on commit 0fe9179

Please sign in to comment.