Skip to content

Commit ac3ef61

Browse files
committed
Fixed empty lines issues
1 parent 6cbdc46 commit ac3ef61

8 files changed

Lines changed: 85 additions & 19 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs

Lines changed: 77 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace StyleCop.Analyzers.OrderingRules
2828
internal sealed class UsingCodeFixProvider : CodeFixProvider
2929
{
3030
private static readonly List<UsingDirectiveSyntax> EmptyUsingsList = new List<UsingDirectiveSyntax>();
31+
private static readonly SyntaxAnnotation UsingCodeFixAnnotation = new SyntaxAnnotation(nameof(UsingCodeFixProvider));
3132

3233
/// <inheritdoc/>
3334
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
@@ -91,12 +92,16 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
9192
{
9293
case UsingDirectivesPlacement.InsideNamespace:
9394
if (compilationUnit.AttributeLists.Any()
94-
|| compilationUnit.Members.Count != 1
95-
|| namespaceCount != 1)
95+
|| compilationUnit.Members.Count > 1
96+
|| namespaceCount > 1)
9697
{
9798
// Override the user's setting with a more conservative one
9899
usingDirectivesPlacement = UsingDirectivesPlacement.Preserve;
99100
}
101+
else if (namespaceCount == 0)
102+
{
103+
usingDirectivesPlacement = UsingDirectivesPlacement.OutsideNamespace;
104+
}
100105
else
101106
{
102107
usingDirectivesPlacement = UsingDirectivesPlacement.InsideNamespace;
@@ -159,6 +164,8 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
159164
newSyntaxRoot = AddUsingsToCompilationRoot(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
160165
}
161166

167+
// Final cleanup
168+
newSyntaxRoot = StripMultipleBlankLines(newSyntaxRoot);
162169
newSyntaxRoot = ReAddFileHeader(syntaxRoot, newSyntaxRoot);
163170

164171
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());
@@ -174,7 +181,7 @@ private static SyntaxNode ReAddFileHeader(SyntaxNode syntaxRoot, SyntaxNode newS
174181
return newSyntaxRoot;
175182
}
176183

177-
var fileHeader = UsingsHelper.GetFileHeader(oldFirstToken.LeadingTrivia.ToList());
184+
var fileHeader = UsingsHelper.GetFileHeader(oldFirstToken.LeadingTrivia);
178185
if (!fileHeader.Any())
179186
{
180187
return newSyntaxRoot;
@@ -271,9 +278,10 @@ private static SyntaxNode AddUsingsToNamespace(SyntaxNode newSyntaxRoot, UsingsH
271278

272279
var groupedUsings = usingsHelper.GenerateGroupedUsings(usingsHelper.RootSpan, usingsIndentation, withTrailingBlankLine, qualifyNames: false);
273280
groupedUsings = groupedUsings.AddRange(rootNamespace.Usings);
274-
var newRootNamespace = rootNamespace.WithUsings(groupedUsings);
275281

282+
var newRootNamespace = rootNamespace.WithUsings(groupedUsings);
276283
newSyntaxRoot = newSyntaxRoot.ReplaceNode(rootNamespace, newRootNamespace);
284+
277285
return newSyntaxRoot;
278286
}
279287

@@ -285,6 +293,52 @@ private static SyntaxNode AddUsingsToCompilationRoot(SyntaxNode newSyntaxRoot, U
285293
var groupedUsings = usingsHelper.GenerateGroupedUsings(usingsHelper.RootSpan, usingsIndentation, withTrailingBlankLine, qualifyNames: true);
286294
groupedUsings = groupedUsings.AddRange(newCompilationUnit.Usings);
287295
newSyntaxRoot = newCompilationUnit.WithUsings(groupedUsings);
296+
297+
return newSyntaxRoot;
298+
}
299+
300+
private static SyntaxNode StripMultipleBlankLines(SyntaxNode syntaxRoot)
301+
{
302+
var replaceMap = new Dictionary<SyntaxToken, SyntaxToken>();
303+
304+
var usingDirectives = syntaxRoot.GetAnnotatedNodes(UsingCodeFixAnnotation).Cast<UsingDirectiveSyntax>();
305+
306+
foreach (var usingDirective in usingDirectives)
307+
{
308+
var nextToken = usingDirective.SemicolonToken.GetNextToken(true);
309+
310+
// start at -1 to compensate for the always present end-of-line.
311+
var count = -1;
312+
313+
// count the blanks lines at the end of the using statement.
314+
foreach (var trivia in usingDirective.SemicolonToken.TrailingTrivia.Reverse())
315+
{
316+
if (!trivia.IsKind(SyntaxKind.EndOfLineTrivia))
317+
{
318+
break;
319+
}
320+
321+
count++;
322+
}
323+
324+
// count the blank lines at the start of the next token
325+
foreach (var trivia in nextToken.LeadingTrivia)
326+
{
327+
if (!trivia.IsKind(SyntaxKind.EndOfLineTrivia))
328+
{
329+
break;
330+
}
331+
332+
count++;
333+
}
334+
335+
if (count > 1)
336+
{
337+
replaceMap[nextToken] = nextToken.WithLeadingTrivia(nextToken.LeadingTrivia.Skip(count - 1));
338+
}
339+
}
340+
341+
var newSyntaxRoot = syntaxRoot.ReplaceTokens(replaceMap.Keys, (original, rewritten) => replaceMap[original]);
288342
return newSyntaxRoot;
289343
}
290344

@@ -479,7 +533,7 @@ public SyntaxList<UsingDirectiveSyntax> GenerateGroupedUsings(List<UsingDirectiv
479533
return SyntaxFactory.List(usingList);
480534
}
481535

482-
internal static List<SyntaxTrivia> GetFileHeader(List<SyntaxTrivia> newLeadingTrivia)
536+
internal static List<SyntaxTrivia> GetFileHeader(SyntaxTriviaList newLeadingTrivia)
483537
{
484538
var onBlankLine = false;
485539
var hasHeader = false;
@@ -528,10 +582,10 @@ internal static List<SyntaxTrivia> GetFileHeader(List<SyntaxTrivia> newLeadingTr
528582
return hasHeader ? fileHeader : new List<SyntaxTrivia>();
529583
}
530584

531-
private static List<SyntaxTrivia> StripFileHeader(List<SyntaxTrivia> newLeadingTrivia)
585+
private static List<SyntaxTrivia> StripFileHeader(SyntaxTriviaList leadingTrivia)
532586
{
533-
var fileHeader = GetFileHeader(newLeadingTrivia);
534-
return newLeadingTrivia.Skip(fileHeader.Count).ToList();
587+
var fileHeader = GetFileHeader(leadingTrivia);
588+
return leadingTrivia.Skip(fileHeader.Count).ToList();
535589
}
536590

537591
private List<UsingDirectiveSyntax> GenerateUsings(Dictionary<DirectiveSpan, List<UsingDirectiveSyntax>> usingsGroup, DirectiveSpan directiveSpan, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames)
@@ -636,19 +690,20 @@ private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usi
636690
});
637691
}
638692

