Skip to content

Commit 2f59501

Browse files
committed
Create shared extension methods ReplaceExteriorTrivia and WithoutFirstAndLastNewlines
1 parent e0d09c7 commit 2f59501

2 files changed

Lines changed: 141 additions & 140 deletions

File tree

OpenStackNetAnalyzers/OpenStackNetAnalyzers/DocumentValueFromSummaryCodeFix.cs

Lines changed: 3 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private async Task<Document> CreateChangedDocument(CodeFixContext context, Prope
119119
XmlSyntaxFactory.MultiLineElement(
120120
"value",
121121
XmlSyntaxFactory.List(
122-
XmlSyntaxFactory.ParaElement(XmlSyntaxFactory.PlaceholderElement(RemoveFirstAndListNewlines(summaryContent))),
122+
XmlSyntaxFactory.ParaElement(XmlSyntaxFactory.PlaceholderElement(summaryContent.WithoutFirstAndLastNewlines())),
123123
XmlSyntaxFactory.NewLine(),
124124
XmlSyntaxFactory.TokenElement(defaultValueToken)));
125125

@@ -131,8 +131,8 @@ private async Task<Document> CreateChangedDocument(CodeFixContext context, Prope
131131
SyntaxTrivia exteriorTrivia = GetLastDocumentationCommentExteriorTrivia(documentationComment);
132132
if (!exteriorTrivia.Token.IsMissing)
133133
{
134-
leadingNewLine = ReplaceExteriorTrivia(leadingNewLine, exteriorTrivia);
135-
valueElement = ReplaceExteriorTrivia(valueElement, exteriorTrivia);
134+
leadingNewLine = leadingNewLine.ReplaceExteriorTrivia(exteriorTrivia);
135+
valueElement = valueElement.ReplaceExteriorTrivia(exteriorTrivia);
136136
}
137137

138138
DocumentationCommentTriviaSyntax newDocumentationComment = documentationComment.WithContent(
@@ -168,142 +168,5 @@ private SyntaxTrivia GetLastDocumentationCommentExteriorTrivia(SyntaxNode node)
168168
.Where(trivia => trivia.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia))
169169
.LastOrDefault();
170170
}
171-
172-
private T ReplaceExteriorTrivia<T>(T node, SyntaxTrivia trivia)
173-
where T : SyntaxNode
174-
{
175-
// Make sure to include a space after the '///' characters.
176-
SyntaxTrivia triviaWithSpace = SyntaxFactory.DocumentationCommentExterior(trivia.ToString() + " ");
177-
178-
return node.ReplaceTrivia(
179-
node.DescendantTrivia(descendIntoTrivia: true).Where(i => i.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia)),
180-
(originalTrivia, rewrittenTrivia) => SelectExteriorTrivia(rewrittenTrivia, trivia, triviaWithSpace));
181-
}
182-
183-
private SyntaxTrivia SelectExteriorTrivia(SyntaxTrivia rewrittenTrivia, SyntaxTrivia trivia, SyntaxTrivia triviaWithSpace)
184-
{
185-
// if the trivia had a trailing space, make sure to preserve it
186-
if (rewrittenTrivia.ToString().EndsWith(" "))
187-
return triviaWithSpace;
188-
189-
// otherwise the space is part of the leading trivia of the following token, so don't add an extra one to
190-
// the exterior trivia
191-
return trivia;
192-
}
193-
194-
private SyntaxList<XmlNodeSyntax> RemoveFirstAndListNewlines(SyntaxList<XmlNodeSyntax> summaryContent)
195-
{
196-
if (summaryContent.Count == 0)
197-
return summaryContent;
198-
199-
XmlTextSyntax firstSyntax = summaryContent[0] as XmlTextSyntax;
200-
if (firstSyntax == null)
201-
return summaryContent;
202-
203-
XmlTextSyntax lastSyntax = summaryContent[summaryContent.Count - 1] as XmlTextSyntax;
204-
if (lastSyntax == null)
205-
return summaryContent;
206-
207-
SyntaxTokenList firstSyntaxTokens = firstSyntax.TextTokens;
208-
209-
int removeFromStart;
210-
if (IsNewLine(firstSyntaxTokens[0]))
211-
{
212-
removeFromStart = 1;
213-
}
214-
else
215-
{
216-
if (!IsWhitespace(firstSyntaxTokens[0]))
217-
return summaryContent;
218-
219-
if (!IsNewLine(firstSyntaxTokens[1]))
220-
return summaryContent;
221-
222-
removeFromStart = 2;
223-
}
224-
225-
SyntaxTokenList lastSyntaxTokens = lastSyntax.TextTokens;
226-
227-
int removeFromEnd;
228-
if (IsNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 1]))
229-
{
230-
removeFromEnd = 1;
231-
}
232-
else
233-
{
234-
if (!IsWhitespace(lastSyntaxTokens[lastSyntaxTokens.Count - 1]))
235-
return summaryContent;
236-
237-
if (!IsNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 2]))
238-
return summaryContent;
239-
240-
removeFromEnd = 2;
241-
}
242-
243-
for (int i = 0; i < removeFromStart; i++)
244-
{
245-
firstSyntaxTokens = firstSyntaxTokens.RemoveAt(0);
246-
}
247-
248-
if (firstSyntax == lastSyntax)
249-
{
250-
lastSyntaxTokens = firstSyntaxTokens;
251-
}
252-
253-
for (int i = 0; i < removeFromEnd; i++)
254-
{
255-
lastSyntaxTokens = lastSyntaxTokens.RemoveAt(lastSyntaxTokens.Count - 1);
256-
}
257-
258-
summaryContent = summaryContent.RemoveAt(summaryContent.Count - 1);
259-
if (lastSyntaxTokens.Count != 0)
260-
summaryContent = summaryContent.Add(lastSyntax.WithTextTokens(lastSyntaxTokens));
261-
262-
if (firstSyntax != lastSyntax)
263-
{
264-
summaryContent = summaryContent.RemoveAt(0);
265-
if (firstSyntaxTokens.Count != 0)
266-
summaryContent = summaryContent.Insert(0, firstSyntax.WithTextTokens(firstSyntaxTokens));
267-
}
268-
269-
if (summaryContent.Count > 0)
270-
{
271-
// Make sure to remove the leading trivia
272-
summaryContent = summaryContent.Replace(summaryContent[0], summaryContent[0].WithLeadingTrivia());
273-
274-
// Remove leading spaces (between the <para> start tag and the start of the paragraph content)
275-
XmlTextSyntax firstTextSyntax = summaryContent[0] as XmlTextSyntax;
276-
if (firstTextSyntax != null && firstTextSyntax.TextTokens.Count > 0)
277-
{
278-
SyntaxToken firstTextToken = firstTextSyntax.TextTokens[0];
279-
string firstTokenText = firstTextToken.Text;
280-
string trimmed = firstTokenText.TrimStart();
281-
if (trimmed != firstTokenText)
282-
{
283-
SyntaxToken newFirstToken = SyntaxFactory.Token(
284-
firstTextToken.LeadingTrivia,
285-
firstTextToken.CSharpKind(),
286-
trimmed,
287-
firstTextToken.ValueText.TrimStart(),
288-
firstTextToken.TrailingTrivia);
289-
290-
summaryContent = summaryContent.Replace(firstTextSyntax, firstTextSyntax.ReplaceToken(firstTextToken, newFirstToken));
291-
}
292-
}
293-
}
294-
295-
return summaryContent;
296-
}
297-
298-
private bool IsNewLine(SyntaxToken node)
299-
{
300-
return node.IsKind(SyntaxKind.XmlTextLiteralNewLineToken);
301-
}
302-
303-
private bool IsWhitespace(SyntaxToken node)
304-
{
305-
return node.IsKind(SyntaxKind.XmlTextLiteralToken)
306-
&& string.IsNullOrWhiteSpace(node.Text);
307-
}
308171
}
309172
}

