Skip to content

Commit 4da16ee

Browse files
committed
Fix inconsistency that forced full-sentence links to have a glowing white period
1 parent 06903c0 commit 4da16ee

File tree

2 files changed

+49
-8
lines changed

2 files changed

+49
-8
lines changed

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/SA1629DocumentationTextMustEndWithAPeriod.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,7 @@ private static void HandleSectionOrBlockXmlElement(SyntaxNodeAnalysisContext con
125125

126126
if (!string.IsNullOrEmpty(textWithoutTrailingWhitespace))
127127
{
128-
if (!textWithoutTrailingWhitespace.EndsWith(".", StringComparison.Ordinal)
129-
&& !textWithoutTrailingWhitespace.EndsWith(".)", StringComparison.Ordinal)
130-
&& (startingWithFinalParagraph || !textWithoutTrailingWhitespace.EndsWith(":", StringComparison.Ordinal))
131-
&& !textWithoutTrailingWhitespace.EndsWith("-or-", StringComparison.Ordinal))
128+
if (IsMissingRequiredPeriod(textWithoutTrailingWhitespace, startingWithFinalParagraph))
132129
{
133130
int spanStart = textToken.SpanStart + textWithoutTrailingWhitespace.Length;
134131
ImmutableDictionary<string, string> properties = null;
@@ -162,10 +159,15 @@ void SetReplaceChar()
162159
}
163160
else if (xmlElement.Content[i].IsInlineElement() && !currentParagraphDone)
164161
{
165-
// Treat empty XML elements as a "word not ending with a period"
166-
var location = Location.Create(xmlElement.SyntaxTree, new TextSpan(xmlElement.Content[i].Span.End, 1));
167-
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
168-
currentParagraphDone = true;
162+
var lastTextElement = XmlCommentHelper.TryGetLastTextElementWithContent(xmlElement.Content[i]);
163+
164+
if (lastTextElement is null // Treat empty XML elements as a "word not ending with a period"
165+
|| IsMissingRequiredPeriod(lastTextElement.TextTokens.Last().Text.TrimEnd(' ', '\r', '\n'), startingWithFinalParagraph))
166+
{
167+
var location = Location.Create(xmlElement.SyntaxTree, new TextSpan(xmlElement.Content[i].Span.End, 1));
168+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
169+
currentParagraphDone = true;
170+
}
169171
}
170172
else if (xmlElement.Content[i] is XmlElementSyntax childXmlElement)
171173
{
@@ -198,5 +200,13 @@ void SetReplaceChar()
198200
}
199201
}
200202
}
203+
204+
private static bool IsMissingRequiredPeriod(string textWithoutTrailingWhitespace, bool startingWithFinalParagraph)
205+
{
206+
return !textWithoutTrailingWhitespace.EndsWith(".", StringComparison.Ordinal)
207+
&& !textWithoutTrailingWhitespace.EndsWith(".)", StringComparison.Ordinal)
208+
&& (startingWithFinalParagraph || !textWithoutTrailingWhitespace.EndsWith(":", StringComparison.Ordinal))
209+
&& !textWithoutTrailingWhitespace.EndsWith("-or-", StringComparison.Ordinal);
210+
}
201211
}
202212
}

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/XmlCommentHelper.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,37 @@ internal static XmlTextSyntax TryGetFirstTextElementWithContent(XmlNodeSyntax no
191191
return null;
192192
}
193193

194+
/// <summary>
195+
/// Returns the last <see cref="XmlTextSyntax"/> which is not simply empty or whitespace.
196+
/// </summary>
197+
/// <param name="node">The XML content to search.</param>
198+
/// <returns>The last <see cref="XmlTextSyntax"/> which is not simply empty or whitespace, or
199+
/// <see langword="null"/> if no such element exists.</returns>
200+
internal static XmlTextSyntax TryGetLastTextElementWithContent(XmlNodeSyntax node)
201+
{
202+
if (node is XmlEmptyElementSyntax)
203+
{
204+
return null;
205+
}
206+
else if (node is XmlTextSyntax xmlText)
207+
{
208+
return !IsConsideredEmpty(node) ? xmlText : null;
209+
}
210+
else if (node is XmlElementSyntax xmlElement)
211+
{
212+
for (var i = xmlElement.Content.Count - 1; i >= 0; i--)
213+
{
214+
var nestedContent = TryGetFirstTextElementWithContent(xmlElement.Content[i]);
215+
if (nestedContent != null)
216+
{
217+
return nestedContent;
218+
}
219+
}
220+
}
221+
222+
return null;
223+
}
224+
194225
/// <summary>
195226
/// Checks if a <see cref="SyntaxTrivia"/> contains a <see cref="DocumentationCommentTriviaSyntax"/> and returns
196227
/// <see langword="true"/> if it is considered empty.

0 commit comments

Comments
 (0)