Skip to content

Commit cb774f0

Browse files
committed
Merge pull request #2 from vweijsters/usings-config-vw
Fixed empty lines issues
2 parents ee80333 + ac3ef61 commit cb774f0

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
@@ -27,6 +27,7 @@ namespace StyleCop.Analyzers.OrderingRules
2727
internal sealed class UsingCodeFixProvider : CodeFixProvider
2828
{
2929
private static readonly List<UsingDirectiveSyntax> EmptyUsingsList = new List<UsingDirectiveSyntax>();
30+
private static readonly SyntaxAnnotation UsingCodeFixAnnotation = new SyntaxAnnotation(nameof(UsingCodeFixProvider));
3031

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

166+
// Final cleanup
167+
newSyntaxRoot = StripMultipleBlankLines(newSyntaxRoot);
161168
newSyntaxRoot = ReAddFileHeader(syntaxRoot, newSyntaxRoot);
162169

163170
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());
@@ -173,7 +180,7 @@ private static SyntaxNode ReAddFileHeader(SyntaxNode syntaxRoot, SyntaxNode newS
173180
return newSyntaxRoot;
174181
}
175182

176-
var fileHeader = UsingsHelper.GetFileHeader(oldFirstToken.LeadingTrivia.ToList());
183+
var fileHeader = UsingsHelper.GetFileHeader(oldFirstToken.LeadingTrivia);
177184
if (!fileHeader.Any())
178185
{
179186
return newSyntaxRoot;
@@ -270,9 +277,10 @@ private static SyntaxNode AddUsingsToNamespace(SyntaxNode newSyntaxRoot, UsingsH
270277

271278
var groupedUsings = usingsHelper.GenerateGroupedUsings(usingsHelper.RootSpan, usingsIndentation, withTrailingBlankLine, qualifyNames: false);
272279
groupedUsings = groupedUsings.AddRange(rootNamespace.Usings);
273-
var newRootNamespace = rootNamespace.WithUsings(groupedUsings);
274280

281+
var newRootNamespace = rootNamespace.WithUsings(groupedUsings);
275282
newSyntaxRoot = newSyntaxRoot.ReplaceNode(rootNamespace, newRootNamespace);
283+
276284
return newSyntaxRoot;
277285
}
278286

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

@@ -478,7 +532,7 @@ public SyntaxList<UsingDirectiveSyntax> GenerateGroupedUsings(List<UsingDirectiv
478532
return SyntaxFactory.List(usingList);
479533
}
480534

481-
internal static List<SyntaxTrivia> GetFileHeader(List<SyntaxTrivia> newLeadingTrivia)
535+
internal static List<SyntaxTrivia> GetFileHeader(SyntaxTriviaList newLeadingTrivia)
482536
{
483537
var onBlankLine = false;
484538
var hasHeader = false;
@@ -527,10 +581,10 @@ internal static List<SyntaxTrivia> GetFileHeader(List<SyntaxTrivia> newLeadingTr
527581
return hasHeader ? fileHeader : new List<SyntaxTrivia>();
528582
}
529583

530-
private static List<SyntaxTrivia> StripFileHeader(List<SyntaxTrivia> newLeadingTrivia)
584+
private static List<SyntaxTrivia> StripFileHeader(SyntaxTriviaList leadingTrivia)
531585
{
532-
var fileHeader = GetFileHeader(newLeadingTrivia);
533-
return newLeadingTrivia.Skip(fileHeader.Count).ToList();
586+
var fileHeader = GetFileHeader(leadingTrivia);
587+
return leadingTrivia.Skip(fileHeader.Count).ToList();
534588
}
535589

536590
private List<UsingDirectiveSyntax> GenerateUsings(Dictionary<DirectiveSpan, List<UsingDirectiveSyntax>> usingsGroup, DirectiveSpan directiveSpan, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames)
@@ -564,19 +618,20 @@ private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usi
564618
currentUsing = this.QualifyUsingDirective(currentUsing);
565619
}
566620

567-
triviaToMove.AddRange(currentUsing.GetLeadingTrivia().Where(tr => tr.IsDirective || tr.IsKind(SyntaxKind.DisabledTextTrivia)));
568-
569-
// preserve leading trivia (excluding directive trivia), indenting each line as appropriate
570-
var newLeadingTrivia = currentUsing
571-
.GetLeadingTrivia()
572-
.Where(tr => !tr.IsDirective && !tr.IsKind(SyntaxKind.DisabledTextTrivia))
573-
.ToList();
574-
575-
if (i == 0)
621+
// when there is a directive trivia, add it (and any trivia before it) to the triviaToMove collection.
622+
var leadingTrivia = (i == 0) ? StripFileHeader(currentUsing.GetLeadingTrivia()) : currentUsing.GetLeadingTrivia().ToList();
623+
for (var m = leadingTrivia.Count - 1; m >= 0; m--)
576624
{
577-
newLeadingTrivia = StripFileHeader(newLeadingTrivia);
625+
if (leadingTrivia[m].IsDirective)
626+
{
627+
triviaToMove.AddRange(leadingTrivia.Take(m + 1));
628+
break;
629+
}
578630
}
579631

632+
// preserve leading trivia (excluding directive trivia), indenting each line as appropriate
633+
var newLeadingTrivia = leadingTrivia.Except(triviaToMove).ToList();
634+
580635
// strip any leading whitespace on each line (and also all blank lines)
581636
var k = 0;
582637
var startOfLine = true;
@@ -626,7 +681,10 @@ private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usi
626681
newTrailingTrivia = newTrailingTrivia.Add(SyntaxFactory.CarriageReturnLineFeed);
627682
}
628683

629-
var processedUsing = currentUsing.WithLeadingTrivia(newLeadingTrivia).WithTrailingTrivia(newTrailingTrivia);
684+
var processedUsing = currentUsing
685+
.WithLeadingTrivia(newLeadingTrivia)
686+
.WithTrailingTrivia(newTrailingTrivia)
687+
.WithAdditionalAnnotations(UsingCodeFixAnnotation);
630688

631689
// filter duplicate using declarations, preferring to keep the one with an alias
632690
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)