From dc2162337fd7d2c6d54c828859bd599615acfcb7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 20:50:55 +0000 Subject: [PATCH 01/15] Initial plan From f5b0c53e68212dcbff138eca2e102887f8a98818 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:03:08 +0000 Subject: [PATCH 02/15] Implement csharp_style_namespace_declarations EditorConfig support - Add CSharpCodeGenerationConfiguration class with namespace declaration style setting - Modify DefineStepsCommand to read EditorConfig and generate file-scoped namespaces when configured - Add csharp_style_namespace_declarations setting to .editorconfig - Create unit tests for the new configuration class Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .editorconfig | 1 + .../CSharpCodeGenerationConfiguration.cs | 18 ++++ .../Editor/Commands/DefineStepsCommand.cs | 36 +++++++- .../CSharpCodeGenerationConfigurationTests.cs | 82 +++++++++++++++++++ 4 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs create mode 100644 Tests/Reqnroll.VisualStudio.Tests/Configuration/CSharpCodeGenerationConfigurationTests.cs diff --git a/.editorconfig b/.editorconfig index c785b717..64d0c0c2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,6 +20,7 @@ tab_width=2 csharp_new_line_before_members_in_object_initializers=false csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion csharp_space_after_cast=false +csharp_style_namespace_declarations=file_scoped:warning csharp_style_var_elsewhere=true:hint csharp_style_var_for_built_in_types=true:hint csharp_style_var_when_type_is_apparent=true:hint diff --git a/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs b/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs new file mode 100644 index 00000000..ef6f7e3c --- /dev/null +++ b/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs @@ -0,0 +1,18 @@ +namespace Reqnroll.VisualStudio.Configuration; + +public class CSharpCodeGenerationConfiguration +{ + /// + /// Specifies the namespace declaration style for generated C# code. + /// Uses file-scoped namespaces when set to "file_scoped", otherwise uses block-scoped namespaces. + /// + [EditorConfigSetting("csharp_style_namespace_declarations")] + public string NamespaceDeclarationStyle { get; set; } = "block_scoped"; + + /// + /// Determines if file-scoped namespaces should be used based on the EditorConfig setting. + /// + public bool UseFileScopedNamespaces => + NamespaceDeclarationStyle != null && + NamespaceDeclarationStyle.StartsWith("file_scoped", StringComparison.OrdinalIgnoreCase); +} \ No newline at end of file diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index 507d804f..b8c7bc0f 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -4,13 +4,17 @@ namespace Reqnroll.VisualStudio.Editor.Commands; [Export(typeof(IDeveroomFeatureEditorCommand))] public class DefineStepsCommand : DeveroomEditorCommandBase, IDeveroomFeatureEditorCommand { + private readonly IEditorConfigOptionsProvider _editorConfigOptionsProvider; + [ImportingConstructor] public DefineStepsCommand( IIdeScope ideScope, IBufferTagAggregatorFactoryService aggregatorFactory, - IDeveroomTaggerProvider taggerProvider) + IDeveroomTaggerProvider taggerProvider, + IEditorConfigOptionsProvider editorConfigOptionsProvider) : base(ideScope, aggregatorFactory, taggerProvider) { + _editorConfigOptionsProvider = editorConfigOptionsProvider; } public override DeveroomEditorCommandTargetKey[] Targets => new[] @@ -101,7 +105,7 @@ public override bool PreExec(IWpfTextView textView, DeveroomEditorCommandTargetK switch (viewModel.Result) { case CreateStepDefinitionsDialogResult.Create: - SaveAsStepDefinitionClass(projectScope, combinedSnippet, viewModel.ClassName, indent, newLine); + SaveAsStepDefinitionClass(projectScope, combinedSnippet, viewModel.ClassName, indent, newLine, textView); break; case CreateStepDefinitionsDialogResult.CopyToClipboard: Logger.LogVerbose($"Copy to clipboard: {combinedSnippet}"); @@ -114,7 +118,7 @@ public override bool PreExec(IWpfTextView textView, DeveroomEditorCommandTargetK } private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combinedSnippet, string className, - string indent, string newLine) + string indent, string newLine, IWpfTextView textView) { string targetFolder = projectScope.ProjectFolder; var projectSettings = projectScope.GetProjectSettings(); @@ -130,7 +134,30 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin var isSpecFlow = projectTraits.HasFlag(ReqnrollProjectTraits.LegacySpecFlow) || projectTraits.HasFlag(ReqnrollProjectTraits.SpecFlowCompatibility); var libraryNameSpace = isSpecFlow ? "SpecFlow" : "Reqnroll"; - var template = "using System;" + newLine + + // Get C# code generation configuration from EditorConfig + var csharpConfig = new CSharpCodeGenerationConfiguration(); + var editorConfigOptions = _editorConfigOptionsProvider.GetEditorConfigOptions(textView); + editorConfigOptions.UpdateFromEditorConfig(csharpConfig); + + string template; + if (csharpConfig.UseFileScopedNamespaces) + { + // Generate file-scoped namespace + template = "using System;" + newLine + + $"using {libraryNameSpace};" + newLine + + newLine + + $"namespace {fileNamespace};" + newLine + + newLine + + "[Binding]" + newLine + + $"public class {className}" + newLine + + "{" + newLine + + combinedSnippet + + "}" + newLine; + } + else + { + // Generate block-scoped namespace (existing behavior) + template = "using System;" + newLine + $"using {libraryNameSpace};" + newLine + newLine + $"namespace {fileNamespace}" + newLine + @@ -141,6 +168,7 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin combinedSnippet + $"{indent}}}" + newLine + "}" + newLine; + } var targetFile = FileDetails .FromPath(targetFolder, className + ".cs") diff --git a/Tests/Reqnroll.VisualStudio.Tests/Configuration/CSharpCodeGenerationConfigurationTests.cs b/Tests/Reqnroll.VisualStudio.Tests/Configuration/CSharpCodeGenerationConfigurationTests.cs new file mode 100644 index 00000000..b4994d98 --- /dev/null +++ b/Tests/Reqnroll.VisualStudio.Tests/Configuration/CSharpCodeGenerationConfigurationTests.cs @@ -0,0 +1,82 @@ +using Reqnroll.VisualStudio.Configuration; +using Xunit; + +namespace Reqnroll.VisualStudio.Tests.Configuration; + +public class CSharpCodeGenerationConfigurationTests +{ + [Fact] + public void UseFileScopedNamespaces_WhenFileScopedSet_ReturnsTrue() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration + { + NamespaceDeclarationStyle = "file_scoped" + }; + + // Act & Assert + Assert.True(config.UseFileScopedNamespaces); + } + + [Fact] + public void UseFileScopedNamespaces_WhenFileScopedWithSeveritySet_ReturnsTrue() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration + { + NamespaceDeclarationStyle = "file_scoped:warning" + }; + + // Act & Assert + Assert.True(config.UseFileScopedNamespaces); + } + + [Fact] + public void UseFileScopedNamespaces_WhenBlockScopedSet_ReturnsFalse() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration + { + NamespaceDeclarationStyle = "block_scoped" + }; + + // Act & Assert + Assert.False(config.UseFileScopedNamespaces); + } + + [Fact] + public void UseFileScopedNamespaces_WhenDefaultValue_ReturnsFalse() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration(); + + // Act & Assert + Assert.False(config.UseFileScopedNamespaces); + } + + [Fact] + public void UseFileScopedNamespaces_WhenNullValue_ReturnsFalse() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration + { + NamespaceDeclarationStyle = null + }; + + // Act & Assert + Assert.False(config.UseFileScopedNamespaces); + } + + [Fact] + public void UseFileScopedNamespaces_WhenUnknownValue_ReturnsFalse() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration + { + NamespaceDeclarationStyle = "unknown_style" + }; + + // Act & Assert + Assert.False(config.UseFileScopedNamespaces); + } +} \ No newline at end of file From 61bebbcb2d2e9171568e5014ec4b66bf9dd5f865 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:17:06 +0000 Subject: [PATCH 03/15] Fix missing using statements in EditorConfig implementation Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../Configuration/CSharpCodeGenerationConfiguration.cs | 2 ++ Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs b/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs index ef6f7e3c..4935d5be 100644 --- a/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs +++ b/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs @@ -1,3 +1,5 @@ +using Reqnroll.VisualStudio.Editor.Services.EditorConfig; + namespace Reqnroll.VisualStudio.Configuration; public class CSharpCodeGenerationConfiguration diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index b8c7bc0f..d77ecd3c 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -1,4 +1,6 @@ #nullable disable +using Reqnroll.VisualStudio.Configuration; + namespace Reqnroll.VisualStudio.Editor.Commands; [Export(typeof(IDeveroomFeatureEditorCommand))] From 1004f481665c25ccee573b52813434e61f0ae18d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:00:09 +0000 Subject: [PATCH 04/15] Remove redundant using statements to fix compilation issues Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../Configuration/CSharpCodeGenerationConfiguration.cs | 2 -- Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs b/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs index 4935d5be..ef6f7e3c 100644 --- a/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs +++ b/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs @@ -1,5 +1,3 @@ -using Reqnroll.VisualStudio.Editor.Services.EditorConfig; - namespace Reqnroll.VisualStudio.Configuration; public class CSharpCodeGenerationConfiguration diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index d77ecd3c..fefac866 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -1,5 +1,4 @@ #nullable disable -using Reqnroll.VisualStudio.Configuration; namespace Reqnroll.VisualStudio.Editor.Commands; From ee827843d2d8e9a3df393fd7ea131868e8f96d59 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:45:37 +0000 Subject: [PATCH 05/15] Minor formatting adjustment to trigger CI build verification Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../Configuration/CSharpCodeGenerationConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs b/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs index ef6f7e3c..8398b493 100644 --- a/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs +++ b/Reqnroll.VisualStudio/Configuration/CSharpCodeGenerationConfiguration.cs @@ -12,7 +12,7 @@ public class CSharpCodeGenerationConfiguration /// /// Determines if file-scoped namespaces should be used based on the EditorConfig setting. /// - public bool UseFileScopedNamespaces => - NamespaceDeclarationStyle != null && + public bool UseFileScopedNamespaces => + NamespaceDeclarationStyle != null && NamespaceDeclarationStyle.StartsWith("file_scoped", StringComparison.OrdinalIgnoreCase); } \ No newline at end of file From ad7fb00254fba34df3763608b8077a5f83513bf8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 Aug 2025 21:10:14 +0000 Subject: [PATCH 06/15] Fix DefineStepsCommand constructor calls in tests to include missing EditorConfigOptionsProvider parameter Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../StepDefinitions/ProjectSystemSteps.cs | 3 ++- .../Editor/Commands/DefineStepsCommandTests.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/Reqnroll.VisualStudio.Specs/StepDefinitions/ProjectSystemSteps.cs b/Tests/Reqnroll.VisualStudio.Specs/StepDefinitions/ProjectSystemSteps.cs index 17bc2a84..219e3d08 100644 --- a/Tests/Reqnroll.VisualStudio.Specs/StepDefinitions/ProjectSystemSteps.cs +++ b/Tests/Reqnroll.VisualStudio.Specs/StepDefinitions/ProjectSystemSteps.cs @@ -478,7 +478,8 @@ private void PerformCommand(string commandName, string parameter = null, } case "Define Steps": { - _invokedCommand = new DefineStepsCommand(_ideScope, aggregatorFactoryService, taggerProvider); + _invokedCommand = new DefineStepsCommand(_ideScope, aggregatorFactoryService, taggerProvider, + new StubEditorConfigOptionsProvider()); _invokedCommand.PreExec(_wpfTextView, _invokedCommand.Targets.First()); return; } diff --git a/Tests/Reqnroll.VisualStudio.Tests/Editor/Commands/DefineStepsCommandTests.cs b/Tests/Reqnroll.VisualStudio.Tests/Editor/Commands/DefineStepsCommandTests.cs index fba8031c..95d2ec84 100644 --- a/Tests/Reqnroll.VisualStudio.Tests/Editor/Commands/DefineStepsCommandTests.cs +++ b/Tests/Reqnroll.VisualStudio.Tests/Editor/Commands/DefineStepsCommandTests.cs @@ -6,7 +6,8 @@ public class DefineStepsCommandTests : CommandTestBase { public DefineStepsCommandTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper, (ps, tp) => - new DefineStepsCommand(ps.IdeScope, new StubBufferTagAggregatorFactoryService(tp), tp), + new DefineStepsCommand(ps.IdeScope, new StubBufferTagAggregatorFactoryService(tp), tp, + new StubEditorConfigOptionsProvider()), "ShowProblem: User Notification: ") { } From 655c69b0939cc2e46c54ce5c6618b3dffd0c434f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 Aug 2025 21:48:07 +0000 Subject: [PATCH 07/15] Deduplicate template generation code in DefineStepsCommand Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../Editor/Commands/DefineStepsCommand.cs | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index fefac866..ded1d452 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -140,35 +140,33 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin var editorConfigOptions = _editorConfigOptionsProvider.GetEditorConfigOptions(textView); editorConfigOptions.UpdateFromEditorConfig(csharpConfig); - string template; + // Build template with common structure + var template = "using System;" + newLine + + $"using {libraryNameSpace};" + newLine + + newLine; + if (csharpConfig.UseFileScopedNamespaces) { // Generate file-scoped namespace - template = "using System;" + newLine + - $"using {libraryNameSpace};" + newLine + - newLine + - $"namespace {fileNamespace};" + newLine + - newLine + - "[Binding]" + newLine + - $"public class {className}" + newLine + - "{" + newLine + - combinedSnippet + - "}" + newLine; + template += $"namespace {fileNamespace};" + newLine + + newLine + + "[Binding]" + newLine + + $"public class {className}" + newLine + + "{" + newLine + + combinedSnippet + + "}" + newLine; } else { // Generate block-scoped namespace (existing behavior) - template = "using System;" + newLine + - $"using {libraryNameSpace};" + newLine + - newLine + - $"namespace {fileNamespace}" + newLine + - "{" + newLine + - $"{indent}[Binding]" + newLine + - $"{indent}public class {className}" + newLine + - $"{indent}{{" + newLine + - combinedSnippet + - $"{indent}}}" + newLine + - "}" + newLine; + template += $"namespace {fileNamespace}" + newLine + + "{" + newLine + + $"{indent}[Binding]" + newLine + + $"{indent}public class {className}" + newLine + + $"{indent}{{" + newLine + + combinedSnippet + + $"{indent}}}" + newLine + + "}" + newLine; } var targetFile = FileDetails From 3ac3a40b0af5199e15173d0b70c66087bbcd1d59 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 Aug 2025 22:35:13 +0000 Subject: [PATCH 08/15] Fix BufferGraphFactoryService GuardedOperations parameter passing in test infrastructure Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs b/Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs index 916eb61d..e2871d9f 100644 --- a/Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs +++ b/Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs @@ -79,8 +79,8 @@ public static void Initialize() CreateObject( "Microsoft.VisualStudio.Text.Utilities.GuardedOperations, Microsoft.VisualStudio.Platform.VSEditor"); BufferGraphFactoryService = CreateObject( - "Microsoft.VisualStudio.Text.Projection.Implementation.BufferGraphFactoryService, Microsoft.VisualStudio.Platform.VSEditor"); - SetField(BufferGraphFactoryService, nameof(GuardedOperations), GuardedOperations); + "Microsoft.VisualStudio.Text.Projection.Implementation.BufferGraphFactoryService, Microsoft.VisualStudio.Platform.VSEditor", + GuardedOperations); BufferFactoryService = CreateObject( "Microsoft.VisualStudio.Text.Implementation.BufferFactoryService, Microsoft.VisualStudio.Platform.VSEditor"); From 8d61bc899ae498de21915137f5f648dfbcaeca49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 Aug 2025 22:52:36 +0000 Subject: [PATCH 09/15] Fix BufferGraphFactoryService constructor issue by reverting to SetField approach Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs b/Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs index e2871d9f..916eb61d 100644 --- a/Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs +++ b/Tests/Reqnroll.VisualStudio.VsxStubs/VsxStubObjects.cs @@ -79,8 +79,8 @@ public static void Initialize() CreateObject( "Microsoft.VisualStudio.Text.Utilities.GuardedOperations, Microsoft.VisualStudio.Platform.VSEditor"); BufferGraphFactoryService = CreateObject( - "Microsoft.VisualStudio.Text.Projection.Implementation.BufferGraphFactoryService, Microsoft.VisualStudio.Platform.VSEditor", - GuardedOperations); + "Microsoft.VisualStudio.Text.Projection.Implementation.BufferGraphFactoryService, Microsoft.VisualStudio.Platform.VSEditor"); + SetField(BufferGraphFactoryService, nameof(GuardedOperations), GuardedOperations); BufferFactoryService = CreateObject( "Microsoft.VisualStudio.Text.Implementation.BufferFactoryService, Microsoft.VisualStudio.Platform.VSEditor"); From d4010d2d4af3a28768e13db19de063204a03e02e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:09:47 +0000 Subject: [PATCH 10/15] Deduplicate template generation using StringBuilder to eliminate code duplication in DefineStepsCommand Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../Editor/Commands/DefineStepsCommand.cs | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index ded1d452..d4e086ac 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -1,5 +1,7 @@ #nullable disable +using System.Text; + namespace Reqnroll.VisualStudio.Editor.Commands; [Export(typeof(IDeveroomFeatureEditorCommand))] @@ -141,37 +143,42 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin editorConfigOptions.UpdateFromEditorConfig(csharpConfig); // Build template with common structure - var template = "using System;" + newLine + - $"using {libraryNameSpace};" + newLine + - newLine; - + var template = new StringBuilder(); + template.AppendLine("using System;"); + template.AppendLine($"using {libraryNameSpace};"); + template.AppendLine(); + + // Determine indentation level based on namespace style + var classIndent = csharpConfig.UseFileScopedNamespaces ? "" : indent; + + // Add namespace declaration if (csharpConfig.UseFileScopedNamespaces) { - // Generate file-scoped namespace - template += $"namespace {fileNamespace};" + newLine + - newLine + - "[Binding]" + newLine + - $"public class {className}" + newLine + - "{" + newLine + - combinedSnippet + - "}" + newLine; + template.AppendLine($"namespace {fileNamespace};"); + template.AppendLine(); } else { - // Generate block-scoped namespace (existing behavior) - template += $"namespace {fileNamespace}" + newLine + - "{" + newLine + - $"{indent}[Binding]" + newLine + - $"{indent}public class {className}" + newLine + - $"{indent}{{" + newLine + - combinedSnippet + - $"{indent}}}" + newLine + - "}" + newLine; + template.AppendLine($"namespace {fileNamespace}"); + template.AppendLine("{"); + } + + // Add class declaration (common structure with appropriate indentation) + template.AppendLine($"{classIndent}[Binding]"); + template.AppendLine($"{classIndent}public class {className}"); + template.AppendLine($"{classIndent}{{"); + template.Append(combinedSnippet); + template.AppendLine($"{classIndent}}}"); + + // Close namespace if block-scoped + if (!csharpConfig.UseFileScopedNamespaces) + { + template.AppendLine("}"); } var targetFile = FileDetails .FromPath(targetFolder, className + ".cs") - .WithCSharpContent(template); + .WithCSharpContent(template.ToString()); if (IdeScope.FileSystem.File.Exists(targetFile.FullName)) if (IdeScope.Actions.ShowSyncQuestion("Overwrite file?", From b84db03e00231014835faf73e0c788889bd23f8c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:51:36 +0000 Subject: [PATCH 11/15] Fix StringBuilder to string conversion error in DefineStepsCommand Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index d4e086ac..e45dda76 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -186,7 +186,7 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin defaultButton: MessageBoxResult.No) != MessageBoxResult.Yes) return; - projectScope.AddFile(targetFile, template); + projectScope.AddFile(targetFile, template.ToString()); projectScope.IdeScope.Actions.NavigateTo(new SourceLocation(targetFile, 9, 1)); IDiscoveryService discoveryService = projectScope.GetDiscoveryService(); From 9d0ae718ed197c43c8a990d08081ea545594b91a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 21:18:39 +0000 Subject: [PATCH 12/15] Fix EditorConfig parsing bug and add debugging for namespace declaration support Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../Editor/Commands/DefineStepsCommand.cs | 4 ++ .../EditorConfig/EditorConfigOptions.cs | 19 +++++ .../EditorConfigOptionsExtensions.cs | 20 +++++- .../CSharpCodeGenerationConfigurationTests.cs | 72 +++++++++++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index e45dda76..76c670ed 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -142,6 +142,10 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin var editorConfigOptions = _editorConfigOptionsProvider.GetEditorConfigOptions(textView); editorConfigOptions.UpdateFromEditorConfig(csharpConfig); + // Debug: Log the configuration values + System.Diagnostics.Debug.WriteLine($"[DefineStepsCommand] NamespaceDeclarationStyle: '{csharpConfig.NamespaceDeclarationStyle}'"); + System.Diagnostics.Debug.WriteLine($"[DefineStepsCommand] UseFileScopedNamespaces: {csharpConfig.UseFileScopedNamespaces}"); + // Build template with common structure var template = new StringBuilder(); template.AppendLine("using System;"); diff --git a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptions.cs b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptions.cs index 8eb2cb91..c58e0288 100644 --- a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptions.cs +++ b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptions.cs @@ -19,13 +19,21 @@ public EditorConfigOptions(DocumentOptionSet options) public TResult GetOption(string editorConfigKey, TResult defaultValue) { + System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] GetOption: key='{editorConfigKey}', defaultValue='{defaultValue}', type={typeof(TResult).Name}"); + if (_directEditorConfigValues.TryGetValue(editorConfigKey, out var simplifiedValue)) { + System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] Found value: '{simplifiedValue}' for key '{editorConfigKey}'"); if (TryConvertFromString(simplifiedValue, defaultValue, out var convertedValue)) { + System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] Converted value: '{convertedValue}' for key '{editorConfigKey}'"); return convertedValue; } } + else + { + System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] No value found for key '{editorConfigKey}', returning default: '{defaultValue}'"); + } return defaultValue; } @@ -60,7 +68,18 @@ private Dictionary ExtractEditorConfigValues() var dacOptionsField = optionsAnalyzerConfigOptions?.GetType()?.GetField("Options", BindingFlags.NonPublic | BindingFlags.Instance); var optionsCollection = dacOptionsField?.GetValue(optionsAnalyzerConfigOptions) as ImmutableDictionary; if (optionsCollection != null) + { values = new Dictionary(optionsCollection); + System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] Extracted {values.Count} EditorConfig values"); + foreach (var kvp in values) + { + System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] Key: '{kvp.Key}' = '{kvp.Value}'"); + } + } + else + { + System.Diagnostics.Debug.WriteLine("[EditorConfigOptions] No options collection found"); + } } catch (Exception ex) { diff --git a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsExtensions.cs b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsExtensions.cs index bac62066..8b0b2285 100644 --- a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsExtensions.cs +++ b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsExtensions.cs @@ -21,23 +21,37 @@ public static void UpdateFromEditorConfig(this IEditorConfigOptions edi { if (config == null) throw new ArgumentNullException(nameof(config)); + System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] Updating configuration for type: {typeof(TConfig).Name}"); + var propertiesWithEditorConfig = typeof(TConfig) .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Select(p => new { PropertyInfo = p, - ((EditorConfigSettingAttribute) Attribute.GetCustomAttribute(p, typeof(EditorConfigSettingAttribute))) + EditorConfigKey = ((EditorConfigSettingAttribute) Attribute.GetCustomAttribute(p, typeof(EditorConfigSettingAttribute))) ?.EditorConfigSettingName }) - .Where(p => p.EditorConfigSettingName != null); + .Where(p => p.EditorConfigKey != null); foreach (var property in propertiesWithEditorConfig) { var currentValue = property.PropertyInfo.GetValue(config); + System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] Property: {property.PropertyInfo.Name}, EditorConfigKey: {property.EditorConfigKey}, CurrentValue: {currentValue}"); + var updatedValue = editorConfigOptions.GetOption(property.PropertyInfo.PropertyType, - property.EditorConfigSettingName, currentValue); + property.EditorConfigKey, currentValue); + + System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] UpdatedValue: {updatedValue}"); + if (!Equals(currentValue, updatedValue)) + { + System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] Setting property {property.PropertyInfo.Name} from '{currentValue}' to '{updatedValue}'"); property.PropertyInfo.SetValue(config, updatedValue); + } + else + { + System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] Property {property.PropertyInfo.Name} unchanged: '{currentValue}'"); + } } } } diff --git a/Tests/Reqnroll.VisualStudio.Tests/Configuration/CSharpCodeGenerationConfigurationTests.cs b/Tests/Reqnroll.VisualStudio.Tests/Configuration/CSharpCodeGenerationConfigurationTests.cs index b4994d98..b29aeb2d 100644 --- a/Tests/Reqnroll.VisualStudio.Tests/Configuration/CSharpCodeGenerationConfigurationTests.cs +++ b/Tests/Reqnroll.VisualStudio.Tests/Configuration/CSharpCodeGenerationConfigurationTests.cs @@ -1,4 +1,5 @@ using Reqnroll.VisualStudio.Configuration; +using Reqnroll.VisualStudio.Editor.Services.EditorConfig; using Xunit; namespace Reqnroll.VisualStudio.Tests.Configuration; @@ -79,4 +80,75 @@ public void UseFileScopedNamespaces_WhenUnknownValue_ReturnsFalse() // Act & Assert Assert.False(config.UseFileScopedNamespaces); } + + [Fact] + public void UpdateFromEditorConfig_WhenFileScopedValue_SetsCorrectValue() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration(); + var editorConfigOptions = new TestEditorConfigOptions("file_scoped:silent"); + + // Act + editorConfigOptions.UpdateFromEditorConfig(config); + + // Assert + Assert.Equal("file_scoped:silent", config.NamespaceDeclarationStyle); + Assert.True(config.UseFileScopedNamespaces); + } + + [Fact] + public void UpdateFromEditorConfig_WhenBlockScopedValue_SetsCorrectValue() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration(); + var editorConfigOptions = new TestEditorConfigOptions("block_scoped"); + + // Act + editorConfigOptions.UpdateFromEditorConfig(config); + + // Assert + Assert.Equal("block_scoped", config.NamespaceDeclarationStyle); + Assert.False(config.UseFileScopedNamespaces); + } + + [Fact] + public void UpdateFromEditorConfig_WhenNoValue_KeepsDefault() + { + // Arrange + var config = new CSharpCodeGenerationConfiguration(); + var editorConfigOptions = new TestEditorConfigOptions(null); + + // Act + editorConfigOptions.UpdateFromEditorConfig(config); + + // Assert + Assert.Equal("block_scoped", config.NamespaceDeclarationStyle); // Should keep default + Assert.False(config.UseFileScopedNamespaces); + } +} + +// Test EditorConfig options provider that simulates reading specific values +public class TestEditorConfigOptions : IEditorConfigOptions +{ + private readonly string _namespaceStyle; + + public TestEditorConfigOptions(string namespaceStyle) + { + _namespaceStyle = namespaceStyle; + } + + public TResult GetOption(string editorConfigKey, TResult defaultValue) + { + if (editorConfigKey == "csharp_style_namespace_declarations" && _namespaceStyle != null) + { + return (TResult)(object)_namespaceStyle; + } + + return defaultValue; + } + + public bool GetBoolOption(string editorConfigKey, bool defaultValue) + { + return defaultValue; + } } \ No newline at end of file From ce41e6656f0522a63286f97c39df26fcc3d02c8b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 21:20:05 +0000 Subject: [PATCH 13/15] Clean up debugging output after fixing EditorConfig parsing bug Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../Editor/Commands/DefineStepsCommand.cs | 4 ---- .../EditorConfig/EditorConfigOptions.cs | 19 ------------------- .../EditorConfigOptionsExtensions.cs | 14 -------------- 3 files changed, 37 deletions(-) diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index 76c670ed..e45dda76 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -142,10 +142,6 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin var editorConfigOptions = _editorConfigOptionsProvider.GetEditorConfigOptions(textView); editorConfigOptions.UpdateFromEditorConfig(csharpConfig); - // Debug: Log the configuration values - System.Diagnostics.Debug.WriteLine($"[DefineStepsCommand] NamespaceDeclarationStyle: '{csharpConfig.NamespaceDeclarationStyle}'"); - System.Diagnostics.Debug.WriteLine($"[DefineStepsCommand] UseFileScopedNamespaces: {csharpConfig.UseFileScopedNamespaces}"); - // Build template with common structure var template = new StringBuilder(); template.AppendLine("using System;"); diff --git a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptions.cs b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptions.cs index c58e0288..8eb2cb91 100644 --- a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptions.cs +++ b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptions.cs @@ -19,21 +19,13 @@ public EditorConfigOptions(DocumentOptionSet options) public TResult GetOption(string editorConfigKey, TResult defaultValue) { - System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] GetOption: key='{editorConfigKey}', defaultValue='{defaultValue}', type={typeof(TResult).Name}"); - if (_directEditorConfigValues.TryGetValue(editorConfigKey, out var simplifiedValue)) { - System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] Found value: '{simplifiedValue}' for key '{editorConfigKey}'"); if (TryConvertFromString(simplifiedValue, defaultValue, out var convertedValue)) { - System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] Converted value: '{convertedValue}' for key '{editorConfigKey}'"); return convertedValue; } } - else - { - System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] No value found for key '{editorConfigKey}', returning default: '{defaultValue}'"); - } return defaultValue; } @@ -68,18 +60,7 @@ private Dictionary ExtractEditorConfigValues() var dacOptionsField = optionsAnalyzerConfigOptions?.GetType()?.GetField("Options", BindingFlags.NonPublic | BindingFlags.Instance); var optionsCollection = dacOptionsField?.GetValue(optionsAnalyzerConfigOptions) as ImmutableDictionary; if (optionsCollection != null) - { values = new Dictionary(optionsCollection); - System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] Extracted {values.Count} EditorConfig values"); - foreach (var kvp in values) - { - System.Diagnostics.Debug.WriteLine($"[EditorConfigOptions] Key: '{kvp.Key}' = '{kvp.Value}'"); - } - } - else - { - System.Diagnostics.Debug.WriteLine("[EditorConfigOptions] No options collection found"); - } } catch (Exception ex) { diff --git a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsExtensions.cs b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsExtensions.cs index 8b0b2285..7d1fe9a2 100644 --- a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsExtensions.cs +++ b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsExtensions.cs @@ -21,8 +21,6 @@ public static void UpdateFromEditorConfig(this IEditorConfigOptions edi { if (config == null) throw new ArgumentNullException(nameof(config)); - System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] Updating configuration for type: {typeof(TConfig).Name}"); - var propertiesWithEditorConfig = typeof(TConfig) .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Select(p => new @@ -36,22 +34,10 @@ public static void UpdateFromEditorConfig(this IEditorConfigOptions edi foreach (var property in propertiesWithEditorConfig) { var currentValue = property.PropertyInfo.GetValue(config); - System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] Property: {property.PropertyInfo.Name}, EditorConfigKey: {property.EditorConfigKey}, CurrentValue: {currentValue}"); - var updatedValue = editorConfigOptions.GetOption(property.PropertyInfo.PropertyType, property.EditorConfigKey, currentValue); - - System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] UpdatedValue: {updatedValue}"); - if (!Equals(currentValue, updatedValue)) - { - System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] Setting property {property.PropertyInfo.Name} from '{currentValue}' to '{updatedValue}'"); property.PropertyInfo.SetValue(config, updatedValue); - } - else - { - System.Diagnostics.Debug.WriteLine($"[UpdateFromEditorConfig] Property {property.PropertyInfo.Name} unchanged: '{currentValue}'"); - } } } } From 36f5be859f3f8cdcf306c34b602abf54c840f8ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 22:13:23 +0000 Subject: [PATCH 14/15] Fix EditorConfig file scoping, StringBuilder capacity, and indentation issues Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .editorconfig | 1 - .../Editor/Commands/DefineStepsCommand.cs | 16 +++++++++---- .../EditorConfigOptionsProvider.cs | 24 ++++++++++++++++++- .../IEditorConfigOptionsProvider.cs | 1 + .../StubEditorConfigOptionsProvider.cs | 1 + 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/.editorconfig b/.editorconfig index 64d0c0c2..c785b717 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,7 +20,6 @@ tab_width=2 csharp_new_line_before_members_in_object_initializers=false csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion csharp_space_after_cast=false -csharp_style_namespace_declarations=file_scoped:warning csharp_style_var_elsewhere=true:hint csharp_style_var_for_built_in_types=true:hint csharp_style_var_when_type_is_apparent=true:hint diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index e45dda76..4d6049a8 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -137,13 +137,15 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin var isSpecFlow = projectTraits.HasFlag(ReqnrollProjectTraits.LegacySpecFlow) || projectTraits.HasFlag(ReqnrollProjectTraits.SpecFlowCompatibility); var libraryNameSpace = isSpecFlow ? "SpecFlow" : "Reqnroll"; - // Get C# code generation configuration from EditorConfig + // Get C# code generation configuration from EditorConfig using target .cs file path + var targetFilePath = Path.Combine(targetFolder, className + ".cs"); var csharpConfig = new CSharpCodeGenerationConfiguration(); - var editorConfigOptions = _editorConfigOptionsProvider.GetEditorConfigOptions(textView); + var editorConfigOptions = _editorConfigOptionsProvider.GetEditorConfigOptionsByPath(targetFilePath); editorConfigOptions.UpdateFromEditorConfig(csharpConfig); - // Build template with common structure - var template = new StringBuilder(); + // Estimate template size for StringBuilder capacity + var estimatedSize = 200 + fileNamespace.Length + className.Length + combinedSnippet.Length; + var template = new StringBuilder(estimatedSize); template.AppendLine("using System;"); template.AppendLine($"using {libraryNameSpace};"); template.AppendLine(); @@ -151,6 +153,10 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin // Determine indentation level based on namespace style var classIndent = csharpConfig.UseFileScopedNamespaces ? "" : indent; + // Adjust combinedSnippet indentation based on namespace style + var adjustedSnippet = csharpConfig.UseFileScopedNamespaces + ? combinedSnippet.Replace(indent + indent, indent) // Remove one level of indentation for file-scoped + : combinedSnippet; // Add namespace declaration if (csharpConfig.UseFileScopedNamespaces) { @@ -167,7 +173,7 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin template.AppendLine($"{classIndent}[Binding]"); template.AppendLine($"{classIndent}public class {className}"); template.AppendLine($"{classIndent}{{"); - template.Append(combinedSnippet); + template.Append(adjustedSnippet); template.AppendLine($"{classIndent}}}"); // Close namespace if block-scoped diff --git a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsProvider.cs b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsProvider.cs index 2045bdea..0f0f1e4b 100644 --- a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsProvider.cs +++ b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/EditorConfigOptionsProvider.cs @@ -26,6 +26,21 @@ public IEditorConfigOptions GetEditorConfigOptions(IWpfTextView textView) return new EditorConfigOptions(options); } + public IEditorConfigOptions GetEditorConfigOptionsByPath(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + return NullEditorConfigOptions.Instance; + + var document = CreateAdHocDocumentByPath(filePath); + if (document == null) + return NullEditorConfigOptions.Instance; + + var options = + ThreadHelper.JoinableTaskFactory.Run(() => document.GetOptionsAsync()); + + return new EditorConfigOptions(options); + } + private Document GetDocument(IWpfTextView textView) => textView.TextBuffer.GetRelatedDocuments().FirstOrDefault() ?? CreateAdHocDocument(textView); @@ -35,10 +50,17 @@ private Document CreateAdHocDocument(IWpfTextView textView) var editorFilePath = GetPath(textView); if (editorFilePath == null) return null; + return CreateAdHocDocumentByPath(editorFilePath); + } + + private Document CreateAdHocDocumentByPath(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + return null; var project = _visualStudioWorkspace.CurrentSolution.Projects.FirstOrDefault(); if (project == null) return null; - return project.AddDocument(editorFilePath, string.Empty, filePath: editorFilePath); + return project.AddDocument(filePath, string.Empty, filePath: filePath); } public static string GetPath(IWpfTextView textView) diff --git a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/IEditorConfigOptionsProvider.cs b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/IEditorConfigOptionsProvider.cs index 22ba4b0e..3d8e9964 100644 --- a/Reqnroll.VisualStudio/Editor/Services/EditorConfig/IEditorConfigOptionsProvider.cs +++ b/Reqnroll.VisualStudio/Editor/Services/EditorConfig/IEditorConfigOptionsProvider.cs @@ -3,4 +3,5 @@ namespace Reqnroll.VisualStudio.Editor.Services.EditorConfig; public interface IEditorConfigOptionsProvider { IEditorConfigOptions GetEditorConfigOptions(IWpfTextView textView); + IEditorConfigOptions GetEditorConfigOptionsByPath(string filePath); } diff --git a/Tests/Reqnroll.VisualStudio.VsxStubs/StubEditorConfigOptionsProvider.cs b/Tests/Reqnroll.VisualStudio.VsxStubs/StubEditorConfigOptionsProvider.cs index dd45a7b1..d24b4823 100644 --- a/Tests/Reqnroll.VisualStudio.VsxStubs/StubEditorConfigOptionsProvider.cs +++ b/Tests/Reqnroll.VisualStudio.VsxStubs/StubEditorConfigOptionsProvider.cs @@ -3,4 +3,5 @@ namespace Reqnroll.VisualStudio.VsxStubs; public class StubEditorConfigOptionsProvider : IEditorConfigOptionsProvider { public IEditorConfigOptions GetEditorConfigOptions(IWpfTextView textView) => new NullEditorConfigOptions(); + public IEditorConfigOptions GetEditorConfigOptionsByPath(string filePath) => new NullEditorConfigOptions(); } From 2c49097d9ac2edc9fb68154cd64407ed5d1e0a72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 22:22:24 +0000 Subject: [PATCH 15/15] Replace tricky string replacement with robust line-by-line indentation adjustment Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> --- .../Editor/Commands/DefineStepsCommand.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs index 4d6049a8..4906475c 100644 --- a/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs +++ b/Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs @@ -155,7 +155,7 @@ private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combin // Adjust combinedSnippet indentation based on namespace style var adjustedSnippet = csharpConfig.UseFileScopedNamespaces - ? combinedSnippet.Replace(indent + indent, indent) // Remove one level of indentation for file-scoped + ? AdjustIndentationForFileScopedNamespace(combinedSnippet, indent, newLine) : combinedSnippet; // Add namespace declaration if (csharpConfig.UseFileScopedNamespaces) @@ -208,4 +208,31 @@ await discoveryService.BindingRegistryCache Finished.Set(); } + + private static string AdjustIndentationForFileScopedNamespace(string snippet, string indent, string newLine) + { + if (string.IsNullOrEmpty(snippet)) + return snippet; + + // Split into lines and process each line + var lines = snippet.Split(new[] { newLine }, StringSplitOptions.None); + var adjustedLines = new string[lines.Length]; + + for (int i = 0; i < lines.Length; i++) + { + var line = lines[i]; + + // If line starts with double indentation, reduce it to single indentation + if (line.StartsWith(indent + indent)) + { + adjustedLines[i] = indent + line.Substring((indent + indent).Length); + } + else + { + adjustedLines[i] = line; + } + } + + return string.Join(newLine, adjustedLines); + } }