diff --git a/.editorconfig b/.editorconfig index 69718aea09..034defa6af 100644 --- a/.editorconfig +++ b/.editorconfig @@ -35,6 +35,7 @@ roslynator_use_var_instead_of_implicit_object_creation = true roslynator_infinite_loop_style = while roslynator_doc_comment_summary_style = multi_line roslynator_enum_flag_value_style = shift_operator +roslynator_blank_line_after_file_scoped_namespace_declaration = true csharp_style_namespace_declarations = file_scoped:suggestion @@ -77,6 +78,7 @@ dotnet_diagnostic.RCS0055.severity = suggestion dotnet_diagnostic.RCS0057.severity = suggestion dotnet_diagnostic.RCS0058.severity = suggestion dotnet_diagnostic.RCS0059.severity = suggestion +dotnet_diagnostic.RCS0060.severity = suggestion dotnet_diagnostic.RCS1002.severity = silent dotnet_diagnostic.RCS1006.severity = suggestion dotnet_diagnostic.RCS1008.severity = suggestion diff --git a/ChangeLog.md b/ChangeLog.md index f469b88c58..3fd2e4d0fb 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add Arm64 VS 2022 extension support ([#990](https://github.com/JosefPihrt/Roslynator/pull/990) by @snickler). +- Add analyzer "Add/remove blank line after file scoped namespace declaration" [RCS0060](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS0060.md) ([#993](https://github.com/josefpihrt/roslynator/pull/993). + - Required option: `roslynator_blank_line_after_file_scoped_namespace_declaration = true|false` ### Changed diff --git a/src/Common/CSharp/Extensions/CodeStyleExtensions.cs b/src/Common/CSharp/Extensions/CodeStyleExtensions.cs index 00b07a66da..1cbf76a43f 100644 --- a/src/Common/CSharp/Extensions/CodeStyleExtensions.cs +++ b/src/Common/CSharp/Extensions/CodeStyleExtensions.cs @@ -495,6 +495,16 @@ public static BodyStyle GetBodyStyle(this SyntaxNodeAnalysisContext context) return null; } + public static BlankLineStyle GetBlankLineAfterFileScopedNamespaceDeclaration(this SyntaxNodeAnalysisContext context) + { + if (ConfigOptions.TryGetValueAsBool(context.GetConfigOptions(), ConfigOptions.BlankLineAfterFileScopedNamespaceDeclaration, out bool value)) + { + return (value) ? BlankLineStyle.Add : BlankLineStyle.Remove; + } + + return BlankLineStyle.None; + } + public static bool? GetSuppressUnityScriptMethods(this SyntaxNodeAnalysisContext context) { if (ConfigOptions.TryGetValueAsBool(context.GetConfigOptions(), ConfigOptions.SuppressUnityScriptMethods, out bool value)) diff --git a/src/Common/ConfigOptionKeys.Generated.cs b/src/Common/ConfigOptionKeys.Generated.cs index 88def25b2f..18b43bc31c 100644 --- a/src/Common/ConfigOptionKeys.Generated.cs +++ b/src/Common/ConfigOptionKeys.Generated.cs @@ -11,6 +11,7 @@ internal static partial class ConfigOptionKeys public const string ArrayCreationTypeStyle = "roslynator_array_creation_type_style"; public const string ArrowTokenNewLine = "roslynator_arrow_token_new_line"; public const string BinaryOperatorNewLine = "roslynator_binary_operator_new_line"; + public const string BlankLineAfterFileScopedNamespaceDeclaration = "roslynator_blank_line_after_file_scoped_namespace_declaration"; public const string BlankLineBetweenClosingBraceAndSwitchSection = "roslynator_blank_line_between_closing_brace_and_switch_section"; public const string BlankLineBetweenSingleLineAccessors = "roslynator_blank_line_between_single_line_accessors"; public const string BlankLineBetweenUsingDirectives = "roslynator_blank_line_between_using_directives"; diff --git a/src/Common/ConfigOptions.Generated.cs b/src/Common/ConfigOptions.Generated.cs index 4e8be78351..755d624575 100644 --- a/src/Common/ConfigOptions.Generated.cs +++ b/src/Common/ConfigOptions.Generated.cs @@ -38,6 +38,12 @@ public static partial class ConfigOptions defaultValuePlaceholder: "after|before", description: "Place new line after/before binary operator"); + public static readonly ConfigOptionDescriptor BlankLineAfterFileScopedNamespaceDeclaration = new( + key: ConfigOptionKeys.BlankLineAfterFileScopedNamespaceDeclaration, + defaultValue: null, + defaultValuePlaceholder: "true|false", + description: "Add/remove blank line after file scoped namespace declaration"); + public static readonly ConfigOptionDescriptor BlankLineBetweenClosingBraceAndSwitchSection = new( key: ConfigOptionKeys.BlankLineBetweenClosingBraceAndSwitchSection, defaultValue: null, @@ -213,6 +219,7 @@ public static partial class ConfigOptions yield return new KeyValuePair("RCS0052", JoinOptionKeys(ConfigOptionKeys.EqualsTokenNewLine)); yield return new KeyValuePair("RCS0058", JoinOptionKeys(ConfigOptionKeys.NewLineAtEndOfFile)); yield return new KeyValuePair("RCS0059", JoinOptionKeys(ConfigOptionKeys.NullConditionalOperatorNewLine)); + yield return new KeyValuePair("RCS0060", JoinOptionKeys(ConfigOptionKeys.BlankLineAfterFileScopedNamespaceDeclaration)); yield return new KeyValuePair("RCS1014", JoinOptionKeys(ConfigOptionKeys.ArrayCreationTypeStyle)); yield return new KeyValuePair("RCS1016", JoinOptionKeys(ConfigOptionKeys.BodyStyle, ConfigOptionKeys.UseBlockBodyWhenDeclarationSpansOverMultipleLines, ConfigOptionKeys.UseBlockBodyWhenExpressionSpansOverMultipleLines)); yield return new KeyValuePair("RCS1018", JoinOptionKeys(ConfigOptionKeys.AccessibilityModifiers)); diff --git a/src/Common/ConfigOptions.xml b/src/Common/ConfigOptions.xml index 303a6fa295..e571fe5d67 100644 --- a/src/Common/ConfigOptions.xml +++ b/src/Common/ConfigOptions.xml @@ -195,6 +195,10 @@ Use 'for'/'while' statement as an infinite loop +