From ceddae9ef4aba2c77bbc7ac123a0e42d4c70a043 Mon Sep 17 00:00:00 2001 From: David Boike Date: Wed, 1 Apr 2026 08:26:52 -0500 Subject: [PATCH 1/2] Allow .editorconfig nservicebus_handler_style = IHandleMessages|Conventions to determine which fixers are available --- .../AddConventionBasedHandleMethodFixer.cs | 21 ++++++++++++------- .../AddIHandleMessagesInterfaceFixer.cs | 19 +++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddConventionBasedHandleMethodFixer.cs b/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddConventionBasedHandleMethodFixer.cs index e2933bc6c9..feb351475e 100644 --- a/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddConventionBasedHandleMethodFixer.cs +++ b/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddConventionBasedHandleMethodFixer.cs @@ -1,9 +1,11 @@ namespace NServiceBus.Core.Analyzer.Fixes; +using System; using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; +using Handlers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; @@ -12,7 +14,6 @@ namespace NServiceBus.Core.Analyzer.Fixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; -using NServiceBus.Core.Analyzer.Handlers; [Shared] [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AddConventionBasedHandleMethodFixer))] @@ -25,14 +26,20 @@ public class AddConventionBasedHandleMethodFixer : CodeFixProvider public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root is null) + { + return; + } - foreach (var diagnostic in context.Diagnostics) + var analyzerOptions = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(root.SyntaxTree); + if (analyzerOptions.TryGetValue("nservicebus_handler_style", out var handlerStyle) && string.Equals(handlerStyle, "IHandleMessages", StringComparison.OrdinalIgnoreCase)) { - if (root?.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) is not { } node) - { - continue; - } + return; + } + foreach (var diagnostic in context.Diagnostics) + { + var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); var classDecl = node.FirstAncestorOrSelf(); if (classDecl is null || !HandlerFixerGuards.IsEmptyHandlerShell(classDecl)) { @@ -43,7 +50,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) CodeAction.Create( "Add convention-based Handle(MyMessage, ...)", token => AddHandleMethod(context.Document, classDecl.SpanStart, token), - EquivalenceKey), + EquivalenceKey + handlerStyle), diagnostic); } } diff --git a/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddIHandleMessagesInterfaceFixer.cs b/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddIHandleMessagesInterfaceFixer.cs index 1783ff85a8..212e52aa9f 100644 --- a/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddIHandleMessagesInterfaceFixer.cs +++ b/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddIHandleMessagesInterfaceFixer.cs @@ -1,5 +1,6 @@ namespace NServiceBus.Core.Analyzer.Fixes; +using System; using System.Collections.Immutable; using System.Composition; using System.Threading; @@ -25,14 +26,20 @@ public class AddIHandleMessagesInterfaceFixer : CodeFixProvider public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root is null) + { + return; + } - foreach (var diagnostic in context.Diagnostics) + var analyzerOptions = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(root.SyntaxTree); + if (analyzerOptions.TryGetValue("nservicebus_handler_style", out var handlerStyle) && string.Equals(handlerStyle, "Conventions", StringComparison.OrdinalIgnoreCase)) { - if (root?.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) is not { } node) - { - continue; - } + return; + } + foreach (var diagnostic in context.Diagnostics) + { + var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); var classDecl = node.FirstAncestorOrSelf(); if (classDecl is null || !HandlerFixerGuards.IsEmptyHandlerShell(classDecl)) { @@ -43,7 +50,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) CodeAction.Create( "Implement IHandleMessages", token => AddInterface(context.Document, classDecl.SpanStart, token), - EquivalenceKey), + EquivalenceKey + handlerStyle), diagnostic); } } From 4e990e3d4290d85872896f2c1ad62acc28f4a197 Mon Sep 17 00:00:00 2001 From: David Boike Date: Wed, 1 Apr 2026 12:55:53 -0500 Subject: [PATCH 2/2] Change values and encapsulate --- .../AddConventionBasedHandleMethodFixer.cs | 5 ++- .../AddIHandleMessagesInterfaceFixer.cs | 5 ++- .../NServiceBus.Core.Analyzer.Fixes.csproj | 1 + .../EditorConfigSettings.cs | 36 +++++++++++++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 src/NServiceBus.Core.Analyzer/EditorConfigSettings.cs diff --git a/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddConventionBasedHandleMethodFixer.cs b/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddConventionBasedHandleMethodFixer.cs index feb351475e..a3160b6a7e 100644 --- a/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddConventionBasedHandleMethodFixer.cs +++ b/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddConventionBasedHandleMethodFixer.cs @@ -31,8 +31,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) return; } - var analyzerOptions = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(root.SyntaxTree); - if (analyzerOptions.TryGetValue("nservicebus_handler_style", out var handlerStyle) && string.Equals(handlerStyle, "IHandleMessages", StringComparison.OrdinalIgnoreCase)) + if (EditorConfigSettings.KeyMatches(context.Document.Project.AnalyzerOptions, root.SyntaxTree, EditorConfigSettings.HandlerStyleKey, EditorConfigSettings.HandlerStyleInterfaces)) { return; } @@ -50,7 +49,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) CodeAction.Create( "Add convention-based Handle(MyMessage, ...)", token => AddHandleMethod(context.Document, classDecl.SpanStart, token), - EquivalenceKey + handlerStyle), + EquivalenceKey), diagnostic); } } diff --git a/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddIHandleMessagesInterfaceFixer.cs b/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddIHandleMessagesInterfaceFixer.cs index 212e52aa9f..7ec1bec6c5 100644 --- a/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddIHandleMessagesInterfaceFixer.cs +++ b/src/NServiceBus.Core.Analyzer.Fixes/Handlers/AddIHandleMessagesInterfaceFixer.cs @@ -31,8 +31,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) return; } - var analyzerOptions = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(root.SyntaxTree); - if (analyzerOptions.TryGetValue("nservicebus_handler_style", out var handlerStyle) && string.Equals(handlerStyle, "Conventions", StringComparison.OrdinalIgnoreCase)) + if (EditorConfigSettings.KeyMatches(context.Document.Project.AnalyzerOptions, root.SyntaxTree, EditorConfigSettings.HandlerStyleKey, EditorConfigSettings.HandlerStyleConventions)) { return; } @@ -50,7 +49,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) CodeAction.Create( "Implement IHandleMessages", token => AddInterface(context.Document, classDecl.SpanStart, token), - EquivalenceKey + handlerStyle), + EquivalenceKey), diagnostic); } } diff --git a/src/NServiceBus.Core.Analyzer.Fixes/NServiceBus.Core.Analyzer.Fixes.csproj b/src/NServiceBus.Core.Analyzer.Fixes/NServiceBus.Core.Analyzer.Fixes.csproj index 3523523564..10b086fb14 100644 --- a/src/NServiceBus.Core.Analyzer.Fixes/NServiceBus.Core.Analyzer.Fixes.csproj +++ b/src/NServiceBus.Core.Analyzer.Fixes/NServiceBus.Core.Analyzer.Fixes.csproj @@ -20,6 +20,7 @@ + diff --git a/src/NServiceBus.Core.Analyzer/EditorConfigSettings.cs b/src/NServiceBus.Core.Analyzer/EditorConfigSettings.cs new file mode 100644 index 0000000000..947cd0675b --- /dev/null +++ b/src/NServiceBus.Core.Analyzer/EditorConfigSettings.cs @@ -0,0 +1,36 @@ +#nullable enable + +namespace NServiceBus.Core.Analyzer; + +using System; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +static class EditorConfigSettings +{ + /// + /// Get an editorconfig setting in an Analyzer. The AnalyzerOptions should be accessible from an Analyzer through context.Options + /// or in a CodeFixProvider from context.Document.Project.AnalyzerOptions. + /// + /// + public static bool TryGetValue(AnalyzerOptions analyzerOptions, SyntaxTree? syntaxTree, string settingName, [NotNullWhen(true)] out string? value) + { + var provider = analyzerOptions.AnalyzerConfigOptionsProvider; + + if (syntaxTree is null) + { + return provider.GlobalOptions.TryGetValue(settingName, out value); + } + + var options = analyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree); + return options.TryGetValue(settingName, out value); + } + + public static bool KeyMatches(AnalyzerOptions analyzerOptions, SyntaxTree? syntaxTree, string settingName, string matchValue, StringComparison comparison = StringComparison.OrdinalIgnoreCase) + => TryGetValue(analyzerOptions, syntaxTree, settingName, out var value) && string.Equals(value, matchValue, comparison); + + public const string HandlerStyleKey = "nservicebus_handler_style"; + public const string HandlerStyleInterfaces = "interfaces"; + public const string HandlerStyleConventions = "conventions"; +} \ No newline at end of file