From 60619520630ad4de020b135444160c99b971b789 Mon Sep 17 00:00:00 2001 From: filipw Date: Sat, 28 Dec 2019 09:45:44 +0100 Subject: [PATCH 1/5] added support for ImplementTypeOptions --- .../ImplementTypeWorkspaceOptionsProvider.cs | 50 +++++++++++++++++++ .../Options/ImplementTypeOptions.cs | 20 ++++++++ .../Options/OmniSharpOptions.cs | 2 + 3 files changed, 72 insertions(+) create mode 100644 src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs create mode 100644 src/OmniSharp.Shared/Options/ImplementTypeOptions.cs diff --git a/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs b/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs new file mode 100644 index 0000000000..cdc593eeb2 --- /dev/null +++ b/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Composition; +using System.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Options; +using OmniSharp.Options; +using OmniSharp.Roslyn.Options; +using OmniSharp.Services; +using OmniSharp.Utilities; + +namespace OmniSharp.Roslyn.CSharp.Services +{ + [Export(typeof(IWorkspaceOptionsProvider)), Shared] + public class ImplementTypeWorkspaceOptionsProvider : IWorkspaceOptionsProvider + { + private readonly IAssemblyLoader _assemblyLoader; + private readonly Lazy _csharpFeatureAssembly; + private readonly Lazy _implementTypeOptions; + + [ImportingConstructor] + public ImplementTypeWorkspaceOptionsProvider(IAssemblyLoader assemblyLoader) + { + _assemblyLoader = assemblyLoader; + _csharpFeatureAssembly = _assemblyLoader.LazyLoad(Configuration.RoslynFeatures); + _implementTypeOptions = _csharpFeatureAssembly.LazyGetType("Microsoft.CodeAnalysis.ImplementType.ImplementTypeOptions"); + } + + public int Order => 110; + + public OptionSet Process(OptionSet currentOptionSet, OmniSharpOptions omniSharpOptions, IOmniSharpEnvironment omnisharpEnvironment) + { + if (omniSharpOptions.ImplementTypeOptions.InsertionBehavior != null) + { + var insertionBehaviorOptionValue = _implementTypeOptions.Value.GetField("InsertionBehavior", BindingFlags.Public | BindingFlags.Static)?.GetValue(null) as IOption; + if (insertionBehaviorOptionValue != null) + { + currentOptionSet = currentOptionSet.WithChangedOption(new OptionKey(insertionBehaviorOptionValue, LanguageNames.CSharp), (int)omniSharpOptions.ImplementTypeOptions.InsertionBehavior); + } + } + + var propertyGenerationBehaviorOptionValue = _implementTypeOptions.Value.GetField("PropertyGenerationBehavior", BindingFlags.Public | BindingFlags.Static)?.GetValue(null) as IOption; + if (propertyGenerationBehaviorOptionValue != null) + { + currentOptionSet = currentOptionSet.WithChangedOption(new OptionKey(propertyGenerationBehaviorOptionValue, LanguageNames.CSharp), (int)omniSharpOptions.ImplementTypeOptions.PropertyGenerationBehavior); + } + + return currentOptionSet; + } + } +} diff --git a/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs b/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs new file mode 100644 index 0000000000..19511956c5 --- /dev/null +++ b/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs @@ -0,0 +1,20 @@ +namespace OmniSharp.Options +{ + public class ImplementTypeOptions + { + public ImplementTypeInsertionBehavior? InsertionBehavior { get; set; } + public ImplementTypePropertyGenerationBehavior PropertyGenerationBehavior { get; set; } = ImplementTypePropertyGenerationBehavior.PreferAutoProperties; + } + + public enum ImplementTypeInsertionBehavior + { + WithOtherMembersOfTheSameKind = 0, + AtTheEnd = 1, + } + + public enum ImplementTypePropertyGenerationBehavior + { + PreferThrowingProperties = 0, + PreferAutoProperties = 1, + } +} diff --git a/src/OmniSharp.Shared/Options/OmniSharpOptions.cs b/src/OmniSharp.Shared/Options/OmniSharpOptions.cs index ce27f4f8fb..f6a8579963 100644 --- a/src/OmniSharp.Shared/Options/OmniSharpOptions.cs +++ b/src/OmniSharp.Shared/Options/OmniSharpOptions.cs @@ -12,6 +12,8 @@ public class OmniSharpOptions public RenameOptions RenameOptions { get; set; } = new RenameOptions(); + public ImplementTypeOptions ImplementTypeOptions { get; set; } = new ImplementTypeOptions(); + public OmniSharpExtensionsOptions Plugins { get; set; } = new OmniSharpExtensionsOptions(); } } From 4eb2c05c4fe931f5bbc18a66f22ca46139df1b7f Mon Sep 17 00:00:00 2001 From: filipw Date: Sat, 28 Dec 2019 16:59:03 +0100 Subject: [PATCH 2/5] use pattern matching --- .../Services/ImplementTypeWorkspaceOptionsProvider.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs b/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs index cdc593eeb2..d51e13f6ae 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs @@ -31,17 +31,18 @@ public OptionSet Process(OptionSet currentOptionSet, OmniSharpOptions omniSharpO { if (omniSharpOptions.ImplementTypeOptions.InsertionBehavior != null) { - var insertionBehaviorOptionValue = _implementTypeOptions.Value.GetField("InsertionBehavior", BindingFlags.Public | BindingFlags.Static)?.GetValue(null) as IOption; - if (insertionBehaviorOptionValue != null) + if (_implementTypeOptions.Value.GetField("InsertionBehavior", BindingFlags.Public | BindingFlags.Static)?.GetValue(null) is IOption insertionBehaviorOptionValue) { currentOptionSet = currentOptionSet.WithChangedOption(new OptionKey(insertionBehaviorOptionValue, LanguageNames.CSharp), (int)omniSharpOptions.ImplementTypeOptions.InsertionBehavior); } } - var propertyGenerationBehaviorOptionValue = _implementTypeOptions.Value.GetField("PropertyGenerationBehavior", BindingFlags.Public | BindingFlags.Static)?.GetValue(null) as IOption; - if (propertyGenerationBehaviorOptionValue != null) + if (omniSharpOptions.ImplementTypeOptions.PropertyGenerationBehavior != null) { - currentOptionSet = currentOptionSet.WithChangedOption(new OptionKey(propertyGenerationBehaviorOptionValue, LanguageNames.CSharp), (int)omniSharpOptions.ImplementTypeOptions.PropertyGenerationBehavior); + if (_implementTypeOptions.Value.GetField("PropertyGenerationBehavior", BindingFlags.Public | BindingFlags.Static)?.GetValue(null) is IOption propertyGenerationBehaviorOptionValue) + { + currentOptionSet = currentOptionSet.WithChangedOption(new OptionKey(propertyGenerationBehaviorOptionValue, LanguageNames.CSharp), (int)omniSharpOptions.ImplementTypeOptions.PropertyGenerationBehavior); + } } return currentOptionSet; From dd04de646696e5e6d4a45e3e2154ea6a3473c258 Mon Sep 17 00:00:00 2001 From: filipw Date: Sat, 28 Dec 2019 16:59:18 +0100 Subject: [PATCH 3/5] default tp Roslyn defaults --- src/OmniSharp.Shared/Options/ImplementTypeOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs b/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs index 19511956c5..7a609ebc1e 100644 --- a/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs +++ b/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs @@ -3,7 +3,7 @@ public class ImplementTypeOptions { public ImplementTypeInsertionBehavior? InsertionBehavior { get; set; } - public ImplementTypePropertyGenerationBehavior PropertyGenerationBehavior { get; set; } = ImplementTypePropertyGenerationBehavior.PreferAutoProperties; + public ImplementTypePropertyGenerationBehavior? PropertyGenerationBehavior { get; set; } } public enum ImplementTypeInsertionBehavior From d2bf5ea203926c242a5cbc6f98a16a6a99ff7497 Mon Sep 17 00:00:00 2001 From: filipw Date: Sun, 29 Dec 2019 15:30:27 +0100 Subject: [PATCH 4/5] added ImplementTypeFacts --- .../ImplementTypeFacts.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/OmniSharp.Roslyn.CSharp.Tests/ImplementTypeFacts.cs diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/ImplementTypeFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/ImplementTypeFacts.cs new file mode 100644 index 0000000000..1e012b6438 --- /dev/null +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/ImplementTypeFacts.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using OmniSharp.Models; +using OmniSharp.Models.V2.CodeActions; +using OmniSharp.Roslyn.CSharp.Services.Refactoring.V2; +using TestUtility; +using Xunit; +using Xunit.Abstractions; + +namespace OmniSharp.Roslyn.CSharp.Tests +{ + public class ImplementTypeFacts : AbstractCodeActionsTestFixture + { + public ImplementTypeFacts(ITestOutputHelper output) + : base(output) + { + } + + [Theory] + [InlineData("PreferAutoProperties", "public string Name { get; set; }")] + [InlineData("PreferThrowingProperties", "public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }")] + [InlineData(null, "public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }")] + public async Task ImplementInterface(string implementTypePropertyGenerationBehavior, string expectedChange) + { + const string code = @" +interface IFoo +{ + string Name { get; set; } +} + +class Foo : I$$Foo +{ +}"; + + var testFile = new TestFile("test.cs", code); + var hostProperties = implementTypePropertyGenerationBehavior != null ? new Dictionary + { + ["ImplementTypeOptions:PropertyGenerationBehavior"] = implementTypePropertyGenerationBehavior + } : null; + using (var host = CreateOmniSharpHost(new[] { testFile }, hostProperties)) + { + var requestHandler = host.GetRequestHandler(OmniSharpEndpoints.V2.RunCodeAction); + var point = testFile.Content.GetPointFromPosition(); + + var request = new RunCodeActionRequest + { + Line = point.Line, + Column = point.Offset, + FileName = testFile.FileName, + Buffer = testFile.Content.Code, + Identifier = "False;False;AssemblyName;global::IFoo;Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceCodeAction", + WantsTextChanges = true, + WantsAllCodeActionOperations = true + }; + + var response = await requestHandler.Handle(request); + var changes = response.Changes.ToArray(); + + Assert.Single(changes); + Assert.NotNull(changes[0].FileName); + AssertIgnoringIndent(expectedChange, ((ModifiedFileResponse)changes[0]).Changes.First().NewText); + } + } + + } +} From 43ebda40d303b735ea90db8995474b5a5c5a403a Mon Sep 17 00:00:00 2001 From: filipw Date: Sun, 29 Dec 2019 16:13:51 +0100 Subject: [PATCH 5/5] more tests --- .../ImplementTypeFacts.cs | 76 ++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/ImplementTypeFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/ImplementTypeFacts.cs index 1e012b6438..4c7e0a384e 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/ImplementTypeFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/ImplementTypeFacts.cs @@ -21,7 +21,7 @@ public ImplementTypeFacts(ITestOutputHelper output) [InlineData("PreferAutoProperties", "public string Name { get; set; }")] [InlineData("PreferThrowingProperties", "public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }")] [InlineData(null, "public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }")] - public async Task ImplementInterface(string implementTypePropertyGenerationBehavior, string expectedChange) + public async Task ImplementInterface_PropertyGeneration(string implementTypePropertyGenerationBehavior, string expectedChange) { const string code = @" interface IFoo @@ -33,11 +33,82 @@ class Foo : I$$Foo { }"; - var testFile = new TestFile("test.cs", code); var hostProperties = implementTypePropertyGenerationBehavior != null ? new Dictionary { ["ImplementTypeOptions:PropertyGenerationBehavior"] = implementTypePropertyGenerationBehavior } : null; + await VerifyImplementType(code, expectedChange, hostProperties); + } + + [Fact] + public async Task ImplementInterface_Insertion_AtTheEnd() + { + const string code = @" +public interface IFoo +{ + string Name { get; set; } + void Do(); + string Name2 { get; set; } +} + +public class Foo : I$$Foo +{ +}"; + + const string expectedChange = @" + public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + + public void Do() + { + throw new System.NotImplementedException(); + } + + public string Name2 { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } +"; + + await VerifyImplementType(code, expectedChange, new Dictionary + { + ["ImplementTypeOptions:InsertionBehavior"] = "AtTheEnd" + }); + } + + [Theory] + [InlineData("WithOtherMembersOfTheSameKind")] + [InlineData(null)] + public async Task ImplementInterface_Insertion_WithOtherMembersOfTheSameKind(string implementTypeInsertionBehavior) + { + const string code = @" +public interface IFoo +{ + string Name { get; set; } + void Do(); + string Name2 { get; set; } +} + +public class Foo : I$$Foo +{ +}"; + + const string expectedChange = @" + public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + public string Name2 { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + + public void Do() + { + throw new System.NotImplementedException(); + } +"; + + var hostProperties = implementTypeInsertionBehavior != null ? new Dictionary + { + ["ImplementTypeOptions:InsertionBehavior"] = implementTypeInsertionBehavior + } : null; + await VerifyImplementType(code, expectedChange, hostProperties); + } + + private async Task VerifyImplementType(string code, string expectedChange, Dictionary hostProperties) + { + var testFile = new TestFile("test.cs", code); using (var host = CreateOmniSharpHost(new[] { testFile }, hostProperties)) { var requestHandler = host.GetRequestHandler(OmniSharpEndpoints.V2.RunCodeAction); @@ -62,6 +133,5 @@ class Foo : I$$Foo AssertIgnoringIndent(expectedChange, ((ModifiedFileResponse)changes[0]).Changes.First().NewText); } } - } }