forked from dotnet/roslynator
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Analyzer: Add/remove blank line after file scoped namespace (dotnet#993)
- Loading branch information
1 parent
23e81b1
commit 8320d2a
Showing
12 changed files
with
540 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
src/Formatting.Analyzers.CodeFixes/CSharp/FileScopedNamespaceDeclarationCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Roslynator.CSharp; | ||
using Roslynator.Formatting.CSharp; | ||
using Roslynator.CSharp.CodeStyle; | ||
using System.Diagnostics; | ||
|
||
namespace Roslynator.Formatting.CodeFixes.CSharp; | ||
|
||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FileScopedNamespaceDeclarationCodeFixProvider))] | ||
[Shared] | ||
public sealed class FileScopedNamespaceDeclarationCodeFixProvider : BaseCodeFixProvider | ||
{ | ||
public override ImmutableArray<string> FixableDiagnosticIds | ||
{ | ||
get { return ImmutableArray.Create(DiagnosticIdentifiers.BlankLineAfterFileScopedNamespaceDeclaration); } | ||
} | ||
|
||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); | ||
|
||
if (!TryFindFirstAncestorOrSelf(root, context.Span, out FileScopedNamespaceDeclarationSyntax fileScopedNamespace)) | ||
return; | ||
|
||
Document document = context.Document; | ||
Diagnostic diagnostic = context.Diagnostics[0]; | ||
MemberDeclarationSyntax member = fileScopedNamespace.Members[0]; | ||
BlankLineStyle style = BlankLineAfterFileScopedNamespaceDeclarationAnalyzer.GetCurrentStyle(fileScopedNamespace, member); | ||
|
||
if (style == BlankLineStyle.Add) | ||
{ | ||
CodeAction codeAction = CodeAction.Create( | ||
CodeFixTitles.AddBlankLine, | ||
ct => | ||
{ | ||
MemberDeclarationSyntax newMember; | ||
if (!fileScopedNamespace.SemicolonToken.TrailingTrivia.Contains(SyntaxKind.EndOfLineTrivia)) | ||
{ | ||
newMember = member.PrependToLeadingTrivia(new SyntaxTrivia[] { CSharpFactory.NewLine(), CSharpFactory.NewLine() }); | ||
} | ||
else | ||
{ | ||
newMember = member.PrependEndOfLineToLeadingTrivia(); | ||
} | ||
return document.ReplaceNodeAsync(member, newMember, ct); | ||
}, | ||
GetEquivalenceKey(diagnostic)); | ||
|
||
context.RegisterCodeFix(codeAction, diagnostic); | ||
} | ||
else if (style == BlankLineStyle.Remove) | ||
{ | ||
CodeAction codeAction = CodeAction.Create( | ||
CodeFixTitles.RemoveBlankLine, | ||
ct => CodeFixHelpers.RemoveBlankLinesBeforeAsync(document, member.GetFirstToken(), ct), | ||
GetEquivalenceKey(diagnostic)); | ||
|
||
context.RegisterCodeFix(codeAction, diagnostic); | ||
} | ||
else | ||
{ | ||
Debug.Fail(""); | ||
} | ||
} | ||
} |
135 changes: 135 additions & 0 deletions
135
src/Formatting.Analyzers/CSharp/BlankLineAfterFileScopedNamespaceDeclarationAnalyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Text; | ||
using Roslynator.CSharp; | ||
using Roslynator.CSharp.CodeStyle; | ||
|
||
namespace Roslynator.Formatting.CSharp; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public sealed class BlankLineAfterFileScopedNamespaceDeclarationAnalyzer : BaseDiagnosticAnalyzer | ||
{ | ||
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics; | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics | ||
{ | ||
get | ||
{ | ||
if (_supportedDiagnostics.IsDefault) | ||
Immutable.InterlockedInitialize(ref _supportedDiagnostics, DiagnosticRules.BlankLineAfterFileScopedNamespaceDeclaration); | ||
|
||
return _supportedDiagnostics; | ||
} | ||
} | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
base.Initialize(context); | ||
|
||
context.RegisterSyntaxNodeAction(f => AnalyzeFileScopedNamespaceDeclaration(f), SyntaxKind.FileScopedNamespaceDeclaration); | ||
} | ||
|
||
private static void AnalyzeFileScopedNamespaceDeclaration(SyntaxNodeAnalysisContext context) | ||
{ | ||
var namespaceDeclaration = (FileScopedNamespaceDeclarationSyntax)context.Node; | ||
|
||
MemberDeclarationSyntax memberDeclaration = namespaceDeclaration.Members.FirstOrDefault(); | ||
|
||
if (memberDeclaration is null) | ||
return; | ||
|
||
BlankLineStyle style = context.GetBlankLineAfterFileScopedNamespaceDeclaration(); | ||
|
||
if (style == BlankLineStyle.None) | ||
return; | ||
|
||
BlankLineStyle currentStyle = GetCurrentStyle(namespaceDeclaration, memberDeclaration); | ||
|
||
if (style != currentStyle) | ||
return; | ||
|
||
context.ReportDiagnostic( | ||
DiagnosticRules.BlankLineAfterFileScopedNamespaceDeclaration, | ||
Location.Create(namespaceDeclaration.SyntaxTree, new TextSpan(memberDeclaration.FullSpan.Start, 0)), | ||
(style == BlankLineStyle.Add) ? "Add" : "Remove"); | ||
} | ||
|
||
internal static BlankLineStyle GetCurrentStyle( | ||
FileScopedNamespaceDeclarationSyntax namespaceDeclaration, | ||
MemberDeclarationSyntax memberDeclaration) | ||
{ | ||
(bool add, bool remove) = AnalyzeTrailingTrivia(); | ||
|
||
if (add || remove) | ||
{ | ||
BlankLineStyle style = AnalyzeLeadingTrivia(); | ||
|
||
if (style == BlankLineStyle.Add) | ||
{ | ||
if (add) | ||
return style; | ||
} | ||
else if (style == BlankLineStyle.Remove) | ||
{ | ||
if (remove) | ||
return style; | ||
} | ||
} | ||
|
||
return BlankLineStyle.None; | ||
|
||
(bool add, bool remove) AnalyzeTrailingTrivia() | ||
{ | ||
SyntaxTriviaList.Enumerator en = namespaceDeclaration.SemicolonToken.TrailingTrivia.GetEnumerator(); | ||
|
||
if (!en.MoveNext()) | ||
return (true, false); | ||
|
||
if (en.Current.IsWhitespaceTrivia() | ||
&& !en.MoveNext()) | ||
{ | ||
return (true, false); | ||
} | ||
|
||
if (en.Current.IsKind(SyntaxKind.SingleLineCommentTrivia) | ||
&& !en.MoveNext()) | ||
{ | ||
return (true, false); | ||
} | ||
|
||
return (en.Current.IsEndOfLineTrivia()) | ||
? (true, true) | ||
: (false, false); | ||
} | ||
|
||
BlankLineStyle AnalyzeLeadingTrivia() | ||
{ | ||
SyntaxTriviaList.Enumerator en = memberDeclaration.GetLeadingTrivia().GetEnumerator(); | ||
|
||
if (!en.MoveNext()) | ||
return BlankLineStyle.Add; | ||
|
||
if (en.Current.IsWhitespaceTrivia() | ||
&& !en.MoveNext()) | ||
{ | ||
return BlankLineStyle.Add; | ||
} | ||
|
||
switch (en.Current.Kind()) | ||
{ | ||
case SyntaxKind.SingleLineCommentTrivia: | ||
case SyntaxKind.SingleLineDocumentationCommentTrivia: | ||
return BlankLineStyle.Add; | ||
case SyntaxKind.EndOfLineTrivia: | ||
return BlankLineStyle.Remove; | ||
} | ||
|
||
return BlankLineStyle.None; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.