OpenStackNetAnalyzers/OpenStackNetAnalyzers/DocumentationSyntaxExtensions.cs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Linq;
66
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CSharp;
78
using Microsoft.CodeAnalysis.CSharp.Syntax;
89

910
internal static class DocumentationSyntaxExtensions
@@ -48,5 +49,142 @@ public static IEnumerable<XmlNodeSyntax> GetXmlElements(this SyntaxList<XmlNodeS
4849
}
4950
}
5051
}
52+
53+
public static T ReplaceExteriorTrivia<T>(this T node, SyntaxTrivia trivia)
54+
where T : XmlNodeSyntax
55+
{
56+
// Make sure to include a space after the '///' characters.
57+
SyntaxTrivia triviaWithSpace = SyntaxFactory.DocumentationCommentExterior(trivia.ToString() + " ");
58+
59+
return node.ReplaceTrivia(
60+
node.DescendantTrivia(descendIntoTrivia: true).Where(i => i.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia)),
61+
(originalTrivia, rewrittenTrivia) => SelectExteriorTrivia(rewrittenTrivia, trivia, triviaWithSpace));
62+
}
63+
64+
private static SyntaxTrivia SelectExteriorTrivia(SyntaxTrivia rewrittenTrivia, SyntaxTrivia trivia, SyntaxTrivia triviaWithSpace)
65+
{
66+
// if the trivia had a trailing space, make sure to preserve it
67+
if (rewrittenTrivia.ToString().EndsWith(" "))
68+
return triviaWithSpace;
69+
70+
// otherwise the space is part of the leading trivia of the following token, so don't add an extra one to
71+
// the exterior trivia
72+
return trivia;
73+
}
74+
75+
public static SyntaxList<XmlNodeSyntax> WithoutFirstAndLastNewlines(this SyntaxList<XmlNodeSyntax> summaryContent)
76+
{
77+
if (summaryContent.Count == 0)
78+
return summaryContent;
79+
80+
XmlTextSyntax firstSyntax = summaryContent[0] as XmlTextSyntax;
81+
if (firstSyntax == null)
82+
return summaryContent;
83+
84+
XmlTextSyntax lastSyntax = summaryContent[summaryContent.Count - 1] as XmlTextSyntax;
85+
if (lastSyntax == null)
86+
return summaryContent;
87+
88+
SyntaxTokenList firstSyntaxTokens = firstSyntax.TextTokens;
89+
90+
int removeFromStart;
91+
if (IsXmlNewLine(firstSyntaxTokens[0]))
92+
{
93+
removeFromStart = 1;
94+
}
95+
else
96+
{
97+
if (!IsXmlWhitespace(firstSyntaxTokens[0]))
98+
return summaryContent;
99+
100+
if (!IsXmlNewLine(firstSyntaxTokens[1]))
101+
return summaryContent;
102+
103+
removeFromStart = 2;
104+
}
105+
106+
SyntaxTokenList lastSyntaxTokens = lastSyntax.TextTokens;
107+
108+
int removeFromEnd;
109+
if (IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 1]))
110+
{
111+
removeFromEnd = 1;
112+
}
113+
else
114+
{
115+
if (!IsXmlWhitespace(lastSyntaxTokens[lastSyntaxTokens.Count - 1]))
116+
return summaryContent;
117+
118+
if (!IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 2]))
119+
return summaryContent;
120+
121+
removeFromEnd = 2;
122+
}
123+
124+
for (int i = 0; i < removeFromStart; i++)
125+
{
126+
firstSyntaxTokens = firstSyntaxTokens.RemoveAt(0);
127+
}
128+
129+
if (firstSyntax == lastSyntax)
130+
{
131+
lastSyntaxTokens = firstSyntaxTokens;
132+
}
133+
134+
for (int i = 0; i < removeFromEnd; i++)
135+
{
136+
lastSyntaxTokens = lastSyntaxTokens.RemoveAt(lastSyntaxTokens.Count - 1);
137+
}
138+
139+
summaryContent = summaryContent.RemoveAt(summaryContent.Count - 1);
140+
if (lastSyntaxTokens.Count != 0)
141+
summaryContent = summaryContent.Add(lastSyntax.WithTextTokens(lastSyntaxTokens));
142+
143+
if (firstSyntax != lastSyntax)
144+
{
145+
summaryContent = summaryContent.RemoveAt(0);
146+
if (firstSyntaxTokens.Count != 0)
147+
summaryContent = summaryContent.Insert(0, firstSyntax.WithTextTokens(firstSyntaxTokens));
148+
}
149+
150+
if (summaryContent.Count > 0)
151+
{
152+
// Make sure to remove the leading trivia
153+
summaryContent = summaryContent.Replace(summaryContent[0], summaryContent[0].WithLeadingTrivia());
154+
155+
// Remove leading spaces (between the <para> start tag and the start of the paragraph content)
156+
XmlTextSyntax firstTextSyntax = summaryContent[0] as XmlTextSyntax;
157+
if (firstTextSyntax != null && firstTextSyntax.TextTokens.Count > 0)
158+
{
159+
SyntaxToken firstTextToken = firstTextSyntax.TextTokens[0];
160+
string firstTokenText = firstTextToken.Text;
161+
string trimmed = firstTokenText.TrimStart();
162+
if (trimmed != firstTokenText)
163+
{
164+
SyntaxToken newFirstToken = SyntaxFactory.Token(
165+
firstTextToken.LeadingTrivia,
166+
firstTextToken.CSharpKind(),
167+
trimmed,
168+
firstTextToken.ValueText.TrimStart(),
169+
firstTextToken.TrailingTrivia);
170+
171+
summaryContent = summaryContent.Replace(firstTextSyntax, firstTextSyntax.ReplaceToken(firstTextToken, newFirstToken));
172+
}
173+
}
174+
}
175+
176+
return summaryContent;
177+
}
178+
179+
public static bool IsXmlNewLine(this SyntaxToken node)
180+
{
181+
return node.IsKind(SyntaxKind.XmlTextLiteralNewLineToken);
182+
}
183+
184+
public static bool IsXmlWhitespace(this SyntaxToken node)
185+
{
186+
return node.IsKind(SyntaxKind.XmlTextLiteralToken)
187+
&& string.IsNullOrWhiteSpace(node.Text);
188+
}
51189
}
52190
}

0 commit comments

Comments
 (0)