639-
triviaToMove.AddRange(currentUsing.GetLeadingTrivia().Where(tr => tr.IsDirective || tr.IsKind(SyntaxKind.DisabledTextTrivia)));
640-
641-
// preserve leading trivia (excluding directive trivia), indenting each line as appropriate
642-
var newLeadingTrivia = currentUsing
643-
.GetLeadingTrivia()
644-
.Where(tr => !tr.IsDirective && !tr.IsKind(SyntaxKind.DisabledTextTrivia))
645-
.ToList();
646-
647-
if (i == 0)
693+
// when there is a directive trivia, add it (and any trivia before it) to the triviaToMove collection.
694+
var leadingTrivia = (i == 0) ? StripFileHeader(currentUsing.GetLeadingTrivia()) : currentUsing.GetLeadingTrivia().ToList();
695+
for (var m = leadingTrivia.Count - 1; m >= 0; m--)
648696
{
649-
newLeadingTrivia = StripFileHeader(newLeadingTrivia);
697+
if (leadingTrivia[m].IsDirective)
698+
{
699+
triviaToMove.AddRange(leadingTrivia.Take(m + 1));
700+
break;
701+
}
650702
}
651703

704+
// preserve leading trivia (excluding directive trivia), indenting each line as appropriate
705+
var newLeadingTrivia = leadingTrivia.Except(triviaToMove).ToList();
706+
652707
// strip any leading whitespace on each line (and also all blank lines)
653708
var k = 0;
654709
var startOfLine = true;
@@ -698,7 +753,10 @@ private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usi
698753
newTrailingTrivia = newTrailingTrivia.Add(SyntaxFactory.CarriageReturnLineFeed);
699754
}
700755

