diff --git a/.gitignore b/.gitignore index 7ccc837..b7d1e2e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build output .gradle .tmp +packages # User-specific files *.suo diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/Navigation/AutoMapperNavigationActionAvailabilityTest.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/Navigation/AutoMapperNavigationActionAvailabilityTest.cs new file mode 100644 index 0000000..1c6ffe8 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/Navigation/AutoMapperNavigationActionAvailabilityTest.cs @@ -0,0 +1,33 @@ +using JetBrains.ReSharper.FeaturesTestFramework.Intentions; +using JetBrains.ReSharper.TestFramework; +using NUnit.Framework; +using ReSharperPlugin.AutoMapper.FindUsage.Navigation; + +namespace ReSharperPlugin.AutoMapper.FindUsage.Tests.Navigation; + +[TestFixture] +[TestPackages("AutoMapper")] +public class AutoMapperNavigationActionAvailabilityTest + : CSharpContextActionAvailabilityTestBase +{ + protected override string RelativeTestDataPath => "Navigation"; + protected override string ExtraPath => ""; + + [Test] public void TestAvailableOnSetter() => DoNamedTest(); + + [Test] public void TestAvailableOnInit() => DoNamedTest(); + + [Test] public void TestReverseMap() => DoNamedTest(); + + [Test] public void TestReverseMapChain() => DoNamedTest(); + + [Test] public void TestMultipleMappings() => DoNamedTest(); + + [Test] public void TestConfigurationExpression() => DoNamedTest(); + + [Test] public void TestNotAvailableOnGetter() => DoNamedTest(); + + [Test] public void TestNotAvailableWithoutMapping() => DoNamedTest(); + + [Test] public void TestNotAvailableOnUnmappedType() => DoNamedTest(); +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/Navigation/AutoMapperNavigationActionWithoutAutoMapperAvailabilityTest.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/Navigation/AutoMapperNavigationActionWithoutAutoMapperAvailabilityTest.cs new file mode 100644 index 0000000..cc73689 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/Navigation/AutoMapperNavigationActionWithoutAutoMapperAvailabilityTest.cs @@ -0,0 +1,16 @@ +using JetBrains.ReSharper.FeaturesTestFramework.Intentions; +using JetBrains.ReSharper.TestFramework; +using NUnit.Framework; +using ReSharperPlugin.AutoMapper.FindUsage.Navigation; + +namespace ReSharperPlugin.AutoMapper.FindUsage.Tests.Navigation; + +[TestFixture] +public class AutoMapperNavigationActionWithoutAutoMapperAvailabilityTest + : CSharpContextActionAvailabilityTestBase +{ + protected override string RelativeTestDataPath => "Navigation"; + protected override string ExtraPath => ""; + + [Test] public void TestNotAvailableWithoutAutoMapper() => DoNamedTest(); +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/ReSharperPlugin.AutoMapper.FindUsage.Tests.csproj b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/ReSharperPlugin.AutoMapper.FindUsage.Tests.csproj index b965ea5..1c02610 100644 --- a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/ReSharperPlugin.AutoMapper.FindUsage.Tests.csproj +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/ReSharperPlugin.AutoMapper.FindUsage.Tests.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestAvailableOnInit.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestAvailableOnInit.cs new file mode 100644 index 0000000..1ece040 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestAvailableOnInit.cs @@ -0,0 +1,21 @@ +using AutoMapper; + +namespace TestProject; + +public class SourceDto +{ + public string Name { get; set; } +} + +public class DestinationDto +{ + public string Name { get; init{on}; } +} + +public class TestProfile : Profile +{ + public TestProfile() + { + CreateMap(); + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestAvailableOnSetter.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestAvailableOnSetter.cs new file mode 100644 index 0000000..348c0ea --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestAvailableOnSetter.cs @@ -0,0 +1,21 @@ +using AutoMapper; + +namespace TestProject; + +public class SourceDto +{ + public string Name { get; set; } +} + +public class DestinationDto +{ + public string Name { get; set{on}; } +} + +public class TestProfile : Profile +{ + public TestProfile() + { + CreateMap(); + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestConfigurationExpression.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestConfigurationExpression.cs new file mode 100644 index 0000000..22d312e --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestConfigurationExpression.cs @@ -0,0 +1,17 @@ +using AutoMapper; + +namespace TestProject; + +public class SourceDto { public string Name { get; set; } } +public class DestinationDto { public string Name { get; set{on}; } } + +public class Startup +{ + public void Configure() + { + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + }); + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestMultipleMappings.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestMultipleMappings.cs new file mode 100644 index 0000000..61b7636 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestMultipleMappings.cs @@ -0,0 +1,20 @@ +using AutoMapper; + +namespace TestProject; + +public class Source1Dto { public string Name { get; set; } } +public class Source2Dto { public string Name { get; set; } } + +public class DestinationDto +{ + public string Name { get; set{on}; } +} + +public class TestProfile : Profile +{ + public TestProfile() + { + CreateMap(); + CreateMap(); + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableOnGetter.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableOnGetter.cs new file mode 100644 index 0000000..a6444f9 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableOnGetter.cs @@ -0,0 +1,21 @@ +using AutoMapper; + +namespace TestProject; + +public class SourceDto +{ + public string Name { get; set; } +} + +public class DestinationDto +{ + public string Name { get{off}; set; } +} + +public class TestProfile : Profile +{ + public TestProfile() + { + CreateMap(); + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableOnUnmappedType.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableOnUnmappedType.cs new file mode 100644 index 0000000..af72e79 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableOnUnmappedType.cs @@ -0,0 +1,19 @@ +using AutoMapper; + +namespace TestProject; + +public class SourceDto { public string Name { get; set; } } +public class DestinationDto { public string Name { get; set; } } + +public class UnmappedDto +{ + public string Name { get; set{off}; } +} + +public class TestProfile : Profile +{ + public TestProfile() + { + CreateMap(); + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableWithoutAutoMapper.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableWithoutAutoMapper.cs new file mode 100644 index 0000000..a6d403c --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableWithoutAutoMapper.cs @@ -0,0 +1,13 @@ +using System; + +namespace TestProject; + +public class SourceDto +{ + public string Name { get; set; } +} + +public class DestinationDto +{ + public string Name { get; set{off}; } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableWithoutMapping.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableWithoutMapping.cs new file mode 100644 index 0000000..7130250 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestNotAvailableWithoutMapping.cs @@ -0,0 +1,21 @@ +using AutoMapper; + +namespace TestProject; + +public class SourceDto +{ + public string Name { get; set; } +} + +public class DestinationDto +{ + public string Name { get; set{off}; } +} + +public class TestProfile : Profile +{ + public TestProfile() + { + // No CreateMap here + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestReverseMap.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestReverseMap.cs new file mode 100644 index 0000000..cf802ad --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestReverseMap.cs @@ -0,0 +1,21 @@ +using AutoMapper; + +namespace TestProject; + +public class SourceDto +{ + public string Name { get; set{on}; } +} + +public class DestinationDto +{ + public string Name { get; set; } +} + +public class TestProfile : Profile +{ + public TestProfile() + { + CreateMap().ReverseMap(); + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestReverseMapChain.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestReverseMapChain.cs new file mode 100644 index 0000000..b3691e1 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/Navigation/TestReverseMapChain.cs @@ -0,0 +1,14 @@ +using AutoMapper; + +namespace TestProject; + +public class SourceDto { public string Name { get; set{on}; } } +public class DestinationDto { public string Name { get; set; } } + +public class TestProfile : Profile +{ + public TestProfile() + { + CreateMap().MaxDepth(5).ReverseMap(); + } +} diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/A714BD8C6AAB995E590847AA474991BBC5450C60428A22B91BDAB0D3DA3469C7.lock b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/A714BD8C6AAB995E590847AA474991BBC5450C60428A22B91BDAB0D3DA3469C7.lock new file mode 100644 index 0000000..beaaa69 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/A714BD8C6AAB995E590847AA474991BBC5450C60428A22B91BDAB0D3DA3469C7.lock @@ -0,0 +1,6 @@ +# Please commit this file, it's crucial for tests stability. Even if you believe it's not yours, it still needs to be committed. +# Input (NuGetFramework=net9.0): +# JetBrains.Annotations [2025.2.0] +# Microsoft.NETCore.App.Ref [9.0.0] +JetBrains.Annotations 2025.2.0 +Microsoft.NETCore.App.Ref 9.0.0 \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/C57A061EA9C7F3875D28DF0F1248AC3453B0C7741443AEB2C239278A72BF8406.lock b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/C57A061EA9C7F3875D28DF0F1248AC3453B0C7741443AEB2C239278A72BF8406.lock new file mode 100644 index 0000000..79fb6c5 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/C57A061EA9C7F3875D28DF0F1248AC3453B0C7741443AEB2C239278A72BF8406.lock @@ -0,0 +1,15 @@ +# Please commit this file, it's crucial for tests stability. Even if you believe it's not yours, it still needs to be committed. +# Input (NuGetFramework=net9.0): +# AutoMapper (, ) +# Microsoft.NETCore.App.Ref [9.0.0] +AutoMapper 16.1.1 +Microsoft.Extensions.DependencyInjection.Abstractions 10.0.0 +Microsoft.Extensions.Logging.Abstractions 10.0.0 +Microsoft.Extensions.Options 10.0.0 +Microsoft.Extensions.Primitives 10.0.0 +Microsoft.IdentityModel.Abstractions 8.14.0 +Microsoft.IdentityModel.JsonWebTokens 8.14.0 +Microsoft.IdentityModel.Logging 8.14.0 +Microsoft.IdentityModel.Tokens 8.14.0 +Microsoft.NETCore.App.Ref 9.0.0 +System.Diagnostics.DiagnosticSource 10.0.0 \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/F1E85CB459CEDD759BDA4F44439771A90968306FF233FECEEC12839A5C161619.lock b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/F1E85CB459CEDD759BDA4F44439771A90968306FF233FECEEC12839A5C161619.lock new file mode 100644 index 0000000..f4161e6 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage.Tests/test/data/NuGetLocks/F1E85CB459CEDD759BDA4F44439771A90968306FF233FECEEC12839A5C161619.lock @@ -0,0 +1,4 @@ +# Please commit this file, it's crucial for tests stability. Even if you believe it's not yours, it still needs to be committed. +# Input (NuGetFramework=net9.0): +# Microsoft.NETCore.App.Ref [9.0.0] +Microsoft.NETCore.App.Ref 9.0.0 \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Navigation/AutoMapperNavigationAction.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Navigation/AutoMapperNavigationAction.cs index fa7bf6a..2e6c216 100644 --- a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Navigation/AutoMapperNavigationAction.cs +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Navigation/AutoMapperNavigationAction.cs @@ -10,6 +10,7 @@ using JetBrains.ReSharper.Feature.Services.Navigation; using JetBrains.ReSharper.Psi; using JetBrains.ReSharper.Psi.CSharp.Tree; +using JetBrains.ReSharper.Psi.Modules; using JetBrains.ReSharper.Psi.Tree; using JetBrains.TextControl; using JetBrains.ReSharper.Psi.Resources; @@ -83,18 +84,26 @@ private IEnumerable CreateBulbItemsInternal() public bool IsAvailable(IUserDataHolder cache) { + if (!IsAutoMapperAvailable(_dataProvider.PsiModule)) + return false; + var property = GetSelectedProperty(); - if (property == null) return false; + if (property == null) + return false; - // To avoid heavy computation in IsAvailable, we just return true if it's a property. - // The actual search will happen in CreateBulbItems. return true; } + private static bool IsAutoMapperAvailable(IPsiModule module) + { + return TypeFactory.CreateTypeByCLRName("AutoMapper.Profile", module).GetTypeElement() != null; + } + private IProperty GetSelectedProperty() { var node = _dataProvider.GetSelectedTreeNode(); - if (node == null) return null; + if (node == null) + return null; var accessorDeclaration = node.GetContainingNode(true); if (accessorDeclaration is { Kind: AccessorKind.SETTER }) @@ -109,9 +118,8 @@ private IProperty GetSelectedProperty() private static IProperty FindCorrespondingProperty(IType type, string propertyName) { var typeElement = (type as IDeclaredType)?.GetTypeElement(); - if (typeElement == null) return null; - return typeElement.GetMembers().OfType().FirstOrDefault(p => p.ShortName == propertyName); + return typeElement?.Properties.FirstOrDefault(p => p.ShortName == propertyName); } private static string GetFullName(IType type, PsiLanguageType language) diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Registrations/AutoMapperMapping.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Registrations/AutoMapperMapping.cs new file mode 100644 index 0000000..eac77fe --- /dev/null +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Registrations/AutoMapperMapping.cs @@ -0,0 +1,18 @@ +using JetBrains.ReSharper.Psi; +using JetBrains.ReSharper.Psi.Tree; + +namespace ReSharperPlugin.AutoMapper.FindUsage.Registrations; + +public sealed class AutoMapperMapping +{ + public IType Source { get; } + public IType Destination { get; } + public ITreeNode Registration { get; } + + public AutoMapperMapping(IType source, IType destination, ITreeNode registration) + { + Source = source; + Destination = destination; + Registration = registration; + } +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Registrations/AutoMapperRegistrationFinder.cs b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Registrations/AutoMapperRegistrationFinder.cs index a01d494..40f61ef 100644 --- a/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Registrations/AutoMapperRegistrationFinder.cs +++ b/src/dotnet/ReSharperPlugin.AutoMapper.FindUsage/Registrations/AutoMapperRegistrationFinder.cs @@ -7,7 +7,6 @@ using JetBrains.ReSharper.Psi.CSharp.Tree; using JetBrains.ReSharper.Psi.Resolve; using JetBrains.ReSharper.Psi.Search; -using JetBrains.ReSharper.Psi.Tree; using JetBrains.Util; namespace ReSharperPlugin.AutoMapper.FindUsage.Registrations; @@ -160,7 +159,7 @@ private static bool HasReverseMap(IInvocationExpression invocation) return true; } - if (current is IInvocationExpression inv && inv.InvokedExpression is IReferenceExpression re && + if (current is IInvocationExpression { InvokedExpression: IReferenceExpression re } && re.Reference.GetName() == "ReverseMap") { return true; @@ -172,18 +171,4 @@ private static bool HasReverseMap(IInvocationExpression invocation) return false; } -} - -public class AutoMapperMapping -{ - public IType Source { get; } - public IType Destination { get; } - public ITreeNode Registration { get; } - - public AutoMapperMapping(IType source, IType destination, ITreeNode registration) - { - Source = source; - Destination = destination; - Registration = registration; - } } \ No newline at end of file