701-
var processedUsing = currentUsing.WithLeadingTrivia(newLeadingTrivia).WithTrailingTrivia(newTrailingTrivia);
756+
var processedUsing = currentUsing
757+
.WithLeadingTrivia(newLeadingTrivia)
758+
.WithTrailingTrivia(newTrailingTrivia)
759+
.WithAdditionalAnnotations(UsingCodeFixAnnotation);
702760

703761
// filter duplicate using declarations, preferring to keep the one with an alias
704762
var existingUsing = result.Find(u => string.Equals(u.Name.ToUnaliasedString(), processedUsing.Name.ToUnaliasedString(), StringComparison.Ordinal));

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1208UnitTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ public async Task TestPreprocessorDirectivesAsync()
310310
using Microsoft.CodeAnalysis;
311311
using Microsoft.VisualStudio;
312312
using MyList = System.Collections.Generic.List<int>;
313+
313314
#if true
314315
using System.Collections;
315316
using System.Threading;

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1209UnitTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class A
5959
using System.Linq;
6060
using System.Net;
6161
using TasksNamespace = System.Threading.Tasks;
62+
6263
class A
6364
{
6465
}";
@@ -248,6 +249,7 @@ public async Task TestPreprocessorDirectivesAsync()
248249
var fixedTestCode = @"using System;
249250
using Microsoft.VisualStudio;
250251
using MyList = System.Collections.Generic.List<int>;
252+
251253
#if true
252254
using Microsoft.CodeAnalysis;
253255
using Threads = System.Threading;

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1210CombinedSystemDirectivesUnitTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ public async Task TestPreprocessorDirectivesAsync()
187187
var fixedTestCode = @"using Microsoft.VisualStudio;
188188
using System;
189189
using MyList = System.Collections.Generic.List<int>;
190+
190191
#if true
191192
using Microsoft.CodeAnalysis;
192193
using Microsoft.CodeAnalysis.Diagnostics;

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1210UnitTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ public async Task TestPreprocessorDirectivesAsync()
291291
var fixedTestCode = @"using System;
292292
using Microsoft.VisualStudio;
293293
using MyList = System.Collections.Generic.List<int>;
294+
294295
#if true
295296
using Microsoft.CodeAnalysis;
296297
using Microsoft.CodeAnalysis.Diagnostics;

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1211UnitTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ public async Task TestPreprocessorDirectivesAsync()
179179
var fixedTestCode = @"using System;
180180
using Microsoft.VisualStudio;
181181
using MyList = System.Collections.Generic.List<int>;
182+
182183
#if true
183184
using AThing = System.Threading;
184185
using BThing = System.Threading.Tasks;

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1216UnitTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ public async Task TestPreprocessorDirectivesAsync()
154154
using Microsoft.VisualStudio;
155155
using static System.String;
156156
using MyList = System.Collections.Generic.List<int>;
157+
157158
#if true
158159
using System.Threading;
159160
using System.Threading.Tasks;

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1217UnitTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ public async Task TestPreprocessorDirectivesAsync()
211211
using Microsoft.VisualStudio;
212212
using static System.Tuple;
213213
using MyList = System.Collections.Generic.List<int>;
214+
214215
#if true
215216
using static System.Math;
216217
using static System.String;

0 commit comments

Comments
 (0)